kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 4/4] KVM: x86: Enable MSI for assigned device
@ 2008-09-26  5:17 Sheng Yang
  2008-10-05 10:27 ` Avi Kivity
  0 siblings, 1 reply; 7+ messages in thread
From: Sheng Yang @ 2008-09-26  5:17 UTC (permalink / raw)
  To: Avi Kivity; +Cc: kvm, Weidong", Allen M", Sheng Yang

As well as export ioapic_get_delivery_bitmask().

Signed-off-by: Sheng Yang <sheng.yang@intel.com>
---
 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread
* Re: [PATCH 4/4] KVM: x86: Enable MSI for assigned device
@ 2008-10-07  6:09 Sheng Yang
  0 siblings, 0 replies; 7+ messages in thread
From: Sheng Yang @ 2008-10-07  6:09 UTC (permalink / raw)
  To: Avi Kivity; +Cc: kvm, weidong.han, allen.m.kay

Update according to the comments. Thanks!

--
From: Sheng Yang <sheng@linux.intel.com>
Date: Tue, 7 Oct 2008 14:04:24 +0800
Subject: [PATCH 4/4] KVM: x86: Enable MSI for assigned device

As well as export ioapic_get_delivery_bitmask().

Signed-off-by: Sheng Yang <sheng@linux.intel.com>
---
 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 4836323..3e39485 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,24 @@ 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 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2008-10-08  2:58 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-26  5:17 [PATCH 4/4] KVM: x86: Enable MSI for assigned device Sheng Yang
2008-10-05 10:27 ` Avi Kivity
2008-10-06  3:36   ` Yang, Sheng
2008-10-07  5:59     ` Sheng Yang
2008-10-07 13:42       ` Avi Kivity
2008-10-08  2:55         ` Sheng Yang
  -- strict thread matches above, loose matches on Subject: below --
2008-10-07  6:09 Sheng Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).