KVM: x86: Call mask notifiers from pic
[pandora-kernel.git] / arch / x86 / kvm / i8259.c
index a790fa1..8d10c06 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2003-2004 Fabrice Bellard
  * Copyright (c) 2007 Intel Corporation
+ * Copyright 2009 Red Hat, Inc. and/or its affilates.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
 #include <linux/kvm_host.h>
 #include "trace.h"
 
+static void pic_irq_request(struct kvm *kvm, int level);
+
+static void pic_lock(struct kvm_pic *s)
+       __acquires(&s->lock)
+{
+       raw_spin_lock(&s->lock);
+}
+
+static void pic_unlock(struct kvm_pic *s)
+       __releases(&s->lock)
+{
+       bool wakeup = s->wakeup_needed;
+       struct kvm_vcpu *vcpu, *found = NULL;
+       int i;
+
+       s->wakeup_needed = false;
+
+       raw_spin_unlock(&s->lock);
+
+       if (wakeup) {
+               kvm_for_each_vcpu(i, vcpu, s->kvm) {
+                       if (kvm_apic_accept_pic_intr(vcpu)) {
+                               found = vcpu;
+                               break;
+                       }
+               }
+
+               if (!found)
+                       found = s->kvm->bsp_vcpu;
+
+               kvm_vcpu_kick(found);
+       }
+}
+
 static void pic_clear_isr(struct kvm_kpic_state *s, int irq)
 {
        s->isr &= ~(1 << irq);
@@ -45,19 +80,19 @@ static void pic_clear_isr(struct kvm_kpic_state *s, int irq)
         * Other interrupt may be delivered to PIC while lock is dropped but
         * it should be safe since PIC state is already updated at this stage.
         */
-       raw_spin_unlock(&s->pics_state->lock);
+       pic_unlock(s->pics_state);
        kvm_notify_acked_irq(s->pics_state->kvm, SELECT_PIC(irq), irq);
-       raw_spin_lock(&s->pics_state->lock);
+       pic_lock(s->pics_state);
 }
 
 void kvm_pic_clear_isr_ack(struct kvm *kvm)
 {
        struct kvm_pic *s = pic_irqchip(kvm);
 
-       raw_spin_lock(&s->lock);
+       pic_lock(s);
        s->pics[0].isr_ack = 0xff;
        s->pics[1].isr_ack = 0xff;
-       raw_spin_unlock(&s->lock);
+       pic_unlock(s);
 }
 
 /*
@@ -150,17 +185,14 @@ static void pic_update_irq(struct kvm_pic *s)
                pic_set_irq1(&s->pics[0], 2, 0);
        }
        irq = pic_get_irq(&s->pics[0]);
-       if (irq >= 0)
-               s->irq_request(s->irq_request_opaque, 1);
-       else
-               s->irq_request(s->irq_request_opaque, 0);
+       pic_irq_request(s->kvm, irq >= 0);
 }
 
 void kvm_pic_update_irq(struct kvm_pic *s)
 {
-       raw_spin_lock(&s->lock);
+       pic_lock(s);
        pic_update_irq(s);
-       raw_spin_unlock(&s->lock);
+       pic_unlock(s);
 }
 
 int kvm_pic_set_irq(void *opaque, int irq, int level)
@@ -168,14 +200,14 @@ int kvm_pic_set_irq(void *opaque, int irq, int level)
        struct kvm_pic *s = opaque;
        int ret = -1;
 
-       raw_spin_lock(&s->lock);
+       pic_lock(s);
        if (irq >= 0 && irq < PIC_NUM_PINS) {
                ret = pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
                pic_update_irq(s);
                trace_kvm_pic_set_irq(irq >> 3, irq & 7, s->pics[irq >> 3].elcr,
                                      s->pics[irq >> 3].imr, ret == 0);
        }
-       raw_spin_unlock(&s->lock);
+       pic_unlock(s);
 
        return ret;
 }
@@ -205,7 +237,7 @@ int kvm_pic_read_irq(struct kvm *kvm)
        int irq, irq2, intno;
        struct kvm_pic *s = pic_irqchip(kvm);
 
-       raw_spin_lock(&s->lock);
+       pic_lock(s);
        irq = pic_get_irq(&s->pics[0]);
        if (irq >= 0) {
                pic_intack(&s->pics[0], irq);
@@ -230,7 +262,7 @@ int kvm_pic_read_irq(struct kvm *kvm)
                intno = s->pics[0].irq_base + irq;
        }
        pic_update_irq(s);
-       raw_spin_unlock(&s->lock);
+       pic_unlock(s);
 
        return intno;
 }
@@ -238,8 +270,7 @@ int kvm_pic_read_irq(struct kvm *kvm)
 void kvm_pic_reset(struct kvm_kpic_state *s)
 {
        int irq;
-       struct kvm *kvm = s->pics_state->irq_request_opaque;
-       struct kvm_vcpu *vcpu0 = kvm->bsp_vcpu;
+       struct kvm_vcpu *vcpu0 = s->pics_state->kvm->bsp_vcpu;
        u8 irr = s->irr, isr = s->imr;
 
        s->last_irr = 0;
@@ -278,8 +309,7 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
                        /*
                         * deassert a pending interrupt
                         */
