From mboxrd@z Thu Jan 1 00:00:00 1970 From: Sheng Yang Subject: [PATCH 4/4] KVM: Enable MSI for device assignment Date: Wed, 8 Oct 2008 16:38:13 +0800 Message-ID: <1223455093-304-5-git-send-email-sheng@linux.intel.com> References: <1223455093-304-1-git-send-email-sheng@linux.intel.com> Cc: kvm@vger.kernel.org, Sheng Yang To: Avi Kivity Return-path: Received: from mga11.intel.com ([192.55.52.93]:30515 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754827AbYJHIjY (ORCPT ); Wed, 8 Oct 2008 04:39:24 -0400 In-Reply-To: <1223455093-304-1-git-send-email-sheng@linux.intel.com> Sender: kvm-owner@vger.kernel.org List-ID: Signed-off-by: Sheng Yang --- arch/x86/kvm/x86.c | 115 ++++++++++++++++++++++++++++++++++++++++++++-- include/linux/kvm.h | 4 ++ include/linux/kvm_host.h | 3 + virt/kvm/ioapic.c | 2 +- virt/kvm/ioapic.h | 2 + 5 files changed, 120 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 19688b3..a4bedf5 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -41,6 +41,7 @@ #include #include #include +#include #define MAX_IO_MSRS 256 #define CR0_RESERVED_BITS \ @@ -121,6 +122,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; @@ -133,8 +178,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); } @@ -173,6 +222,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); @@ -216,6 +267,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; @@ -240,6 +296,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) @@ -268,9 +358,24 @@ static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, } } - 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 as guest " + "requested, fall back to legacy " + "interrupt\n"); + r = -EINVAL; + if (assigned_device_update_irq(kvm, match, + assigned_irq)) { + printk(KERN_INFO "kvm: fail to fall back, " + "release assigned device\n"); + 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..b7890c1 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; + __u32 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..1a3b4e4 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; + u32 guest_msi_addr; + u32 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..153ec2d 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; 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