From mboxrd@z Thu Jan 1 00:00:00 1970 From: Sheng Yang Subject: [PATCH 4/4] KVM: x86: Enable MSI for assigned device Date: Fri, 26 Sep 2008 13:17:35 +0800 Message-ID: <1222406255-27727-5-git-send-email-sheng.yang@intel.com> Cc: kvm@vger.kernel.org, "Weidong\"" , "Allen M\"" , Sheng Yang To: Avi Kivity Return-path: Received: from mga11.intel.com ([192.55.52.93]:54936 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751966AbYIZFQx (ORCPT ); Fri, 26 Sep 2008 01:16:53 -0400 In-Reply-To: <> References: <> Sender: kvm-owner@vger.kernel.org List-ID: As well as export ioapic_get_delivery_bitmask(). Signed-off-by: Sheng Yang --- 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 #include #include +#include #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