-                       s->pics_state->irq_request(s->pics_state->
-                                                  irq_request_opaque, 0);
+                       pic_irq_request(s->pics_state->kvm, 0);
                        s->init_state = 1;
                        s->init4 = val & 1;
                        if (val & 0x02)
@@ -333,10 +363,20 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
                }
        } else
                switch (s->init_state) {
-               case 0:         /* normal mode */
+               case 0: { /* normal mode */
+                       u8 imr_diff = s->imr ^ val,
+                               off = (s == &s->pics_state->pics[0]) ? 0 : 8;
                        s->imr = val;
+                       for (irq = 0; irq < PIC_NUM_PINS/2; irq++)
+                               if (imr_diff & (1 << irq))
+                                       kvm_fire_mask_notifiers(
+                                               s->pics_state->kvm,
+                                               SELECT_PIC(irq + off),
+                                               irq + off,
+                                               !!(s->imr & (1 << irq)));
                        pic_update_irq(s->pics_state);
                        break;
+               }
                case 1:
                        s->irq_base = val & 0xf8;
                        s->init_state = 2;
@@ -444,7 +484,7 @@ static int picdev_write(struct kvm_io_device *this,
                        printk(KERN_ERR "PIC: non byte write\n");
                return 0;
        }
-       raw_spin_lock(&s->lock);
+       pic_lock(s);
        switch (addr) {
        case 0x20:
        case 0x21:
@@ -457,7 +497,7 @@ static int picdev_write(struct kvm_io_device *this,
                elcr_ioport_write(&s->pics[addr & 1], addr, data);
                break;
        }
-       raw_spin_unlock(&s->lock);
+       pic_unlock(s);
        return 0;
 }
 
@@ -474,7 +514,7 @@ static int picdev_read(struct kvm_io_device *this,
                        printk(KERN_ERR "PIC: non byte read\n");
                return 0;
        }
-       raw_spin_lock(&s->lock);
+       pic_lock(s);
        switch (addr) {
        case 0x20:
        case 0x21:
@@ -488,16 +528,15 @@ static int picdev_read(struct kvm_io_device *this,
                break;
        }
        *(unsigned char *)val = data;
-       raw_spin_unlock(&s->lock);
+       pic_unlock(s);
        return 0;
 }
 
 /*
  * callback when PIC0 irq status changed
  */
-static void pic_irq_request(void *opaque, int level)
+static void pic_irq_request(struct kvm *kvm, int level)
 {
-       struct kvm *kvm = opaque;
        struct kvm_vcpu *vcpu = kvm->bsp_vcpu;
        struct kvm_pic *s = pic_irqchip(kvm);
        int irq = pic_get_irq(&s->pics[0]);
@@ -505,7 +544,7 @@ static void pic_irq_request(void *opaque, int level)
        s->output = level;
        if (vcpu && level && (s->pics[0].isr_ack & (1 << irq))) {
                s->pics[0].isr_ack &= ~(1 << irq);
-               kvm_vcpu_kick(vcpu);
+               s->wakeup_needed = true;
        }
 }
 
@@ -526,8 +565,6 @@ struct kvm_pic *kvm_create_pic(struct kvm *kvm)
        s->kvm = kvm;
        s->pics[0].elcr_mask = 0xf8;
        s->pics[1].elcr_mask = 0xde;
-       s->irq_request = pic_irq_request;
-       s->irq_request_opaque = kvm;
        s->pics[0].pics_state = s;
        s->pics[1].pics_state = s;