On Wed, Jul 08, 2009 at 03:58:19PM +0300, Gleb Natapov wrote:
> Excellent patch series.
>
> On Sun, Jul 05, 2009 at 10:55:15PM -0300, Marcelo Tosatti wrote:
> > int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
> > {
> > - int ret;
> > + ktime_t now, expires;
> >
> > - ret = pit_has_pending_timer(vcpu);
> > - ret |= apic_has_pending_timer(vcpu);
> > + expires = kvm_vcpu_next_timer_event(vcpu);
> > + now = ktime_get();
> > + if (expires.tv64 <= now.tv64) {
> > + if (kvm_arch_interrupt_allowed(vcpu))
> > + set_bit(KVM_REQ_UNHALT, &vcpu->requests);
> You shouldn't unhalt vcpu here. Not every timer event will generate
> interrupt (vector can be masked in pic/ioapic)
Yeah. Note however that kvm_vcpu_next_timer_event only returns the
expiration time for events that have been acked (for timers that have
had events injected, but not acked it returns KTIME_MAX).
So, the code above will set one spurious unhalt if:
- inject timer irq
- guest acks irq
- guest mask irq
- unhalt (once)
I had a "kvm_timer_mask" callback before (along with the attached
patch), but decided to keep it simpler using the ack trick above.
I suppose one spurious unhalt is harmless, or is it a correctness issue?
> or timer event can generate NMI instead of interrupt.
In the NMI case it should not unhalt the processor?
(but yes, bypassing the irq injection system its not a very beatiful
shortcut, but its done in other places too eg i8254.c NMI injection via
all cpus LINT0).
> Leaving this code out probably means
> that you can't remove kvm_inject_pending_timer_irqs() call from
> __vcpu_run().
>
> > + return 1;
> > + }
> >
> > - return ret;
> > + return 0;
> > }
> > EXPORT_SYMBOL(kvm_cpu_has_pending_timer);
> >
>
> --
> Gleb.
Index: kvm/arch/x86/kvm/i8259.c
===================================================================
--- kvm.orig/arch/x86/kvm/i8259.c
+++ kvm/arch/x86/kvm/i8259.c
@@ -122,6 +122,18 @@ static inline int get_priority(struct kv
return priority;
}
+int pic_int_is_masked(struct kvm *kvm, int irq)
+{
+ u8 imr;
+ struct kvm_pic *s = pic_irqchip(kvm);
+
+ pic_lock(s);
+ imr = s->pics[SELECT_PIC(irq)].imr;
+ pic_unlock(s);
+
+ return (imr & (1 << irq));
+}
+
/*
* return the pic wanted interrupt. return -1 if none
*/
Index: kvm/arch/x86/kvm/irq.c
===================================================================
--- kvm.orig/arch/x86/kvm/irq.c
+++ kvm/arch/x86/kvm/irq.c
@@ -42,6 +42,29 @@ int kvm_cpu_has_pending_timer(struct kvm
EXPORT_SYMBOL(kvm_cpu_has_pending_timer);
/*
+ * The PIT interrupt can be masked on different levels: PIC/IOAPIC/LAPIC.
+ * Hide the maze from the PIT mask notifier, all it cares about is whether
+ * the interrupt can reach the processor.
+ */
+void kvm_pit_mask_notifier(struct kvm_irq_mask_notifier *kimn, bool mask)
+{
+ bool masked = 0;
+ struct kvm *kvm;
+ struct kvm_pit *pit = container_of(kimn, struct kvm_pit, mask_notifier);
+
+ kvm = pit->kvm;
+
+ /* LAPIC hardware disabled or INTIN0 programmed as ExtINT */
+ if (kvm_apic_accept_pic_intr(kvm->vcpus[0])) {
+ masked = pic_int_is_masked(kvm, 0);
+ /* IOAPIC -> LAPIC */
+ } else
+ masked = ioapic_int_is_masked(kvm, 0);
+
+ kvm_pit_internal_mask_notifier(kvm, mask);
+}
+
+/*
* check if there is pending interrupt without
* intack.
*/
Index: kvm/virt/kvm/ioapic.c
===================================================================
--- kvm.orig/virt/kvm/ioapic.c
+++ kvm/virt/kvm/ioapic.c
@@ -101,6 +101,16 @@ static int ioapic_service(struct kvm_ioa
return injected;
}
+int ioapic_int_is_masked(struct kvm *kvm, int pin)
+{
+ struct kvm_ioapic *ioapic = kvm->arch.vioapic;
+
+ if (!ioapic)
+ return 1;
+
+ return ioapic->redirtbl[pin].fields.mask;
+}
+
static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
{
unsigned index;
Index: kvm/arch/x86/kvm/i8254.c
===================================================================
--- kvm.orig/arch/x86/kvm/i8254.c
+++ kvm/arch/x86/kvm/i8254.c
@@ -558,9 +558,9 @@ void kvm_pit_reset(struct kvm_pit *pit)
pit->pit_state.irq_ack = 1;
}
-static void pit_mask_notifer(struct kvm_irq_mask_notifier *kimn, bool mask)
+void kvm_pit_internal_mask_notifier(struct kvm *kvm, bool mask)
{
- struct kvm_pit *pit = container_of(kimn, struct kvm_pit, mask_notifier);
+ struct kvm_pit *pit = kvm->arch.vpit;
if (!mask) {
atomic_set(&pit->pit_state.pit_timer.pending, 0);
@@ -614,8 +614,8 @@ struct kvm_pit *kvm_create_pit(struct kv
kvm_pit_reset(pit);
- pit->mask_notifier.func = pit_mask_notifer;
- kvm_register_irq_mask_notifier(kvm, 0, &pit->mask_notifier);
+ pit->mask_notifier.func = kvm_pit_mask_notifier;
+ kvm_register_irq_mask_notifier(kvm, 0, &kvm->arch.vpit->mask_notifier);
kvm_iodevice_init(&pit->dev, &pit_dev_ops);
kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev);
Index: kvm/arch/x86/kvm/irq.h
===================================================================
--- kvm.orig/arch/x86/kvm/irq.h
+++ kvm/arch/x86/kvm/irq.h
@@ -98,7 +98,13 @@ void __kvm_migrate_apic_timer(struct kvm
void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu);
void __kvm_migrate_timers(struct kvm_vcpu *vcpu);
+void kvm_pit_internal_mask_notifier(struct kvm *kvm, bool mask);
+void kvm_pit_mask_notifier(struct kvm_irq_mask_notifier *kimn, bool mask);
+
int pit_has_pending_timer(struct kvm_vcpu *vcpu);
int apic_has_pending_timer(struct kvm_vcpu *vcpu);
+int pic_int_is_masked(struct kvm *kvm, int irq);
+int ioapic_int_is_masked(struct kvm *kvm, int irq);
+
#endif