* [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-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
0 siblings, 1 reply; 7+ messages in thread
From: Avi Kivity @ 2008-10-05 10:27 UTC (permalink / raw)
To: Sheng Yang; +Cc: kvm, weidong.han, allen.m.kay
Sheng Yang wrote:
> As well as export ioapic_get_delivery_bitmask().
>
> @@ -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);
> + }
>
What happens if the host interrupt is level triggered pci and the guest
interrupt is msi? Or do we not support this combination?
If not, how do we prevent it?
> 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;
>
Need padding here, just to be safe.
> + __u32 guest_msi_addr;
>
Is u32 enough for the msi address? Including ia64?
> __u32 flags;
> };
>
> 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;
>
u32? or even u64?
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 4/4] KVM: x86: Enable MSI for assigned device
2008-10-05 10:27 ` Avi Kivity
@ 2008-10-06 3:36 ` Yang, Sheng
2008-10-07 5:59 ` Sheng Yang
0 siblings, 1 reply; 7+ messages in thread
From: Yang, Sheng @ 2008-10-06 3:36 UTC (permalink / raw)
To: Avi Kivity; +Cc: kvm@vger.kernel.org, Han, Weidong, Kay, Allen M
On Sunday 05 October 2008 18:27:20 Avi Kivity wrote:
> Sheng Yang wrote:
> > As well as export ioapic_get_delivery_bitmask().
> >
> > @@ -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);
> > + }
>
> What happens if the host interrupt is level triggered pci and the guest
> interrupt is msi? Or do we not support this combination?
>
> If not, how do we prevent it?
I think we don't need to support this combination. Currently, fail to enable
MSI would fallback to enable IRQ. And MSI disabled guest should not expose
MSI capability to guest. And also if guest fail to enable MSI, MSI enable bit
in PCI configuration space should be set 0.
So I would like to sent another return value to tell userspace MSI enable
failed. And before try to enable, we may also provide a interface to
userspace to know if MSI can be enabled.
> > 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;
>
> Need padding here, just to be safe.
>
> > + __u32 guest_msi_addr;
>
> Is u32 enough for the msi address? Including ia64?
>
> > __u32 flags;
> > };
> >
> > 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;
>
> u32? or even u64?
Oops...
Well, here is enough for MSI (I mean u16 for msi_data), for PCI spec define
the size. But I'd better extend msi_data to u32, later I will extend msi_addr
to u64 or msi_add_lo and msi_addr_hi, for the support of MSI-X.
--
regards
Yang, Sheng
>
> --
> error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 4/4] KVM: x86: Enable MSI for assigned device
2008-10-06 3:36 ` Yang, Sheng
@ 2008-10-07 5:59 ` Sheng Yang
2008-10-07 13:42 ` Avi Kivity
0 siblings, 1 reply; 7+ messages in thread
From: Sheng Yang @ 2008-10-07 5:59 UTC (permalink / raw)
To: kvm; +Cc: Avi Kivity, Han, Weidong, Kay, Allen M
On Monday 06 October 2008 11:36:08 Yang, Sheng wrote:
> On Sunday 05 October 2008 18:27:20 Avi Kivity wrote:
> > Sheng Yang wrote:
> > > As well as export ioapic_get_delivery_bitmask().
> > >
> > > @@ -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);
> > > + }
> >
> > What happens if the host interrupt is level triggered pci and the guest
> > interrupt is msi? Or do we not support this combination?
> >
> > If not, how do we prevent it?
>
> I think we don't need to support this combination. Currently, fail to
> enable MSI would fallback to enable IRQ. And MSI disabled guest should not
> expose MSI capability to guest. And also if guest fail to enable MSI, MSI
> enable bit in PCI configuration space should be set 0.
>
> So I would like to sent another return value to tell userspace MSI enable
> failed. And before try to enable, we may also provide a interface to
> userspace to know if MSI can be enabled.
Seems we needn't tell userspace if MSI can be enabled. It's determined mostly
by the device, and pci_enable_msi() checked that.
So I think QEmu can expose MSI capability if the device got it. If it's fail
to be enabled, just fall back to IRQ and return error to QEmu is enough, of
course, QEmu should set MSI enable bit after kernel space done.
--
regards
Yang, Sheng
>
> > > 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;
> >
> > Need padding here, just to be safe.
> >
> > > + __u32 guest_msi_addr;
> >
> > Is u32 enough for the msi address? Including ia64?
> >
> > > __u32 flags;
> > > };
> > >
> > > 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;
> >
> > u32? or even u64?
>
> Oops...
>
> Well, here is enough for MSI (I mean u16 for msi_data), for PCI spec define
> the size. But I'd better extend msi_data to u32, later I will extend
> msi_addr to u64 or msi_add_lo and msi_addr_hi, for the support of MSI-X.
>
> --
> regards
> Yang, Sheng
>
> > --
> > error compiling committee.c: too many arguments to function
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 4/4] KVM: x86: Enable MSI for assigned device
2008-10-07 5:59 ` Sheng Yang
@ 2008-10-07 13:42 ` Avi Kivity
2008-10-08 2:55 ` Sheng Yang
0 siblings, 1 reply; 7+ messages in thread
From: Avi Kivity @ 2008-10-07 13:42 UTC (permalink / raw)
To: Sheng Yang; +Cc: kvm, Han, Weidong, Kay, Allen M
Sheng Yang wrote:
> Seems we needn't tell userspace if MSI can be enabled. It's determined mostly
> by the device, and pci_enable_msi() checked that.
>
> So I think QEmu can expose MSI capability if the device got it. If it's fail
> to be enabled, just fall back to IRQ and return error to QEmu is enough, of
> course, QEmu should set MSI enable bit after kernel space done.
>
Do we need to add the additional condition, "and if the host kernel
supports it"?
--
error compiling committee.c: too many arguments to function
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 4/4] KVM: x86: Enable MSI for assigned device
2008-10-07 13:42 ` Avi Kivity
@ 2008-10-08 2:55 ` Sheng Yang
0 siblings, 0 replies; 7+ messages in thread
From: Sheng Yang @ 2008-10-08 2:55 UTC (permalink / raw)
To: Avi Kivity; +Cc: kvm, Han, Weidong, Kay, Allen M
On Tuesday 07 October 2008 21:42:41 Avi Kivity wrote:
> Sheng Yang wrote:
> > Seems we needn't tell userspace if MSI can be enabled. It's determined
> > mostly by the device, and pci_enable_msi() checked that.
> >
> > So I think QEmu can expose MSI capability if the device got it. If it's
> > fail to be enabled, just fall back to IRQ and return error to QEmu is
> > enough, of course, QEmu should set MSI enable bit after kernel space
> > done.
>
> Do we need to add the additional condition, "and if the host kernel
> supports it"?
Well, for VT-d, it's impossible because CONFIG_DMAR depends on
CONFIG_PCI_MSI...
And for non-VT-d(I don't know about PV or AMD IOMMU), from 2.6.18, MSI support
have been added, before KVM merged. And we also got
ifndef CONFIG_PCI_MSI, pci_enable_msi return -1.
So we can still fall back to IRQ without error.
How do you think?
--
regards
Yang, Sheng
^ permalink raw reply [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).