As well as export ioapic_get_delivery_bitmask().

Signed-off-by: Sheng Yang <[EMAIL PROTECTED]>
---
 arch/x86/kvm/x86.c       |  111 +++++++++++++++++++++++++++++++++++++++++++--
 include/linux/kvm.h      |    4 ++
 include/linux/kvm_host.h |    3 +
 virt/kvm/ioapic.c        |    3 +-
 virt/kvm/ioapic.h        |    2 +
 5 files changed, 117 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 4836323..66d96e2 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -40,6 +40,7 @@
 #include <asm/uaccess.h>
 #include <asm/msr.h>
 #include <asm/desc.h>
+#include <asm/msidef.h>
 
 #define MAX_IO_MSRS 256
 #define CR0_RESERVED_BITS                                              \
@@ -120,6 +121,50 @@ static struct kvm_assigned_dev_kernel 
*kvm_find_assigned_dev(struct list_head *h
        return NULL;
 }
 
+static void assigned_device_msi_dispatch(struct kvm_assigned_dev_kernel
+                                                       *assigned_dev)
+{
+       u8 dest_id = assigned_dev->guest_msi_addr & MSI_ADDR_DEST_ID_MASK;
+       u8 vector = assigned_dev->guest_msi_data & MSI_DATA_VECTOR_MASK;
+       u8 dest_mode = assigned_dev->guest_msi_addr &
+                                       (1 << MSI_ADDR_DEST_MODE_SHIFT);
+       u8 trig_mode = assigned_dev->guest_msi_data &
+                                       (1 << MSI_DATA_TRIGGER_SHIFT);
+       u8 delivery_mode = assigned_dev->guest_msi_data &
+                                       MSI_DATA_DELIVERY_MODE_MASK;
+       u32 deliver_bitmask;
+       int vcpu_id;
+       struct kvm_vcpu *vcpu;
+       struct kvm_ioapic *ioapic = ioapic_irqchip(assigned_dev->kvm);
+
+       BUG_ON(!ioapic);
+
+       deliver_bitmask = ioapic_get_delivery_bitmask(ioapic,
+                                                     dest_id, dest_mode);
+       switch (delivery_mode) {
+       case MSI_DATA_DELIVERY_LOWPRI_VAL:
+               vcpu = kvm_get_lowest_prio_vcpu(ioapic->kvm, vector,
+                               deliver_bitmask);
+               if (vcpu != NULL)
+                       kvm_apic_set_irq(vcpu, vector, trig_mode);
+               else
+                       printk(KERN_INFO "Null lowest priority vcpu!\n");
+               break;
+       case MSI_DATA_DELIVERY_FIXED_VAL:
+               for (vcpu_id = 0; deliver_bitmask != 0; vcpu_id++) {
+                       if (!(deliver_bitmask & (1 << vcpu_id)))
+                               continue;
+                       deliver_bitmask &= ~(1 << vcpu_id);
+                       vcpu = ioapic->kvm->vcpus[vcpu_id];
+                       if (vcpu)
+                               kvm_apic_set_irq(vcpu, vector, trig_mode);
+               }
+               break;
+       default:
+               printk(KERN_INFO "Unsupported MSI delivery mode\n");
+       }
+}
+
 static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
 {
        struct kvm_assigned_dev_kernel *assigned_dev;
@@ -132,8 +177,12 @@ static void kvm_assigned_dev_interrupt_work_handler(struct 
work_struct *work)
         * finer-grained lock, update this
         */
        mutex_lock(&assigned_dev->kvm->lock);
-       kvm_set_irq(assigned_dev->kvm,
-                   assigned_dev->guest_irq, 1);
+       if (assigned_dev->guest_intr_type == KVM_ASSIGNED_DEV_INTR)
+               kvm_set_irq(assigned_dev->kvm, assigned_dev->guest_irq, 1);
+       else if (assigned_dev->guest_intr_type == KVM_ASSIGNED_DEV_MSI) {
+               assigned_device_msi_dispatch(assigned_dev);
+               enable_irq(assigned_dev->host_irq);
+       }
        mutex_unlock(&assigned_dev->kvm->lock);
        kvm_put_kvm(assigned_dev->kvm);
 }
@@ -172,6 +221,8 @@ static void kvm_free_assigned_device(struct kvm *kvm,
 {
        if (irqchip_in_kernel(kvm) && assigned_dev->guest_intr_type)
                free_irq(assigned_dev->host_irq, (void *)assigned_dev);
+       if (assigned_dev->guest_intr_type == KVM_ASSIGNED_DEV_MSI)
+               pci_disable_msi(assigned_dev->dev);
 
        kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier);
 
@@ -215,6 +266,11 @@ static int assigned_device_update_irq(struct kvm *kvm,
                return 0;
        }
        if (irqchip_in_kernel(kvm)) {
+               if (assigned_dev->guest_intr_type == KVM_ASSIGNED_DEV_MSI) {
+                       free_irq(assigned_dev->host_irq, (void *)kvm);
+                       pci_disable_msi(assigned_dev->dev);
+               }
+
                if (!capable(CAP_SYS_RAWIO))
                        return -EPERM;
 
@@ -241,6 +297,40 @@ static int assigned_device_update_irq(struct kvm *kvm,
        return 0;
 }
 
+static int assigned_device_update_msi(struct kvm *kvm,
+                       struct kvm_assigned_dev_kernel *assigned_dev,
+                       struct kvm_assigned_irq *assigned_irq)
+{
+       int r;
+
+       if (assigned_dev->guest_intr_type == KVM_ASSIGNED_DEV_MSI)
+               return 0;
+
+       if (irqchip_in_kernel(kvm)) {
+               if (assigned_dev->guest_intr_type == KVM_ASSIGNED_DEV_INTR)
+                       free_irq(assigned_dev->host_irq, (void *)assigned_dev);
+
+               assigned_dev->guest_msi_addr = assigned_irq->guest_msi_addr;
+               assigned_dev->guest_msi_data = assigned_irq->guest_msi_data;
+
+               r = pci_enable_msi(assigned_dev->dev);
+               if (r)
+                       return r;
+
+               if (request_irq(assigned_dev->dev->irq, kvm_assigned_dev_intr,
+                               0, "kvm_msi_assigned_device",
+                               (void *)assigned_dev))
+                       return -EIO;
+
+               assigned_dev->host_irq = assigned_dev->dev->irq;
+               assigned_dev->kvm = kvm;
+               assigned_dev->guest_intr_type = KVM_ASSIGNED_DEV_MSI;
+       }
+
+       assigned_dev->ack_notifier.gsi = -1;
+       return 0;
+}
+
 static int kvm_vm_ioctl_assign_irq(struct kvm *kvm,
                                   struct kvm_assigned_irq
                                   *assigned_irq)
@@ -261,9 +351,20 @@ static int kvm_vm_ioctl_assign_irq(struct kvm *kvm,
                INIT_WORK(&match->interrupt_work,
                                kvm_assigned_dev_interrupt_work_handler);
 
-       r = assigned_device_update_irq(kvm, match, assigned_irq);
-       if (r)
-               goto out_release;
+       if (assigned_irq->flags & KVM_DEV_IRQ_ASSIGN_ENABLE_MSI) {
+               if (assigned_device_update_msi(kvm, match, assigned_irq)) {
+                       printk(KERN_INFO
+                                       "kvm: fail to enable msi, fall"
+                                       " back to legacy interrupt\n");
+                       if (assigned_device_update_irq(kvm, match,
+                                               assigned_irq))
+                               goto out_release;
+               }
+       } else {
+               r = assigned_device_update_irq(kvm, match, assigned_irq);
+               if (r)
+                       goto out_release;
+       }
 
        mutex_unlock(&kvm->lock);
        return r;
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 4269be1..a9b408b 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -493,9 +493,13 @@ struct kvm_assigned_irq {
        __u32 assigned_dev_id;
        __u32 host_irq;
        __u32 guest_irq;
+       __u16 guest_msi_data;
+       __u32 guest_msi_addr;
        __u32 flags;
 };
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU    (1 << 0)
 
+#define KVM_DEV_IRQ_ASSIGN_ENABLE_MSI  (1 << 0)
+
 #endif
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index e24280b..dc6a046 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -300,8 +300,11 @@ struct kvm_assigned_dev_kernel {
        int host_busnr;
        int host_devfn;
        int host_irq;
+       u16 guest_msi_addr;
+       u16 guest_msi_data;
        int guest_irq;
 #define KVM_ASSIGNED_DEV_INTR  1
+#define KVM_ASSIGNED_DEV_MSI   2
        int guest_intr_type;
        struct pci_dev *dev;
        struct kvm *kvm;
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index 53772bb..fbc329f 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -152,7 +152,7 @@ static void ioapic_inj_nmi(struct kvm_vcpu *vcpu)
        kvm_inject_nmi(vcpu);
 }
 
-static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest,
+u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest,
                                       u8 dest_mode)
 {
        u32 mask = 0;
@@ -191,6 +191,7 @@ static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic 
*ioapic, u8 dest,
        ioapic_debug("mask %x\n", mask);
        return mask;
 }
+EXPORT_SYMBOL_GPL(ioapic_get_delivery_bitmask);
 
 static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
 {
diff --git a/virt/kvm/ioapic.h b/virt/kvm/ioapic.h
index b52732f..29b7e0a 100644
--- a/virt/kvm/ioapic.h
+++ b/virt/kvm/ioapic.h
@@ -92,5 +92,7 @@ void kvm_ioapic_update_eoi(struct kvm *kvm, int vector, int 
trigger_mode);
 int kvm_ioapic_init(struct kvm *kvm);
 void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level);
 void kvm_ioapic_reset(struct kvm_ioapic *ioapic);
+u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest,
+                                      u8 dest_mode);
 
 #endif
-- 
1.5.4.5

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to