Skip to contentSkip to conversation

MSI-X mask emulation support for assigned device

PATCH Superseded Sheng Yang 6 participants 66 messages Oct 20, 2010 – Oct 28, 2010

Subsystems (6)
Documentation x86 KVM PCI +2 more subsystems

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Sheng Yang

author
#0 lore raw

v2 cover MSI-X mask emulation support for assigned device

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Sheng Yang (8):
PCI: MSI: Move MSI-X entry definition to pci_regs.h
irq: Export irq_to_desc() to modules
KVM: x86: Enable ENABLE_CAP capability for x86
KVM: Move struct kvm_io_device to kvm_host.h
KVM: Add kvm_get_irq_routing_entry() func
KVM: assigned dev: Preparation for mask support in userspace
KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing
KVM: Emulation MSI-X mask bits for assigned devices

 Documentation/kvm/api.txt       |   30 +++++-
 arch/x86/include/asm/kvm_host.h |    2 +
 arch/x86/kvm/x86.c              |   32 ++++++
 drivers/pci/msi.h               |    6 -
 include/linux/kvm.h             |   19 +++-
 include/linux/kvm_host.h        |   28 +++++
 include/linux/pci_regs.h        |    7 +
 kernel/irq/handle.c             |    2 +
 virt/kvm/assigned-dev.c         |  230 +++++++++++++++++++++++++++++++++++++++
 virt/kvm/iodev.h                |   25 +----
 virt/kvm/irq_comm.c             |   20 ++++
 11 files changed, 367 insertions(+), 34 deletions(-)
author
#1 lore raw

PATCH v2 1/8 PCI: MSI: Move MSI-X entry definition to pci_regs.h

It would be used by KVM later.

---
 drivers/pci/msi.h        |    6 ------
 include/linux/pci_regs.h |    7 +++++++
 2 files changed, 7 insertions(+), 6 deletions(-)
drivers/pci/msi.h +0 -6
index de27c1c..28a3c52 100644
@@ -6,12 +6,6 @@
 #ifndef MSI_H
 #define MSI_H
 
-#define PCI_MSIX_ENTRY_SIZE		16
-#define  PCI_MSIX_ENTRY_LOWER_ADDR	0
-#define  PCI_MSIX_ENTRY_UPPER_ADDR	4
-#define  PCI_MSIX_ENTRY_DATA		8
-#define  PCI_MSIX_ENTRY_VECTOR_CTRL	12
-
 #define msi_control_reg(base)		(base + PCI_MSI_FLAGS)
 #define msi_lower_address_reg(base)	(base + PCI_MSI_ADDRESS_LO)
 #define msi_upper_address_reg(base)	(base + PCI_MSI_ADDRESS_HI)
include/linux/pci_regs.h +7 -0
index 455b9cc..acfc224 100644
@@ -307,6 +307,13 @@
 #define  PCI_MSIX_FLAGS_MASKALL	(1 << 14)
 #define PCI_MSIX_FLAGS_BIRMASK	(7 << 0)
 
+/* MSI-X entry's format */
+#define PCI_MSIX_ENTRY_SIZE		16
+#define  PCI_MSIX_ENTRY_LOWER_ADDR	0
+#define  PCI_MSIX_ENTRY_UPPER_ADDR	4
+#define  PCI_MSIX_ENTRY_DATA		8
+#define  PCI_MSIX_ENTRY_VECTOR_CTRL	12
+
 /* CompactPCI Hotswap Register */
 
 #define PCI_CHSWP_CSR		2	/* Control and Status Register */
Signature · 1 line
1.7.0.1
Reviewed-by on v2 1/8
#2 lore raw

Re: [PATCH 1/8] PCI: MSI: Move MSI-X entry definition to pci_regs.h

Quoted 5 lines · Sheng Yang

It would be used by KVM later.

Thanks for doing this. It should have been done long ago :-)

Signature · 4 lines
Matthew Wilcox				Intel Open Source Technology Centre
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours.  We can't possibly take such
a retrograde step."
author
#3 lore raw

PATCH v2 2/8 irq: Export irq_to_desc() to modules

In reply to Sheng Yang

KVM need to execute mask/unmask directly on MSI/MSI-X devices. The alternative way of doing this is export mask_msi_irq(), but it lacks of checking if IRQ type was really MSI.

---
 kernel/irq/handle.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
kernel/irq/handle.c +2 -0
index 27e5c69..1ea2a24 100644
@@ -139,6 +139,7 @@ struct irq_desc *irq_to_desc(unsigned int irq)
 {
 	return radix_tree_lookup(&irq_desc_tree, irq);
 }
+EXPORT_SYMBOL_GPL(irq_to_desc);
 
 void replace_irq_desc(unsigned int irq, struct irq_desc *desc)
 {
@@ -276,6 +277,7 @@ struct irq_desc *irq_to_desc(unsigned int irq)
 {
 	return (irq < NR_IRQS) ? irq_desc + irq : NULL;
 }
+EXPORT_SYMBOL_GPL(irq_to_desc);
 
 struct irq_desc *irq_to_desc_alloc_node(unsigned int irq, int node)
 {
Signature · 1 line
1.7.0.1
author
#4 lore raw

PATCH v2 3/8 KVM: x86: Enable ENABLE_CAP capability for x86

In reply to Sheng Yang

It would be used later by KVM_CAP_MSIX_MASK related interfaces.

---
 Documentation/kvm/api.txt |    8 +++++---
 arch/x86/kvm/x86.c        |   26 ++++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 3 deletions(-)
Documentation/kvm/api.txt +5 -3
index b336266..d82d637 100644
@@ -817,7 +817,7 @@ documentation when it pops into existence).
 4.36 KVM_ENABLE_CAP
 
 Capability: KVM_CAP_ENABLE_CAP
-Architectures: ppc
+Architectures: ppc, x86
 Type: vcpu ioctl
 Parameters: struct kvm_enable_cap (in)
 Returns: 0 on success; -1 on error
@@ -828,8 +828,10 @@ can enable an extension, making it available to the guest.
 On systems that do not support this ioctl, it always fails. On systems that
 do support it, it only works for extensions that are supported for enablement.
 
-To check if a capability can be enabled, the KVM_CHECK_EXTENSION ioctl should
-be used.
+For PPC, to check if a capability can be enabled, the KVM_CHECK_EXTENSION
+ioctl should be used.
+
+For x86, only some specific capabilities need to be enabled before use.
 
 struct kvm_enable_cap {
        /* in */
arch/x86/kvm/x86.c +26 -0
index f3f86b2..fc62546 100644
@@ -1926,6 +1926,7 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_DEBUGREGS:
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
+	case KVM_CAP_ENABLE_CAP:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -2707,6 +2708,23 @@ static int kvm_vcpu_ioctl_x86_set_xcrs(struct kvm_vcpu *vcpu,
 	return r;
 }
 
+static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
+				     struct kvm_enable_cap *cap)
+{
+	int r;
+
+	if (cap->flags)
+		return -EINVAL;
+
+	switch (cap->cap) {
+	default:
+		r = -EINVAL;
+		break;
+	}
+
+	return r;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
 			 unsigned int ioctl, unsigned long arg)
 {
@@ -2970,6 +2988,14 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 		r = kvm_vcpu_ioctl_x86_set_xcrs(vcpu, u.xcrs);
 		break;
 	}
+	case KVM_ENABLE_CAP: {
+		struct kvm_enable_cap cap;
+		r = -EFAULT;
+		if (copy_from_user(&cap, argp, sizeof(cap)))
+			goto out;
+		r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
+		break;
+	}
 	default:
 		r = -EINVAL;
 	}
Signature · 1 line
1.7.0.1
author
#5 lore raw

PATCH v2 4/8 KVM: Move struct kvm_io_device to kvm_host.h

In reply to Sheng Yang

Then it can be used in struct kvm_assigned_dev_kernel later.

---
 include/linux/kvm_host.h |   23 +++++++++++++++++++++++
 virt/kvm/iodev.h         |   25 +------------------------
 2 files changed, 24 insertions(+), 24 deletions(-)
include/linux/kvm_host.h +23 -0
index 0b89d00..7f9e4b7 100644
@@ -74,6 +74,29 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx,
 int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
 			      struct kvm_io_device *dev);
 
+struct kvm_io_device;
+
+/**
+ * kvm_io_device_ops are called under kvm slots_lock.
+ * read and write handlers return 0 if the transaction has been handled,
+ * or non-zero to have it passed to the next device.
+ **/
+struct kvm_io_device_ops {
+	int (*read)(struct kvm_io_device *this,
+		    gpa_t addr,
+		    int len,
+		    void *val);
+	int (*write)(struct kvm_io_device *this,
+		     gpa_t addr,
+		     int len,
+		     const void *val);
+	void (*destructor)(struct kvm_io_device *this);
+};
+
+struct kvm_io_device {
+	const struct kvm_io_device_ops *ops;
+};
+
 struct kvm_vcpu {
 	struct kvm *kvm;
 #ifdef CONFIG_PREEMPT_NOTIFIERS
virt/kvm/iodev.h +1 -24
index 12fd3ca..d1f5651 100644
@@ -17,32 +17,9 @@
 #define __KVM_IODEV_H__
 
 #include <linux/kvm_types.h>
+#include <linux/kvm_host.h>
 #include <asm/errno.h>
 
-struct kvm_io_device;
-
-/**
- * kvm_io_device_ops are called under kvm slots_lock.
- * read and write handlers return 0 if the transaction has been handled,
- * or non-zero to have it passed to the next device.
- **/
-struct kvm_io_device_ops {
-	int (*read)(struct kvm_io_device *this,
-		    gpa_t addr,
-		    int len,
-		    void *val);
-	int (*write)(struct kvm_io_device *this,
-		     gpa_t addr,
-		     int len,
-		     const void *val);
-	void (*destructor)(struct kvm_io_device *this);
-};
-
-
-struct kvm_io_device {
-	const struct kvm_io_device_ops *ops;
-};
-
 static inline void kvm_iodevice_init(struct kvm_io_device *dev,
 				     const struct kvm_io_device_ops *ops)
 {
Signature · 1 line
1.7.0.1
author
#6 lore raw

PATCH v2 5/8 KVM: Add kvm_get_irq_routing_entry() func

In reply to Sheng Yang

We need to query the entry later.

---
 include/linux/kvm_host.h |    2 ++
 virt/kvm/irq_comm.c      |   20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 0 deletions(-)
include/linux/kvm_host.h +2 -0
index 7f9e4b7..30f83cd 100644
@@ -614,6 +614,8 @@ int kvm_set_irq_routing(struct kvm *kvm,
 			const struct kvm_irq_routing_entry *entries,
 			unsigned nr,
 			unsigned flags);
+struct kvm_kernel_irq_routing_entry *kvm_get_irq_routing_entry(struct kvm *kvm,
+							int gsi);
 void kvm_free_irq_routing(struct kvm *kvm);
 
 #else
virt/kvm/irq_comm.c +20 -0
index 8edca91..80ced8c 100644
@@ -421,6 +421,26 @@ out:
 	return r;
 }
 
+struct kvm_kernel_irq_routing_entry *kvm_get_irq_routing_entry(struct kvm *kvm,
+							       int gsi)
+{
+	int count = 0;
+	struct kvm_kernel_irq_routing_entry *ei = NULL;
+	struct kvm_irq_routing_table *irq_rt;
+	struct hlist_node *n;
+
+	rcu_read_lock();
+	irq_rt = rcu_dereference(kvm->irq_routing);
+	if (gsi < irq_rt->nr_rt_entries)
+		hlist_for_each_entry(ei, n, &irq_rt->map[gsi], link)
+			count++;
+	rcu_read_unlock();
+	if (count == 1)
+		return ei;
+
+	return NULL;
+}
+
 #define IOAPIC_ROUTING_ENTRY(irq) \
 	{ .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP,	\
 	  .u.irqchip.irqchip = KVM_IRQCHIP_IOAPIC, .u.irqchip.pin = (irq) }
Signature · 1 line
1.7.0.1
#7 lore raw

Re: [PATCH 5/8] KVM: Add kvm_get_irq_routing_entry() func

Quoted 13 lines · Sheng Yang

We need to query the entry later.

virt/kvm/irq_comm.c +10 -0
+struct kvm_kernel_irq_routing_entry *kvm_get_irq_routing_entry(struct kvm *kvm,
+							       int gsi)
+{
+	int count = 0;
+	struct kvm_kernel_irq_routing_entry *ei = NULL;
+	struct kvm_irq_routing_table *irq_rt;
+	struct hlist_node *n;
+
+	rcu_read_lock();
+	irq_rt = rcu_dereference(kvm->irq_routing);

virt/kvm/irq_comm.c out: · line 443

Quoted 10 lines
virt/kvm/irq_comm.c +10 -0
+	if (gsi<  irq_rt->nr_rt_entries)
+		hlist_for_each_entry(ei, n,&irq_rt->map[gsi], link)
+			count++;
+	rcu_read_unlock();
+	if (count == 1)
+		return ei;
+
+	return NULL;
+}
+

I believe this is incorrect rcu usage. rcu_read_lock() prevents ei from being destroyed under us, but rcu_read_unlock() removes that protection, and a future dereference of ei may access freed memory.

Signature · 1 line
error compiling committee.c: too many arguments to function
author
#8 lore raw

Re: [PATCH 5/8] KVM: Add kvm_get_irq_routing_entry() func

Quoted 20 lines · On Wednesday 20 October 2010 16:53:02 Avi Kivity wrote:
Quoted 19 lines · Sheng Yang

We need to query the entry later.

+struct kvm_kernel_irq_routing_entry *kvm_get_irq_routing_entry(struct
kvm *kvm, + int gsi)

virt/kvm/irq_comm.c +14 -0
+{
+	int count = 0;
+	struct kvm_kernel_irq_routing_entry *ei = NULL;
+	struct kvm_irq_routing_table *irq_rt;
+	struct hlist_node *n;
+
+	rcu_read_lock();
+	irq_rt = rcu_dereference(kvm->irq_routing);
+	if (gsi<  irq_rt->nr_rt_entries)
+		hlist_for_each_entry(ei, n,&irq_rt->map[gsi], link)
+			count++;
+	rcu_read_unlock();
+	if (count == 1)
+		return ei;
Quoted 8 lines

virt/kvm/irq_comm.c out: · line 443

Quoted 4 lines
virt/kvm/irq_comm.c +4 -0
+
+	return NULL;
+}
+

I believe this is incorrect rcu usage. rcu_read_lock() prevents ei from being destroyed under us, but rcu_read_unlock() removes that protection, and a future dereference of ei may access freed memory.

Yes... I would update the patch by copying it to caller's variable.

--
regards
Yang, Sheng

author
#9 lore raw

PATCH v2 5/8 KVM: Add kvm_get_irq_routing_entry() func

In reply to Avi Kivity

We need to query the entry later.

---
 include/linux/kvm_host.h |    2 ++
 virt/kvm/irq_comm.c      |   20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 0 deletions(-)
include/linux/kvm_host.h +2 -0
index 7f9e4b7..e2ecbac 100644
@@ -614,6 +614,8 @@ int kvm_set_irq_routing(struct kvm *kvm,
 			const struct kvm_irq_routing_entry *entries,
 			unsigned nr,
 			unsigned flags);
+int kvm_get_irq_routing_entry(struct kvm *kvm, int gsi,
+		struct kvm_kernel_irq_routing_entry *entry);
 void kvm_free_irq_routing(struct kvm *kvm);
 
 #else
virt/kvm/irq_comm.c +20 -0
index 8edca91..870d083 100644
@@ -421,6 +421,26 @@ out:
 	return r;
 }
 
+int kvm_get_irq_routing_entry(struct kvm *kvm, int gsi,
+		struct kvm_kernel_irq_routing_entry *entry)
+{
+	int count = 0;
+	struct kvm_kernel_irq_routing_entry *ei = NULL;
+	struct kvm_irq_routing_table *irq_rt;
+	struct hlist_node *n;
+
+	rcu_read_lock();
+	irq_rt = rcu_dereference(kvm->irq_routing);
+	if (gsi < irq_rt->nr_rt_entries)
+		hlist_for_each_entry(ei, n, &irq_rt->map[gsi], link)
+			count++;
+	rcu_read_unlock();
+	if (count == 1)
+		memcpy(entry, ei, sizeof(*ei));
+
+	return count == 1;
+}
+
 #define IOAPIC_ROUTING_ENTRY(irq) \
 	{ .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP,	\
 	  .u.irqchip.irqchip = KVM_IRQCHIP_IOAPIC, .u.irqchip.pin = (irq) }
Signature · 1 line
1.7.0.1
author
#10 lore raw

PATCH v2 5/8 KVM: Add kvm_get_irq_routing_entry() func

We need to query the entry later.

---
 include/linux/kvm_host.h |    2 ++
 virt/kvm/irq_comm.c      |   20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 0 deletions(-)
include/linux/kvm_host.h +2 -0
index 7f9e4b7..e2ecbac 100644
@@ -614,6 +614,8 @@ int kvm_set_irq_routing(struct kvm *kvm,
 			const struct kvm_irq_routing_entry *entries,
 			unsigned nr,
 			unsigned flags);
+int kvm_get_irq_routing_entry(struct kvm *kvm, int gsi,
+		struct kvm_kernel_irq_routing_entry *entry);
 void kvm_free_irq_routing(struct kvm *kvm);
 
 #else
virt/kvm/irq_comm.c +20 -0
index 8edca91..870d083 100644
@@ -421,6 +421,26 @@ out:
 	return r;
 }
 
+int kvm_get_irq_routing_entry(struct kvm *kvm, int gsi,
+		struct kvm_kernel_irq_routing_entry *entry)
+{
+	int count = 0;
+	struct kvm_kernel_irq_routing_entry *ei = NULL;
+	struct kvm_irq_routing_table *irq_rt;
+	struct hlist_node *n;
+
+	rcu_read_lock();
+	irq_rt = rcu_dereference(kvm->irq_routing);
+	if (gsi < irq_rt->nr_rt_entries)
+		hlist_for_each_entry(ei, n, &irq_rt->map[gsi], link)
+			count++;
+	rcu_read_unlock();
+	if (count == 1)
+		memcpy(entry, ei, sizeof(*ei));
+
+	return (count != 1);
+}
+
 #define IOAPIC_ROUTING_ENTRY(irq) \
 	{ .gsi = irq, .type = KVM_IRQ_ROUTING_IRQCHIP,	\
 	  .u.irqchip.irqchip = KVM_IRQCHIP_IOAPIC, .u.irqchip.pin = (irq) }
Signature · 1 line
1.7.0.1
#11 lore raw

Re: [PATCH 5/8] KVM: Add kvm_get_irq_routing_entry() func

Quoted 8 lines · Sheng Yang
  }
virt/kvm/irq_comm.c +6 -0
+int kvm_get_irq_routing_entry(struct kvm *kvm, int gsi,
+		struct kvm_kernel_irq_routing_entry *entry)
+{
+	int count = 0;
+	struct kvm_kernel_irq_routing_entry *ei = NULL;
+	struct kvm_irq_routing_table *irq_rt;

virt/kvm/irq_comm.c out: · line 439

Quoted 10 lines
virt/kvm/irq_comm.c +10 -0
+	struct hlist_node *n;
+
+	rcu_read_lock();
+	irq_rt = rcu_dereference(kvm->irq_routing);
+	if (gsi<  irq_rt->nr_rt_entries)
+		hlist_for_each_entry(ei, n,&irq_rt->map[gsi], link)
+			count++;
+	rcu_read_unlock();
+	if (count == 1)
+		memcpy(entry, ei, sizeof(*ei));

Need to be before the unlock.

virt/kvm/irq_comm.c out: · line 443

Quoted 4 lines
virt/kvm/irq_comm.c +4 -0
+
+	return (count != 1);
+}
+

"*entry = *ei" is clearer and safer. But is it correct? we might be using outdated data.

Signature · 1 line
error compiling committee.c: too many arguments to function
author
#12 lore raw

PATCH v2 6/8 KVM: assigned dev: Preparation for mask support in userspace

In reply to Sheng Yang

The feature wouldn't be enabled until later patch set msix_flags_enabled. It would be enabled along with mask support in kernel.

---
 arch/x86/include/asm/kvm_host.h |    2 ++
 include/linux/kvm.h             |    6 +++++-
 include/linux/kvm_host.h        |    1 +
 virt/kvm/assigned-dev.c         |   39 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 47 insertions(+), 1 deletions(-)
arch/x86/include/asm/kvm_host.h +2 -0
index e209078..2bb69ba 100644
@@ -456,6 +456,8 @@ struct kvm_arch {
 	/* fields used by HYPER-V emulation */
 	u64 hv_guest_os_id;
 	u64 hv_hypercall;
+
+	bool msix_flags_enabled;
 };
 
 struct kvm_vm_stat {
include/linux/kvm.h +5 -1
index 919ae53..a699ec9 100644
@@ -787,11 +787,15 @@ struct kvm_assigned_msix_nr {
 };
 
 #define KVM_MAX_MSIX_PER_DEV		256
+
+#define KVM_MSIX_FLAG_MASK	1
+
 struct kvm_assigned_msix_entry {
 	__u32 assigned_dev_id;
 	__u32 gsi;
 	__u16 entry; /* The index of entry in the MSI-X table */
-	__u16 padding[3];
+	__u16 flags;
+	__u16 padding[2];
 };
 
 #endif /* __LINUX_KVM_H */
include/linux/kvm_host.h +1 -0
index 30f83cd..81a6284 100644
@@ -438,6 +438,7 @@ struct kvm_irq_ack_notifier {
 };
 
 #define KVM_ASSIGNED_MSIX_PENDING		0x1
+#define KVM_ASSIGNED_MSIX_MASK			0x2
 struct kvm_guest_msix_entry {
 	u32 vector;
 	u16 entry;
virt/kvm/assigned-dev.c +39 -0
index 7c98928..bf96ea7 100644
@@ -666,11 +666,35 @@ msix_nr_out:
 	return r;
 }
 
+static void update_msix_mask(struct kvm_assigned_dev_kernel *assigned_dev,
+			     int index)
+{
+	int irq;
+	struct irq_desc *desc;
+
+	if (!assigned_dev->dev->msix_enabled ||
+	    !(assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX))
+		return;
+
+	irq = assigned_dev->host_msix_entries[index].vector;
+	BUG_ON(irq == 0);
+	desc = irq_to_desc(irq);
+	BUG_ON(!desc->msi_desc);
+
+	if (assigned_dev->guest_msix_entries[index].flags &
+			KVM_ASSIGNED_MSIX_MASK) {
+		desc->chip->mask(irq);
+		flush_work(&assigned_dev->interrupt_work);
+	} else
+		desc->chip->unmask(irq);
+}
+
 static int kvm_vm_ioctl_set_msix_entry(struct kvm *kvm,
 				       struct kvm_assigned_msix_entry *entry)
 {
 	int r = 0, i;
 	struct kvm_assigned_dev_kernel *adev;
+	bool entry_masked;
 
 	mutex_lock(&kvm->lock);
 
@@ -688,6 +712,21 @@ static int kvm_vm_ioctl_set_msix_entry(struct kvm *kvm,
 			adev->guest_msix_entries[i].entry = entry->entry;
 			adev->guest_msix_entries[i].vector = entry->gsi;
 			adev->host_msix_entries[i].entry = entry->entry;
+			if (!kvm->arch.msix_flags_enabled)
+				break;
+			entry_masked = adev->guest_msix_entries[i].flags &
+				KVM_ASSIGNED_MSIX_MASK;
+			if ((entry->flags & KVM_MSIX_FLAG_MASK) &&
+					!entry_masked) {
+				adev->guest_msix_entries[i].flags |=
+					KVM_ASSIGNED_MSIX_MASK;
+				update_msix_mask(adev, i);
+			} else if (!(entry->flags & KVM_MSIX_FLAG_MASK) &&
+					entry_masked) {
+				adev->guest_msix_entries[i].flags &=
+					~KVM_ASSIGNED_MSIX_MASK;
+				update_msix_mask(adev, i);
+			}
 			break;
 		}
 	if (i == adev->entries_nr) {
Signature · 1 line
1.7.0.1
#13 lore raw

Re: [PATCH 6/8] KVM: assigned dev: Preparation for mask support in userspace

Quoted 9 lines · Sheng Yang

The feature wouldn't be enabled until later patch set msix_flags_enabled. It would be enabled along with mask support in kernel.

arch/x86/include/asm/kvm_host.h +0 -0
index e209078..2bb69ba 100644

arch/x86/include/asm/kvm_host.h struct kvm_arch { · line 461

Quoted 6 lines
arch/x86/include/asm/kvm_host.h +2 -0
@@ -456,6 +456,8 @@ struct kvm_arch {
  	/* fields used by HYPER-V emulation */
  	u64 hv_guest_os_id;
  	u64 hv_hypercall;
+
+	bool msix_flags_enabled;
  };

In theory this should be in generic code, under #ifdef KVM_APIC_ARCHITECTURE. In practice x86 is fine since ia64 isn't actively developed.

Quoted 47 lines
arch/x86/include/asm/kvm_host.h +31 -0
@@ -666,11 +666,35 @@ msix_nr_out:
  	return r;
  }
 
+static void update_msix_mask(struct kvm_assigned_dev_kernel *assigned_dev,
+			     int index)
+{
+	int irq;
+	struct irq_desc *desc;
+
+	if (!assigned_dev->dev->msix_enabled ||
+	    !(assigned_dev->irq_requested_type&  KVM_DEV_IRQ_HOST_MSIX))
+		return;
+
+	irq = assigned_dev->host_msix_entries[index].vector;
+	BUG_ON(irq == 0);
+	desc = irq_to_desc(irq);
+	BUG_ON(!desc->msi_desc);
+
+	if (assigned_dev->guest_msix_entries[index].flags&
+			KVM_ASSIGNED_MSIX_MASK) {
+		desc->chip->mask(irq);
+		flush_work(&assigned_dev->interrupt_work);
+	} else
+		desc->chip->unmask(irq);
+}
+
  static int kvm_vm_ioctl_set_msix_entry(struct kvm *kvm,
  				       struct kvm_assigned_msix_entry *entry)
  {
  	int r = 0, i;
  	struct kvm_assigned_dev_kernel *adev;
+	bool entry_masked;
 
  	mutex_lock(&kvm->lock);
 
@@ -688,6 +712,21 @@ static int kvm_vm_ioctl_set_msix_entry(struct kvm *kvm,
  			adev->guest_msix_entries[i].entry = entry->entry;
  			adev->guest_msix_entries[i].vector = entry->gsi;
  			adev->host_msix_entries[i].entry = entry->entry;
+			if (!kvm->arch.msix_flags_enabled)
+				break;
+			entry_masked = adev->guest_msix_entries[i].flags&
+				KVM_ASSIGNED_MSIX_MASK;
+			if ((entry->flags&  KVM_MSIX_FLAG_MASK)&&
+					!entry_masked) {
+				adev->guest_msix_entries[i].flags |=

arch/x86/include/asm/kvm_host.h static int kvm_vm_ioctl_set_msix_entry(struct kvm *kvm, · line 731

Quoted 10 lines
arch/x86/include/asm/kvm_host.h +8 -0
+					KVM_ASSIGNED_MSIX_MASK;
+				update_msix_mask(adev, i);
+			} else if (!(entry->flags&  KVM_MSIX_FLAG_MASK)&&
+					entry_masked) {
+				adev->guest_msix_entries[i].flags&=
+					~KVM_ASSIGNED_MSIX_MASK;
+				update_msix_mask(adev, i);
+			}
  			break;
  		}

I think you can fold these two functions together, will get rid of the extra if and be a little more readable.

Signature · 1 line
error compiling committee.c: too many arguments to function
#14 lore raw

Re: [PATCH 6/8] KVM: assigned dev: Preparation for mask support in userspace

In reply to Sheng Yang
Quoted 77 lines · Sheng Yang

The feature wouldn't be enabled until later patch set msix_flags_enabled. It would be enabled along with mask support in kernel.

---
 arch/x86/include/asm/kvm_host.h |    2 ++
 include/linux/kvm.h             |    6 +++++-
 include/linux/kvm_host.h        |    1 +
 virt/kvm/assigned-dev.c         |   39 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 47 insertions(+), 1 deletions(-)
arch/x86/include/asm/kvm_host.h +2 -0
index e209078..2bb69ba 100644
@@ -456,6 +456,8 @@ struct kvm_arch {
 	/* fields used by HYPER-V emulation */
 	u64 hv_guest_os_id;
 	u64 hv_hypercall;
+
+	bool msix_flags_enabled;
 };
 
 struct kvm_vm_stat {
include/linux/kvm.h +5 -1
index 919ae53..a699ec9 100644
@@ -787,11 +787,15 @@ struct kvm_assigned_msix_nr {
 };
 
 #define KVM_MAX_MSIX_PER_DEV		256
+
+#define KVM_MSIX_FLAG_MASK	1
+
 struct kvm_assigned_msix_entry {
 	__u32 assigned_dev_id;
 	__u32 gsi;
 	__u16 entry; /* The index of entry in the MSI-X table */
-	__u16 padding[3];
+	__u16 flags;
+	__u16 padding[2];
 };
 
 #endif /* __LINUX_KVM_H */
include/linux/kvm_host.h +1 -0
index 30f83cd..81a6284 100644
@@ -438,6 +438,7 @@ struct kvm_irq_ack_notifier {
 };
 
 #define KVM_ASSIGNED_MSIX_PENDING		0x1
+#define KVM_ASSIGNED_MSIX_MASK			0x2
 struct kvm_guest_msix_entry {
 	u32 vector;
 	u16 entry;
virt/kvm/assigned-dev.c +12 -0
index 7c98928..bf96ea7 100644
@@ -666,11 +666,35 @@ msix_nr_out:
 	return r;
 }
 
+static void update_msix_mask(struct kvm_assigned_dev_kernel *assigned_dev,
+			     int index)
+{
+	int irq;
+	struct irq_desc *desc;
+
+	if (!assigned_dev->dev->msix_enabled ||
+	    !(assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX))
+		return;
+
+	irq = assigned_dev->host_msix_entries[index].vector;
+	BUG_ON(irq == 0);

virt/kvm/assigned-dev.c msix_nr_out: · line 690

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+	desc = irq_to_desc(irq);
+	BUG_ON(!desc->msi_desc);
+
+	if (assigned_dev->guest_msix_entries[index].flags &
+			KVM_ASSIGNED_MSIX_MASK) {
+		desc->chip->mask(irq);
+		flush_work(&assigned_dev->interrupt_work);
+	} else
+		desc->chip->unmask(irq);
+}

Bypassing irq handling code like this is wrong (see all the state keeping in kernel/irq/handle.c).

You need a guarantee that MSIX per-vector mask is used for disable_irq/enable_irq, right? I can't see how this provides it.

author
#15 lore raw

Re: [PATCH 6/8] KVM: assigned dev: Preparation for mask support in userspace

Quoted 103 lines · On Friday 22 October 2010 22:53:13 Marcelo Tosatti wrote:
Quoted 102 lines · Sheng Yang

The feature wouldn't be enabled until later patch set msix_flags_enabled. It would be enabled along with mask support in kernel.

---

 arch/x86/include/asm/kvm_host.h |    2 ++
 include/linux/kvm.h             |    6 +++++-
 include/linux/kvm_host.h        |    1 +
 virt/kvm/assigned-dev.c         |   39

+++++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletions(-)

(quoted diff) +0 -0

b/arch/x86/include/asm/kvm_host.h index e209078..2bb69ba 100644

arch/x86/include/asm/kvm_host.h +2 -0
@@ -456,6 +456,8 @@ struct kvm_arch {
 
 	/* fields used by HYPER-V emulation */
 	u64 hv_guest_os_id;
 	u64 hv_hypercall;
 
+
+	bool msix_flags_enabled;
 

};

struct kvm_vm_stat {

include/linux/kvm.h +5 -1
index 919ae53..a699ec9 100644
@@ -787,11 +787,15 @@ struct kvm_assigned_msix_nr {
 
 };
 
 #define KVM_MAX_MSIX_PER_DEV		256
 
+
+#define KVM_MSIX_FLAG_MASK	1
+
 
 struct kvm_assigned_msix_entry {
 
 	__u32 assigned_dev_id;
 	__u32 gsi;
 	__u16 entry; /* The index of entry in the MSI-X table */
 
-	__u16 padding[3];
+	__u16 flags;
+	__u16 padding[2];
 
 };
 
 #endif /* __LINUX_KVM_H */
include/linux/kvm_host.h +1 -0
index 30f83cd..81a6284 100644
@@ -438,6 +438,7 @@ struct kvm_irq_ack_notifier {
 
 };
 
 #define KVM_ASSIGNED_MSIX_PENDING		0x1
 
+#define KVM_ASSIGNED_MSIX_MASK			0x2
 

struct kvm_guest_msix_entry {

include/linux/kvm_host.h +0 -0
 	u32 vector;
 	u16 entry;
virt/kvm/assigned-dev.c +16 -0
index 7c98928..bf96ea7 100644
@@ -666,11 +666,35 @@ msix_nr_out:
 	return r;
 
 }
 
+static void update_msix_mask(struct kvm_assigned_dev_kernel
 *assigned_dev, +			     int index)
+{
+	int irq;
+	struct irq_desc *desc;
+
+	if (!assigned_dev->dev->msix_enabled ||
+	    !(assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX))
+		return;
+
+	irq = assigned_dev->host_msix_entries[index].vector;
+	BUG_ON(irq == 0);
+	desc = irq_to_desc(irq);
+	BUG_ON(!desc->msi_desc);
+
+	if (assigned_dev->guest_msix_entries[index].flags &
+			KVM_ASSIGNED_MSIX_MASK) {
Quoted 8 lines

virt/kvm/assigned-dev.c msix_nr_out: · line 690

Quoted 5 lines
virt/kvm/assigned-dev.c +5 -0
+		desc->chip->mask(irq);
+		flush_work(&assigned_dev->interrupt_work);
+	} else
+		desc->chip->unmask(irq);
+}

Bypassing irq handling code like this is wrong (see all the state keeping in kernel/irq/handle.c).

Would check it.

Quoted 3 lines

You need a guarantee that MSIX per-vector mask is used for disable_irq/enable_irq, right? I can't see how this provides it.

This one meant to directly operate the mask/unmask bit of the MSI-X table, to emulate the mask/unmask behavior that guest want. In the previous patch I used enable_irq()/disable_irq(), but they won't directly operate the MSI-X table unless it's necessary, and Michael want to read the table in userspace then he prefer using mask/unmask directly.

--
regards
Yang, Sheng

#16 lore raw

Re: [PATCH 6/8] KVM: assigned dev: Preparation for mask support in userspace

Quoted 9 lines · Sheng Yang
Quoted 3 lines

You need a guarantee that MSIX per-vector mask is used for disable_irq/enable_irq, right? I can't see how this provides it.

This one meant to directly operate the mask/unmask bit of the MSI-X table, to emulate the mask/unmask behavior that guest want. In the previous patch I used enable_irq()/disable_irq(), but they won't directly operate the MSI-X table unless it's necessary, and Michael want to read the table in userspace then he prefer using mask/unmask directly.

As I said, the main problem was really that the implementation proposed only works for interrupts used by assigned devices. I would like for it to work for irqfd as well.

Signature · 1 line
MST
author
#17 lore raw

Re: [PATCH 6/8] KVM: assigned dev: Preparation for mask support in userspace

Quoted 11 lines · On Sunday 24 October 2010 20:23:20 Michael S. Tsirkin wrote:
Quoted 9 lines · Sheng Yang
Quoted 2 lines

You need a guarantee that MSIX per-vector mask is used for disable_irq/enable_irq, right? I can't see how this provides it.

This one meant to directly operate the mask/unmask bit of the MSI-X table, to emulate the mask/unmask behavior that guest want. In the previous patch I used enable_irq()/disable_irq(), but they won't directly operate the MSI-X table unless it's necessary, and Michael want to read the table in userspace then he prefer using mask/unmask directly.

Quoted 3 lines

As I said, the main problem was really that the implementation proposed only works for interrupts used by assigned devices. I would like for it to work for irqfd as well.

I think we can't let QEmu access mask or pending bit directly. It must communicate with kernel to get the info if kernel owned mask.

That's because in fact guest supposed mask/unmask operation has nothing todo with what host would do. Maybe we can emulate it by do the same thing on the device, but it's two layer in fact. Also we know host kernel would does disabling/enabling according to its own mechanism, e.g. it may disable interrupt temporarily if there are too many interrupts. What host does should be transparent to guest. Directly accessing the data from device should be prohibited.

And pending bit case is the same. In fact kernel knows which IRQ is pending, we can check IRQ_PENDING bit of desc, though we don't have such interface now. But we can do it in the future if it's necessary.

I'm purposing an new interface like "kvm_get_msix_entry", to return the mask bit of specific entry. The pending bit support can be added in the future if it's needed. But we can't directly access the MSI-X table/PBA in theory.

--
regards
Yang, Sheng

author
#18 lore raw

PATCH v2 7/8 KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

In reply to Sheng Yang

It would be work with KVM_CAP_DEVICE_MSIX_MASK, which we would enable in the last patch.

---
 include/linux/kvm.h      |    7 +++
 include/linux/kvm_host.h |    2 +
 virt/kvm/assigned-dev.c  |  131 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+), 0 deletions(-)
include/linux/kvm.h +7 -0
index a699ec9..0a7bd34 100644
@@ -798,4 +798,11 @@ struct kvm_assigned_msix_entry {
 	__u16 padding[2];
 };
 
+struct kvm_assigned_msix_mmio {
+	__u32 assigned_dev_id;
+	__u64 base_addr;
+	__u32 flags;
+	__u32 reserved[2];
+};
+
 #endif /* __LINUX_KVM_H */
include/linux/kvm_host.h +2 -0
index 81a6284..b67082f 100644
@@ -465,6 +465,8 @@ struct kvm_assigned_dev_kernel {
 	struct pci_dev *dev;
 	struct kvm *kvm;
 	spinlock_t assigned_dev_lock;
+	u64 msix_mmio_base;
+	struct kvm_io_device msix_mmio_dev;
 };
 
 struct kvm_irq_mask_notifier {
virt/kvm/assigned-dev.c +131 -0

137 lines not on this page · raw message

Signature · 1 line
1.7.0.1
#19 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

include/linux/kvm.h struct kvm_assigned_msix_entry { · line 803

Quoted 7 lines · Sheng Yang

It would be work with KVM_CAP_DEVICE_MSIX_MASK, which we would enable in the last patch.

include/linux/kvm.h +3 -0
+struct kvm_assigned_msix_mmio {
+	__u32 assigned_dev_id;
+	__u64 base_addr;

Different alignment and size on 32 and 64 bits.

Is base_addr a guest physical address? Do we need a size or it it fixed?

include/linux/kvm.h struct kvm_assigned_dev_kernel { · line 468

Quoted 10 lines
include/linux/kvm.h +5 -0
+	__u32 flags;
+	__u32 reserved[2];
+};
+
@@ -465,6 +465,8 @@ struct kvm_assigned_dev_kernel {
  	struct pci_dev *dev;
  	struct kvm *kvm;
  	spinlock_t assigned_dev_lock;
+	u64 msix_mmio_base;

gpa_t.

Quoted 10 lines
include/linux/kvm_host.h +1 -0
+	struct kvm_io_device msix_mmio_dev;
  };
 
  struct kvm_irq_mask_notifier {
virt/kvm/assigned-dev.c +0 -0
index bf96ea7..5d2adc4 100644
@@ -739,6 +739,137 @@ msix_entry_out:
 

virt/kvm/assigned-dev.c msix_entry_out: · line 749

Quoted 10 lines
virt/kvm/assigned-dev.c +8 -0
  	return r;
  }
+
+static bool msix_mmio_in_range(struct kvm_assigned_dev_kernel *adev,
+			      gpa_t addr, int len, int *idx)
+{
+	int i;
+
+	if (!(adev->irq_requested_type&  KVM_DEV_IRQ_HOST_MSIX))
+		return false;

Just don't install the io_device in that case.

virt/kvm/assigned-dev.c msix_entry_out: · line 759

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+	BUG_ON(adev->msix_mmio_base == 0);
+	for (i = 0; i<  adev->entries_nr; i++) {
+		u64 start, end;
+		start = adev->msix_mmio_base +
+			adev->guest_msix_entries[i].entry * PCI_MSIX_ENTRY_SIZE;
+		end = start + PCI_MSIX_ENTRY_SIZE;
+		if (addr>= start&&  addr + len<= end) {
+			*idx = i;
+			return true;
+		}

What if it's a partial hit? write part of an entry and part of another entry?

Quoted 13 lines
virt/kvm/assigned-dev.c +13 -0
+	}
+	return false;
+}
+
+static int msix_mmio_read(struct kvm_io_device *this, gpa_t addr, int len,
+			  void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	u32 entry[4];
+	struct kvm_kernel_irq_routing_entry *e;

virt/kvm/assigned-dev.c msix_entry_out: · line 782

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len,&idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr&  0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned reading for device MSI-X MMIO! "
+			"addr 0x%llx, len %d\n", addr, len);

Guest exploitable printk()

virt/kvm/assigned-dev.c msix_entry_out: · line 783

Quoted 1 line
virt/kvm/assigned-dev.c +1 -0
+		r = -EOPNOTSUPP;

If the guest assigned the device to another guest, it allows the nested guest to kill the non-nested guest. Need to exit in a graceful fashion.

Quoted 46 lines
virt/kvm/assigned-dev.c +46 -0
+		goto out;
+	}
+
+	e = kvm_get_irq_routing_entry(adev->kvm,
+			adev->guest_msix_entries[idx].vector);
+	if (!e || e->type != KVM_IRQ_ROUTING_MSI) {
+		printk(KERN_WARNING "KVM: Wrong MSI-X routing entry! "
+			"addr 0x%llx, len %d\n", addr, len);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	entry[0] = e->msi.address_lo;
+	entry[1] = e->msi.address_hi;
+	entry[2] = e->msi.data;
+	entry[3] = !!(adev->guest_msix_entries[idx].flags&
+			KVM_ASSIGNED_MSIX_MASK);
+	memcpy(val,&entry[addr % PCI_MSIX_ENTRY_SIZE / 4], len);
+
+out:
+	mutex_unlock(&adev->kvm->lock);
+	return r;
+}
+
+static int msix_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
+			   const void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	unsigned long new_val = *(unsigned long *)val;
+	bool entry_masked;
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len,&idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr&  0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;
+	}

virt/kvm/assigned-dev.c msix_entry_out: · line 839

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+	entry_masked = adev->guest_msix_entries[idx].flags&
+			KVM_ASSIGNED_MSIX_MASK;
+	if (addr % PCI_MSIX_ENTRY_SIZE != PCI_MSIX_ENTRY_VECTOR_CTRL) {
+		/* Only allow entry modification when entry was masked */
+		if (!entry_masked) {
+			printk(KERN_WARNING
+				"KVM: guest try to write unmasked MSI-X entry. "
+				"addr 0x%llx, len %d, val 0x%lx\n",
+				addr, len, new_val);
+			r = 0;

What does the spec says about this situation?

virt/kvm/assigned-dev.c msix_entry_out: · line 841

Quoted 2 lines
virt/kvm/assigned-dev.c +2 -0
+		} else
+			/* Leave it to QEmu */

s/qemu/userspace/

virt/kvm/assigned-dev.c msix_entry_out: · line 842

Quoted 1 line
virt/kvm/assigned-dev.c +1 -0
+			r = -EOPNOTSUPP;

What would userspace do in this situation? I hope you documented precisely what the kernel handles and what it doesn't?

I prefer more kernel code in the kernel to having an interface which is hard to use correctly.

virt/kvm/assigned-dev.c msix_entry_out:

Quoted 3 lines
virt/kvm/assigned-dev.c +3 -0
+		goto out;
+	}
+	if (new_val&  ~1ul) {

Is there a #define for this bit?

Quoted 6 lines
virt/kvm/assigned-dev.c +6 -0
+		printk(KERN_WARNING
+			"KVM: Bad writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;

virt/kvm/assigned-dev.c msix_entry_out: · line 861

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+	}
+	if (new_val == 1&&  !entry_masked) {
+		adev->guest_msix_entries[idx].flags |=
+			KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	} else if (new_val == 0&&  entry_masked) {
+		adev->guest_msix_entries[idx].flags&=
+			~KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	}

Ah, I see you do reuse update_msix_mask().

Quoted 14 lines
virt/kvm/assigned-dev.c +11 -0
+out:
+	mutex_unlock(&adev->kvm->lock);
+
+	return r;
+}
+
+static const struct kvm_io_device_ops msix_mmio_ops = {
+	.read     = msix_mmio_read,
+	.write    = msix_mmio_write,
+};
+
  #endif
 
  long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
Signature · 1 line
error compiling committee.c: too many arguments to function
#20 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

Quoted 9 lines · Avi Kivity

virt/kvm/assigned-dev.c msix_entry_out: · line 839

Quoted 7 lines
virt/kvm/assigned-dev.c +7 -0
+		/* Only allow entry modification when entry was masked */
+		if (!entry_masked) {
+			printk(KERN_WARNING
+				"KVM: guest try to write unmasked MSI-X entry. "
+				"addr 0x%llx, len %d, val 0x%lx\n",
+				addr, len, new_val);
+			r = 0;

What does the spec says about this situation?

That it's not allowed. However, existing userspace changes entries that kvm thinks are unmasked, because it does not know how to tell kvm about mask bits, so we need to keep supporting this.

Signature · 1 line
MST
author
#21 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

In reply to Avi Kivity
Quoted 5 lines · On Wednesday 20 October 2010 17:46:47 Avi Kivity wrote:
Quoted 4 lines · Sheng Yang

It would be work with KVM_CAP_DEVICE_MSIX_MASK, which we would enable in the last patch.

Quoted 7 lines

include/linux/kvm.h struct kvm_assigned_msix_entry { · line 803

Quoted 3 lines
include/linux/kvm.h +3 -0
+struct kvm_assigned_msix_mmio {
+	__u32 assigned_dev_id;
+	__u64 base_addr;

Different alignment and size on 32 and 64 bits.

Is base_addr a guest physical address? Do we need a size or it it fixed?

Yes it is. The size would be implicit showed when guest using KVM_ASSIGN_SET_MSIX_ENTRY(say, entry number).

Quoted 32 lines

include/linux/kvm.h struct kvm_assigned_dev_kernel { · line 470

Quoted 13 lines
include/linux/kvm.h +5 -0
+	__u32 flags;
+	__u32 reserved[2];
+};
+
@@ -465,6 +465,8 @@ struct kvm_assigned_dev_kernel {
 
  	struct pci_dev *dev;
  	struct kvm *kvm;
  	spinlock_t assigned_dev_lock;
 
+	u64 msix_mmio_base;

gpa_t.

Quoted 16 lines
include/linux/kvm_host.h +1 -0
+	struct kvm_io_device msix_mmio_dev;
 
  };
  
  struct kvm_irq_mask_notifier {
virt/kvm/assigned-dev.c +0 -0
index bf96ea7..5d2adc4 100644
@@ -739,6 +739,137 @@ msix_entry_out:
  	return r;
 
  }
 
Quoted 10 lines

virt/kvm/assigned-dev.c msix_entry_out: · line 749

Quoted 8 lines
virt/kvm/assigned-dev.c +8 -0
+
+static bool msix_mmio_in_range(struct kvm_assigned_dev_kernel *adev,
+			      gpa_t addr, int len, int *idx)
+{
+	int i;
+
+	if (!(adev->irq_requested_type&  KVM_DEV_IRQ_HOST_MSIX))
+		return false;

Just don't install the io_device in that case.

Yeah, I meant to remove this line, because entries_nr = 0 in the case.

Quoted 14 lines

virt/kvm/assigned-dev.c msix_entry_out: · line 759

Quoted 11 lines
virt/kvm/assigned-dev.c +10 -0
+	BUG_ON(adev->msix_mmio_base == 0);
+	for (i = 0; i<  adev->entries_nr; i++) {
+		u64 start, end;
+		start = adev->msix_mmio_base +
+			adev->guest_msix_entries[i].entry * PCI_MSIX_ENTRY_SIZE;
+		end = start + PCI_MSIX_ENTRY_SIZE;
+		if (addr>= start&&  addr + len<= end) {
+			*idx = i;
+			return true;
+		}

What if it's a partial hit? write part of an entry and part of another entry?

Can't be. The spec said the accessing must be DWORD aligned. And I forced the checking later.

Quoted 27 lines
Quoted 14 lines
virt/kvm/assigned-dev.c +12 -0
+	}
+	return false;
+}
+
+static int msix_mmio_read(struct kvm_io_device *this, gpa_t addr, int
 len, +			  void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	u32 entry[4];
+	struct kvm_kernel_irq_routing_entry *e;

virt/kvm/assigned-dev.c msix_entry_out: · line 782

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len,&idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr&  0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned reading for device MSI-X MMIO! "
+			"addr 0x%llx, len %d\n", addr, len);

Guest exploitable printk()

Quoted 4 lines

virt/kvm/assigned-dev.c msix_entry_out: · line 784

Quoted 1 line
virt/kvm/assigned-dev.c +1 -0
+		r = -EOPNOTSUPP;

If the guest assigned the device to another guest, it allows the nested guest to kill the non-nested guest. Need to exit in a graceful fashion.

Don't understand... It wouldn't result in kill but return to QEmu/userspace.

Quoted 51 lines
Quoted 51 lines
virt/kvm/assigned-dev.c +49 -0
+		goto out;
+	}
+
+	e = kvm_get_irq_routing_entry(adev->kvm,
+			adev->guest_msix_entries[idx].vector);
+	if (!e || e->type != KVM_IRQ_ROUTING_MSI) {
+		printk(KERN_WARNING "KVM: Wrong MSI-X routing entry! "
+			"addr 0x%llx, len %d\n", addr, len);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	entry[0] = e->msi.address_lo;
+	entry[1] = e->msi.address_hi;
+	entry[2] = e->msi.data;
+	entry[3] = !!(adev->guest_msix_entries[idx].flags&
+			KVM_ASSIGNED_MSIX_MASK);
+	memcpy(val,&entry[addr % PCI_MSIX_ENTRY_SIZE / 4], len);
+
+out:
+	mutex_unlock(&adev->kvm->lock);
+	return r;
+}
+
+static int msix_mmio_write(struct kvm_io_device *this, gpa_t addr, int
 len, +			   const void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	unsigned long new_val = *(unsigned long *)val;
+	bool entry_masked;
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len,&idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr&  0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	entry_masked = adev->guest_msix_entries[idx].flags&
+			KVM_ASSIGNED_MSIX_MASK;
+	if (addr % PCI_MSIX_ENTRY_SIZE != PCI_MSIX_ENTRY_VECTOR_CTRL) {
+		/* Only allow entry modification when entry was masked */
Quoted 8 lines

virt/kvm/assigned-dev.c msix_entry_out: · line 839

Quoted 6 lines
virt/kvm/assigned-dev.c +6 -0
+		if (!entry_masked) {
+			printk(KERN_WARNING
+				"KVM: guest try to write unmasked MSI-X entry. "
+				"addr 0x%llx, len %d, val 0x%lx\n",
+				addr, len, new_val);
+			r = 0;

What does the spec says about this situation?

As Michael pointed out. The spec said the result is "undefined" indeed.

Quoted 11 lines

virt/kvm/assigned-dev.c msix_entry_out: · line 841

Quoted 3 lines
virt/kvm/assigned-dev.c +2 -0
+		} else
+			/* Leave it to QEmu */

s/qemu/userspace/

virt/kvm/assigned-dev.c msix_entry_out: · line 843

Quoted 1 line
virt/kvm/assigned-dev.c +1 -0
+			r = -EOPNOTSUPP;

What would userspace do in this situation? I hope you documented precisely what the kernel handles and what it doesn't?

Quoted 8 lines

I prefer more kernel code in the kernel to having an interface which is hard to use correctly.

virt/kvm/assigned-dev.c msix_entry_out:

Quoted 3 lines
virt/kvm/assigned-dev.c +3 -0
+		goto out;
+	}
+	if (new_val&  ~1ul) {

Is there a #define for this bit?

Sorry I didn't find it. mask_msi_irq() also use the number 1... Maybe we can add one.

--
regards
Yang, Sheng

Quoted 35 lines
Quoted 7 lines
virt/kvm/assigned-dev.c +6 -0
+		printk(KERN_WARNING
+			"KVM: Bad writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;

virt/kvm/assigned-dev.c msix_entry_out: · line 861

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+	}
+	if (new_val == 1&&  !entry_masked) {
+		adev->guest_msix_entries[idx].flags |=
+			KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	} else if (new_val == 0&&  entry_masked) {
+		adev->guest_msix_entries[idx].flags&=
+			~KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	}

Ah, I see you do reuse update_msix_mask().

Quoted 15 lines
virt/kvm/assigned-dev.c +11 -0
+out:
+	mutex_unlock(&adev->kvm->lock);
+
+	return r;
+}
+
+static const struct kvm_io_device_ops msix_mmio_ops = {
+	.read     = msix_mmio_read,
+	.write    = msix_mmio_write,
+};
+
 
  #endif
 
  long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
#22 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

Quoted 6 lines · Sheng Yang
Quoted 4 lines
Quoted 1 line

+ r = -EOPNOTSUPP;

If the guest assigned the device to another guest, it allows the nested guest to kill the non-nested guest. Need to exit in a graceful fashion.

Don't understand... It wouldn't result in kill but return to QEmu/userspace.

What would qemu do on EOPNOTSUPP? It has no way of knowing that this was triggered by an unsupported msix access. What can it do?

Best to just ignore the write.

If you're worried about debugging, we can have a trace_kvm_discard() tracepoint that logs the address and a type enum field that explains why an access was discarded.

Quoted 9 lines
Quoted 9 lines
Quoted 8 lines

+ if (addr % PCI_MSIX_ENTRY_SIZE != PCI_MSIX_ENTRY_VECTOR_CTRL) {
+ /* Only allow entry modification when entry was masked */
+ if (!entry_masked) {
+ printk(KERN_WARNING
+ "KVM: guest try to write unmasked MSI-X entry. "
+ "addr 0x%llx, len %d, val 0x%lx\n",
+ addr, len, new_val);
+ r = 0;

Quoted 3 lines
Quoted 1 line

What does the spec says about this situation?

As Michael pointed out. The spec said the result is "undefined" indeed.

Ok. Then we should silently discard the write instead of allowing the guest to flood host dmesg.

Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

+ goto out;
+ }
+ if (new_val& ~1ul) {

Is there a #define for this bit?

Sorry I didn't find it. mask_msi_irq() also use the number 1... Maybe we can add one.

Yes please.

Signature · 1 line
error compiling committee.c: too many arguments to function
#23 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

Quoted 8 lines · Avi Kivity
Quoted 6 lines · Sheng Yang
Quoted 4 lines
Quoted 1 line

+ r = -EOPNOTSUPP;

If the guest assigned the device to another guest, it allows the nested guest to kill the non-nested guest. Need to exit in a graceful fashion.

Don't understand... It wouldn't result in kill but return to QEmu/userspace.

Quoted 8 lines

What would qemu do on EOPNOTSUPP? It has no way of knowing that this was triggered by an unsupported msix access. What can it do?

Best to just ignore the write.

If you're worried about debugging, we can have a trace_kvm_discard() tracepoint that logs the address and a type enum field that explains why an access was discarded.

The issue is that the same page is used for mask and entry programming.

Quoted 31 lines
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines

+ if (addr % PCI_MSIX_ENTRY_SIZE != PCI_MSIX_ENTRY_VECTOR_CTRL) {
+ /* Only allow entry modification when entry was masked */
+ if (!entry_masked) {
+ printk(KERN_WARNING
+ "KVM: guest try to write unmasked MSI-X entry. "
+ "addr 0x%llx, len %d, val 0x%lx\n",
+ addr, len, new_val);
+ r = 0;

Quoted 3 lines
Quoted 1 line

What does the spec says about this situation?

As Michael pointed out. The spec said the result is "undefined" indeed.

Ok. Then we should silently discard the write instead of allowing the guest to flood host dmesg.

Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

+ goto out;
+ }
+ if (new_val& ~1ul) {

Is there a #define for this bit?

Sorry I didn't find it. mask_msi_irq() also use the number 1... Maybe we can add one.

Yes please.

Signature · 1 line
error compiling committee.c: too many arguments to function
#24 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

Quoted 12 lines · Michael S. Tsirkin
Quoted 11 lines · Avi Kivity
Quoted 6 lines · Sheng Yang
Quoted 4 lines
Quoted 1 line
  +		r = -EOPNOTSUPP;

If the guest assigned the device to another guest, it allows the nested guest to kill the non-nested guest. Need to exit in a graceful fashion.

Don't understand... It wouldn't result in kill but return to QEmu/userspace.

What would qemu do on EOPNOTSUPP? It has no way of knowing that this was triggered by an unsupported msix access. What can it do?

Quoted 7 lines
Quoted 5 lines

Best to just ignore the write.

If you're worried about debugging, we can have a trace_kvm_discard() tracepoint that logs the address and a type enum field that explains why an access was discarded.

The issue is that the same page is used for mask and entry programming.

Yeah. For that use the normal mmio exit_reason. I was referring to misaligned writes.

I'm not happy with partial emulation, but I'm not happy either with yet another interface to communicate the decoded MSI BAR writes to userspace. Shall we just reprogram the irq routing table?

Signature · 1 line
error compiling committee.c: too many arguments to function
#25 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

Quoted 15 lines · Avi Kivity
Quoted 14 lines · Michael S. Tsirkin
Quoted 13 lines · Avi Kivity
Quoted 6 lines · Sheng Yang
Quoted 4 lines
Quoted 1 line
  +		r = -EOPNOTSUPP;

If the guest assigned the device to another guest, it allows the nested guest to kill the non-nested guest. Need to exit in a graceful fashion.

Don't understand... It wouldn't result in kill but return to QEmu/userspace.

What would qemu do on EOPNOTSUPP? It has no way of knowing that this was triggered by an unsupported msix access. What can it do?

Best to just ignore the write.

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

If you're worried about debugging, we can have a trace_kvm_discard() tracepoint that logs the address and a type enum field that explains why an access was discarded.

The issue is that the same page is used for mask and entry programming.

Yeah. For that use the normal mmio exit_reason. I was referring to misaligned writes.

Yes, I think we can just drop them if we like. Might be a good idea to stick a trace point there for debugging.

Quoted 3 lines

I'm not happy with partial emulation, but I'm not happy either with yet another interface to communicate the decoded MSI BAR writes to userspace. Shall we just reprogram the irq routing table?

Well we don't need to touch the routing table at all. We have the MSI mask, address and data in kernel. That is enough to interrupt the guest.

For vhost-net, we would need an interface that maps an irqfd to a vector # (not gsi), or alternatively map gsi to a pair device/vector number. This is easy to implement.

For VFIO, we have a problem if we try to work especially without interrupt remapping as we can't just program all entries for all devices. So solution could be some combination of requiring interrupt remapping, looking at addresses and looking at the pending bit.

New vectors are added/removed rarely. So maybe we can get away with
1. interface to read MSIX table from userspace (good for debugging anyway)
2. an eventfd to signal on MSIX table writes

Quoted 3 lines
Signature · 2 lines
-- 
error compiling committee.c: too many arguments to function
#26 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

In reply to Sheng Yang
Quoted 4 lines · Sheng Yang

It would be work with KVM_CAP_DEVICE_MSIX_MASK, which we would enable in the last patch.

Merge this with patch 8 - it does not make sense to add a bunch of users of the field msix_mmio_base but init it in the next patch.

Quoted 11 lines
---
 include/linux/kvm.h      |    7 +++
 include/linux/kvm_host.h |    2 +
 virt/kvm/assigned-dev.c  |  131 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+), 0 deletions(-)
include/linux/kvm.h +0 -0
index a699ec9..0a7bd34 100644

include/linux/kvm.h struct kvm_assigned_msix_entry { · line 802

Quoted 5 lines
include/linux/kvm.h +2 -0
@@ -798,4 +798,11 @@ struct kvm_assigned_msix_entry {
 	__u16 padding[2];
 };
 
+struct kvm_assigned_msix_mmio {
+	__u32 assigned_dev_id;

I think avi commented - there's padding here.

Quoted 36 lines
include/linux/kvm.h +5 -0
+	__u64 base_addr;
+	__u32 flags;
+	__u32 reserved[2];
+};
+
 #endif /* __LINUX_KVM_H */
include/linux/kvm_host.h +2 -0
index 81a6284..b67082f 100644
@@ -465,6 +465,8 @@ struct kvm_assigned_dev_kernel {
 	struct pci_dev *dev;
 	struct kvm *kvm;
 	spinlock_t assigned_dev_lock;
+	u64 msix_mmio_base;
+	struct kvm_io_device msix_mmio_dev;
 };
 
 struct kvm_irq_mask_notifier {
virt/kvm/assigned-dev.c +9 -0
index bf96ea7..5d2adc4 100644
@@ -739,6 +739,137 @@ msix_entry_out:
 
 	return r;
 }
+
+static bool msix_mmio_in_range(struct kvm_assigned_dev_kernel *adev,
+			      gpa_t addr, int len, int *idx)
+{
+	int i;
+
+	if (!(adev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX))
+		return false;
+	BUG_ON(adev->msix_mmio_base == 0);

virt/kvm/assigned-dev.c msix_entry_out: · line 760

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+	for (i = 0; i < adev->entries_nr; i++) {
+		u64 start, end;
+		start = adev->msix_mmio_base +
+			adev->guest_msix_entries[i].entry * PCI_MSIX_ENTRY_SIZE;
+		end = start + PCI_MSIX_ENTRY_SIZE;
+		if (addr >= start && addr + len <= end) {
+			*idx = i;
+			return true;
+		}
+	}

We really should not need guest_msix_entries at all:
if we are emulating MSIX in kernel anyway, let us just emulate it there. Doing half setup from qemu and half from kvm will just create problems.

If you do it all in kernel, you will simply need a single range check to see whether this is mask write.

Quoted 72 lines
virt/kvm/assigned-dev.c +72 -0
+	return false;
+}
+
+static int msix_mmio_read(struct kvm_io_device *this, gpa_t addr, int len,
+			  void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	u32 entry[4];
+	struct kvm_kernel_irq_routing_entry *e;
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len, &idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr & 0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned reading for device MSI-X MMIO! "
+			"addr 0x%llx, len %d\n", addr, len);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+
+	e = kvm_get_irq_routing_entry(adev->kvm,
+			adev->guest_msix_entries[idx].vector);
+	if (!e || e->type != KVM_IRQ_ROUTING_MSI) {
+		printk(KERN_WARNING "KVM: Wrong MSI-X routing entry! "
+			"addr 0x%llx, len %d\n", addr, len);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	entry[0] = e->msi.address_lo;
+	entry[1] = e->msi.address_hi;
+	entry[2] = e->msi.data;
+	entry[3] = !!(adev->guest_msix_entries[idx].flags &
+			KVM_ASSIGNED_MSIX_MASK);
+	memcpy(val, &entry[addr % PCI_MSIX_ENTRY_SIZE / 4], len);
+
+out:
+	mutex_unlock(&adev->kvm->lock);
+	return r;
+}
+
+static int msix_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
+			   const void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	unsigned long new_val = *(unsigned long *)val;
+	bool entry_masked;
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len, &idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr & 0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	entry_masked = adev->guest_msix_entries[idx].flags &
+			KVM_ASSIGNED_MSIX_MASK;
+	if (addr % PCI_MSIX_ENTRY_SIZE != PCI_MSIX_ENTRY_VECTOR_CTRL) {

virt/kvm/assigned-dev.c msix_entry_out: · line 842

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+		/* Only allow entry modification when entry was masked */
+		if (!entry_masked) {
+			printk(KERN_WARNING
+				"KVM: guest try to write unmasked MSI-X entry. "
+				"addr 0x%llx, len %d, val 0x%lx\n",
+				addr, len, new_val);
+			r = 0;
+		} else
+			/* Leave it to QEmu */
+			r = -EOPNOTSUPP;

So half the emulation is here half is there... Let's just put it all in kernel and be done with it?

Quoted 35 lines
virt/kvm/assigned-dev.c +30 -0
+		goto out;
+	}
+	if (new_val & ~1ul) {
+		printk(KERN_WARNING
+			"KVM: Bad writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if (new_val == 1 && !entry_masked) {
+		adev->guest_msix_entries[idx].flags |=
+			KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	} else if (new_val == 0 && entry_masked) {
+		adev->guest_msix_entries[idx].flags &=
+			~KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	}
+out:
+	mutex_unlock(&adev->kvm->lock);
+
+	return r;
+}
+
+static const struct kvm_io_device_ops msix_mmio_ops = {
+	.read     = msix_mmio_read,
+	.write    = msix_mmio_write,
+};
+
 #endif
 
 long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
Signature · 1 line
1.7.0.1
author
#27 lore raw

Re: [PATCH 7/8] KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing

Quoted 8 lines · On Thursday 21 October 2010 06:35:11 Michael S. Tsirkin wrote:
Quoted 4 lines · Sheng Yang

It would be work with KVM_CAP_DEVICE_MSIX_MASK, which we would enable in the last patch.

Merge this with patch 8 - it does not make sense to add a bunch of users of the field msix_mmio_base but init it in the next patch.

I just meant to make the reviewer easier, seems I am fail. :)

Quoted 7 lines
Quoted 7 lines

---

 include/linux/kvm.h      |    7 +++
 include/linux/kvm_host.h |    2 +
 virt/kvm/assigned-dev.c  |  131

++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed,

140

Quoted 77 lines
Quoted 7 lines

insertions(+), 0 deletions(-)

include/linux/kvm.h +0 -0
index a699ec9..0a7bd34 100644

include/linux/kvm.h struct kvm_assigned_msix_entry { · line 802

Quoted 7 lines
include/linux/kvm.h +2 -0
@@ -798,4 +798,11 @@ struct kvm_assigned_msix_entry {
 
 	__u16 padding[2];
 
 };
 
+struct kvm_assigned_msix_mmio {
+	__u32 assigned_dev_id;

I think avi commented - there's padding here.

Quoted 44 lines
include/linux/kvm.h +5 -0
+	__u64 base_addr;
+	__u32 flags;
+	__u32 reserved[2];
+};
+
 
 #endif /* __LINUX_KVM_H */
include/linux/kvm_host.h +2 -0
index 81a6284..b67082f 100644
@@ -465,6 +465,8 @@ struct kvm_assigned_dev_kernel {
 
 	struct pci_dev *dev;
 	struct kvm *kvm;
 	spinlock_t assigned_dev_lock;
 
+	u64 msix_mmio_base;
+	struct kvm_io_device msix_mmio_dev;
 

};

struct kvm_irq_mask_notifier {

virt/kvm/assigned-dev.c +9 -0
index bf96ea7..5d2adc4 100644
@@ -739,6 +739,137 @@ msix_entry_out:
 	return r;
 
 }
 
+
+static bool msix_mmio_in_range(struct kvm_assigned_dev_kernel *adev,
+			      gpa_t addr, int len, int *idx)
+{
+	int i;
+
+	if (!(adev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX))
+		return false;
+	BUG_ON(adev->msix_mmio_base == 0);

virt/kvm/assigned-dev.c msix_entry_out: · line 761

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+	for (i = 0; i < adev->entries_nr; i++) {
+		u64 start, end;
+		start = adev->msix_mmio_base +
+			adev->guest_msix_entries[i].entry * PCI_MSIX_ENTRY_SIZE;
+		end = start + PCI_MSIX_ENTRY_SIZE;
+		if (addr >= start && addr + len <= end) {
+			*idx = i;
+			return true;
+		}
+	}

We really should not need guest_msix_entries at all:
if we are emulating MSIX in kernel anyway, let us just emulate it there. Doing half setup from qemu and half from kvm will just create problems.

Quoted 2 lines

If you do it all in kernel, you will simply need a single range check to see whether this is mask write.

Would explain it in the an separate mail. And please comments as well.

--
regards
Yang, Sheng

Quoted 121 lines
Quoted 73 lines
virt/kvm/assigned-dev.c +70 -0
+	return false;
+}
+
+static int msix_mmio_read(struct kvm_io_device *this, gpa_t addr, int
 len, +			  void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	u32 entry[4];
+	struct kvm_kernel_irq_routing_entry *e;
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len, &idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr & 0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned reading for device MSI-X MMIO! "
+			"addr 0x%llx, len %d\n", addr, len);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+
+	e = kvm_get_irq_routing_entry(adev->kvm,
+			adev->guest_msix_entries[idx].vector);
+	if (!e || e->type != KVM_IRQ_ROUTING_MSI) {
+		printk(KERN_WARNING "KVM: Wrong MSI-X routing entry! "
+			"addr 0x%llx, len %d\n", addr, len);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	entry[0] = e->msi.address_lo;
+	entry[1] = e->msi.address_hi;
+	entry[2] = e->msi.data;
+	entry[3] = !!(adev->guest_msix_entries[idx].flags &
+			KVM_ASSIGNED_MSIX_MASK);
+	memcpy(val, &entry[addr % PCI_MSIX_ENTRY_SIZE / 4], len);
+
+out:
+	mutex_unlock(&adev->kvm->lock);
+	return r;
+}
+
+static int msix_mmio_write(struct kvm_io_device *this, gpa_t addr, int
 len, +			   const void *val)
+{
+	struct kvm_assigned_dev_kernel *adev =
+			container_of(this, struct kvm_assigned_dev_kernel,
+				     msix_mmio_dev);
+	int idx, r = 0;
+	unsigned long new_val = *(unsigned long *)val;
+	bool entry_masked;
+
+	mutex_lock(&adev->kvm->lock);
+	if (!msix_mmio_in_range(adev, addr, len, &idx)) {
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if ((addr & 0x3) || len != 4) {
+		printk(KERN_WARNING
+			"KVM: Unaligned writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	entry_masked = adev->guest_msix_entries[idx].flags &
+			KVM_ASSIGNED_MSIX_MASK;
+	if (addr % PCI_MSIX_ENTRY_SIZE != PCI_MSIX_ENTRY_VECTOR_CTRL) {

virt/kvm/assigned-dev.c msix_entry_out: · line 842

Quoted 10 lines
virt/kvm/assigned-dev.c +10 -0
+		/* Only allow entry modification when entry was masked */
+		if (!entry_masked) {
+			printk(KERN_WARNING
+				"KVM: guest try to write unmasked MSI-X entry. "
+				"addr 0x%llx, len %d, val 0x%lx\n",
+				addr, len, new_val);
+			r = 0;
+		} else
+			/* Leave it to QEmu */
+			r = -EOPNOTSUPP;

So half the emulation is here half is there... Let's just put it all in kernel and be done with it?

Quoted 34 lines
virt/kvm/assigned-dev.c +30 -0
+		goto out;
+	}
+	if (new_val & ~1ul) {
+		printk(KERN_WARNING
+			"KVM: Bad writing for device MSI-X MMIO! "
+			"addr 0x%llx, len %d, val 0x%lx\n",
+			addr, len, new_val);
+		r = -EOPNOTSUPP;
+		goto out;
+	}
+	if (new_val == 1 && !entry_masked) {
+		adev->guest_msix_entries[idx].flags |=
+			KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	} else if (new_val == 0 && entry_masked) {
+		adev->guest_msix_entries[idx].flags &=
+			~KVM_ASSIGNED_MSIX_MASK;
+		update_msix_mask(adev, idx);
+	}
+out:
+	mutex_unlock(&adev->kvm->lock);
+
+	return r;
+}
+
+static const struct kvm_io_device_ops msix_mmio_ops = {
+	.read     = msix_mmio_read,
+	.write    = msix_mmio_write,
+};
+
 
 #endif
 
 long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
author
#28 lore raw

PATCH v2 8/8 KVM: Emulation MSI-X mask bits for assigned devices

In reply to Sheng Yang

This patch enable per-vector mask for assigned devices using MSI-X.

---
 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 95 insertions(+), 1 deletions(-)
Documentation/kvm/api.txt +22 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 If any additional field gets added to this structure later on, a bit for that
 additional piece of information will be set in the flags bitmap.
 
+4.47 KVM_ASSIGN_REG_MSIX_MMIO
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would handle MSI-X
+mask bit in the kernel.
+
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +6 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
+	case KVM_CAP_DEVICE_MSIX_MASK:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
 		return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 	default:
 		r = -EINVAL;
 		break;
include/linux/kvm.h +7 -1
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct kvm_xen_hvm_config)
 #define KVM_SET_CLOCK             _IOW(KVMIO,  0x7b, struct kvm_clock_data)
 #define KVM_GET_CLOCK             _IOR(KVMIO,  0x7c, struct kvm_clock_data)
+#define KVM_ASSIGN_REG_MSIX_MMIO  _IOW(KVMIO,  0x7d, \
+					struct kvm_assigned_msix_mmio)
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct kvm_pit_state2)
 #define KVM_SET_PIT2              _IOW(KVMIO,  0xa0, struct kvm_pit_state2)
@@ -802,7 +808,7 @@ struct kvm_assigned_msix_mmio {
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
-	__u32 reserved[2];
+	__u64 reserved;
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +60 -0

90 lines not on this page · raw message

Signature · 1 line
1.7.0.1
#29 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

Quoted 18 lines · Sheng Yang

This patch enable per-vector mask for assigned devices using MSI-X.

Documentation/kvm/api.txt +11 -0
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
  If any additional field gets added to this structure later on, a bit for that
  additional piece of information will be set in the flags bitmap.
 
+4.47 KVM_ASSIGN_REG_MSIX_MMIO
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;

Documentation/kvm/api.txt of 4 instructions that make up a hypercall. · line 1110

Quoted 10 lines
Documentation/kvm/api.txt +10 -0
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would handle MSI-X
+mask bit in the kernel.

Need to clarify what happens for non-mask bits.

arch/x86/kvm/x86.c static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, · line 2725

Quoted 8 lines
arch/x86/kvm/x86.c +4 -0
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
  		return -EINVAL;
 
  	switch (cap->cap) {
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;

It's strange to enable a per-vm capability with a vcpu ioctl.

Signature · 1 line
error compiling committee.c: too many arguments to function
#30 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

In reply to Sheng Yang
Quoted 114 lines · Sheng Yang

This patch enable per-vector mask for assigned devices using MSI-X.

---
 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 95 insertions(+), 1 deletions(-)
Documentation/kvm/api.txt +22 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 If any additional field gets added to this structure later on, a bit for that
 additional piece of information will be set in the flags bitmap.
 
+4.47 KVM_ASSIGN_REG_MSIX_MMIO
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would handle MSI-X
+mask bit in the kernel.
+
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +6 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
+	case KVM_CAP_DEVICE_MSIX_MASK:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
 		return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 	default:
 		r = -EINVAL;
 		break;
include/linux/kvm.h +7 -1
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct kvm_xen_hvm_config)
 #define KVM_SET_CLOCK             _IOW(KVMIO,  0x7b, struct kvm_clock_data)
 #define KVM_GET_CLOCK             _IOR(KVMIO,  0x7c, struct kvm_clock_data)
+#define KVM_ASSIGN_REG_MSIX_MMIO  _IOW(KVMIO,  0x7d, \
+					struct kvm_assigned_msix_mmio)
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct kvm_pit_state2)
 #define KVM_SET_PIT2              _IOW(KVMIO,  0xa0, struct kvm_pit_state2)
@@ -802,7 +808,7 @@ struct kvm_assigned_msix_mmio {
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
-	__u32 reserved[2];
+	__u64 reserved;
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +2 -0
index 5d2adc4..9573194 100644
@@ -17,6 +17,8 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/irqnr.h>
+
 #include "irq.h"
 
 static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head,

virt/kvm/assigned-dev.c static void deassign_host_irq(struct kvm *kvm, · line 175

Quoted 5 lines
virt/kvm/assigned-dev.c +2 -0
@@ -169,6 +171,14 @@ static void deassign_host_irq(struct kvm *kvm,
 	 */
 	if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
 		int i;
+#ifdef __KVM_HAVE_MSIX
+		if (assigned_dev->msix_mmio_base) {

This special-casing of 0 address is not a great idea IMHO. Let's just use a flag.

Quoted 82 lines
virt/kvm/assigned-dev.c +56 -0
+			mutex_lock(&kvm->slots_lock);
+			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
+					&assigned_dev->msix_mmio_dev);
+			mutex_unlock(&kvm->slots_lock);
+		}
+#endif
 		for (i = 0; i < assigned_dev->entries_nr; i++)
 			disable_irq_nosync(assigned_dev->
 					   host_msix_entries[i].vector);
@@ -318,6 +328,15 @@ static int assigned_device_enable_host_msix(struct kvm *kvm,
 			goto err;
 	}
 
+	if (dev->msix_mmio_base) {
+		mutex_lock(&kvm->slots_lock);
+		r = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
+				&dev->msix_mmio_dev);
+		mutex_unlock(&kvm->slots_lock);
+		if (r)
+			goto err;
+	}
+
 	return 0;
 err:
 	for (i -= 1; i >= 0; i--)
@@ -870,6 +889,31 @@ static const struct kvm_io_device_ops msix_mmio_ops = {
 	.write    = msix_mmio_write,
 };
 
+static int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm,
+				struct kvm_assigned_msix_mmio *msix_mmio)
+{
+	int r = 0;
+	struct kvm_assigned_dev_kernel *adev;
+
+	mutex_lock(&kvm->lock);
+	adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
+				      msix_mmio->assigned_dev_id);
+	if (!adev) {
+		r = -EINVAL;
+		goto out;
+	}
+	if (msix_mmio->base_addr == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+	adev->msix_mmio_base = msix_mmio->base_addr;
+
+	kvm_iodevice_init(&adev->msix_mmio_dev, &msix_mmio_ops);
+out:
+	mutex_unlock(&kvm->lock);
+
+	return r;
+}
 #endif
 
 long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
@@ -982,6 +1026,22 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
 			goto out;
 		break;
 	}
+	case KVM_ASSIGN_REG_MSIX_MMIO: {
+		struct kvm_assigned_msix_mmio msix_mmio;
+
+		r = -EFAULT;
+		if (copy_from_user(&msix_mmio, argp, sizeof(msix_mmio)))
+			goto out;
+
+		r = -EINVAL;
+		if (msix_mmio.flags != 0 || msix_mmio.reserved != 0)
+			goto out;
+
+		r = kvm_vm_ioctl_register_msix_mmio(kvm, &msix_mmio);
+		if (r)
+			goto out;
+		break;
+	}
 #endif
 	}
 out:
Signature · 1 line
1.7.0.1
author
#31 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

In reply to Sheng Yang
Quoted 1 line · On Wednesday 20 October 2010 16:26:32 Sheng Yang wrote:

This patch enable per-vector mask for assigned devices using MSI-X.

The basic idea of kernel and QEmu's responsibilities are:

1. Because QEmu owned the irq routing table, so the change of table should still go to the QEmu, like we did in msix_mmio_write().

2. And the others things can be done in kernel, for performance. Here we covered the reading(converted entry from routing table and mask bit state of enabled MSI-X entries), and writing the mask bit for enabled MSI-X entries. Originally we only has mask bit handled in kernel, but later we found that Linux kernel would read MSI-X mmio just after every writing to mask bit, in order to flush the writing. So we add reading MSI data/addr as well.

3. Disabled entries's mask bit accessing would go to QEmu, because it may result in disable/enable MSI-X. Explained later.

4. Only QEmu has knowledge of PCI configuration space, so it's QEmu to decide enable/disable MSI-X for device.
.
5. There is an distinction between enabled entry and disabled entry of MSI-X table. The entries we had used for pci_enable_msix()(not necessary in sequence number) are already enabled, the others are disabled. When device's MSI-X is enabled and guest want to enable an disabled entry, we would go back to QEmu because this vector didn't exist in the routing table. Also due to pci_enable_msix() in kernel didn't allow us to enable vectors one by one, but all at once. So we have to disable MSI-X first, then enable it with new entries, which contained the new vector guest want to use. This situation is only happen when device is being initialized. After that, kernel can know and handle the mask bit of the enabled entry.

I've also considered handle all MMIO operation in kernel, and changing irq routing in kernel directly. But as long as irq routing is owned by QEmu, I think it's better to leave to it...

Notice the mask/unmask bits must be handled together, either in kernel or in userspace. Because if kernel has handled enabled vector's mask bit directly, it would be unsync with QEmu's records. It doesn't matter when QEmu don't access the related record. And the only place QEmu want to consult it's enabled entries' mask bit state is writing to MSI addr/data. The writing should be discarded if the entry is unmasked. This checking has already been done by kernel in this patchset, so we are fine here.

If we want to access the enabled entries' mask bit in the future, we can directly access device's MMIO. That's the reason why I have followed Michael's advice to use mask/unmask directly.

Hope this would make the patches more clear. I meant to add comments for this changeset, but miss it later.

--
regards
Yang, Sheng

Quoted 200 lines
---
 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60

+++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletions(-)

Documentation/kvm/api.txt +0 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 If any additional field gets added to this structure later on, a bit for

that additional piece of information will be set in the flags bitmap.

+4.47 KVM_ASSIGN_REG_MSIX_MMIO

Documentation/kvm/api.txt +19 -0
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would handle

MSI-X +mask bit in the kernel.

Documentation/kvm/api.txt +1 -0
+
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +6 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
+	case KVM_CAP_DEVICE_MSIX_MASK:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu
 *vcpu, return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 	default:
 		r = -EINVAL;
 		break;
include/linux/kvm.h +4 -0
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct
 kvm_xen_hvm_config) #define KVM_SET_CLOCK             _IOW(KVMIO,  0x7b,
 struct kvm_clock_data) #define KVM_GET_CLOCK             _IOR(KVMIO, 

0x7c, struct kvm_clock_data) +#define KVM_ASSIGN_REG_MSIX_MMIO _IOW(KVMIO, 0x7d, \

include/linux/kvm.h +1 -0
+					struct kvm_assigned_msix_mmio)
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct

kvm_pit_state2) #define KVM_SET_PIT2 _IOW(KVMIO, 0xa0,
struct kvm_pit_state2) @@ -802,7 +808,7 @@ struct kvm_assigned_msix_mmio {

include/linux/kvm.h +1 -1
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
-	__u32 reserved[2];
+	__u64 reserved;
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +2 -0
index 5d2adc4..9573194 100644
@@ -17,6 +17,8 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/irqnr.h>
+
 #include "irq.h"
 
 static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct

list_head *head, @@ -169,6 +171,14 @@ static void deassign_host_irq(struct kvm *kvm, */

virt/kvm/assigned-dev.c +58 -0
 	if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
 		int i;
+#ifdef __KVM_HAVE_MSIX
+		if (assigned_dev->msix_mmio_base) {
+			mutex_lock(&kvm->slots_lock);
+			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
+					&assigned_dev->msix_mmio_dev);
+			mutex_unlock(&kvm->slots_lock);
+		}
+#endif
 		for (i = 0; i < assigned_dev->entries_nr; i++)
 			disable_irq_nosync(assigned_dev->
 					   host_msix_entries[i].vector);
@@ -318,6 +328,15 @@ static int assigned_device_enable_host_msix(struct kvm
 *kvm, goto err;
 	}
 
+	if (dev->msix_mmio_base) {
+		mutex_lock(&kvm->slots_lock);
+		r = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
+				&dev->msix_mmio_dev);
+		mutex_unlock(&kvm->slots_lock);
+		if (r)
+			goto err;
+	}
+
 	return 0;
 err:
 	for (i -= 1; i >= 0; i--)
@@ -870,6 +889,31 @@ static const struct kvm_io_device_ops msix_mmio_ops =
 { .write    = msix_mmio_write,
 };
 
+static int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm,
+				struct kvm_assigned_msix_mmio *msix_mmio)
+{
+	int r = 0;
+	struct kvm_assigned_dev_kernel *adev;
+
+	mutex_lock(&kvm->lock);
+	adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
+				      msix_mmio->assigned_dev_id);
+	if (!adev) {
+		r = -EINVAL;
+		goto out;
+	}
+	if (msix_mmio->base_addr == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+	adev->msix_mmio_base = msix_mmio->base_addr;
+
+	kvm_iodevice_init(&adev->msix_mmio_dev, &msix_mmio_ops);
+out:
+	mutex_unlock(&kvm->lock);
+
+	return r;
+}
 #endif
 
 long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
@@ -982,6 +1026,22 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm,
 unsigned ioctl, goto out;
 		break;
 	}
+	case KVM_ASSIGN_REG_MSIX_MMIO: {
+		struct kvm_assigned_msix_mmio msix_mmio;
+
+		r = -EFAULT;
+		if (copy_from_user(&msix_mmio, argp, sizeof(msix_mmio)))
+			goto out;
+
+		r = -EINVAL;
+		if (msix_mmio.flags != 0 || msix_mmio.reserved != 0)
+			goto out;
+
+		r = kvm_vm_ioctl_register_msix_mmio(kvm, &msix_mmio);
+		if (r)
+			goto out;
+		break;
+	}
 #endif
 	}
 out:
#32 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

Quoted 15 lines · Sheng Yang
Quoted 1 line · On Wednesday 20 October 2010 16:26:32 Sheng Yang wrote:

This patch enable per-vector mask for assigned devices using MSI-X.

The basic idea of kernel and QEmu's responsibilities are:

1. Because QEmu owned the irq routing table, so the change of table should still go to the QEmu, like we did in msix_mmio_write().

2. And the others things can be done in kernel, for performance. Here we covered the reading(converted entry from routing table and mask bit state of enabled MSI-X entries), and writing the mask bit for enabled MSI-X entries. Originally we only has mask bit handled in kernel, but later we found that Linux kernel would read MSI-X mmio just after every writing to mask bit, in order to flush the writing. So we add reading MSI data/addr as well.

Quoted 6 lines

3. Disabled entries's mask bit accessing would go to QEmu, because it may result in disable/enable MSI-X. Explained later.

4. Only QEmu has knowledge of PCI configuration space, so it's QEmu to decide enable/disable MSI-X for device.
.

Config space yes, but it's a simple global yes/no after all.

Quoted 2 lines

5. There is an distinction between enabled entry and disabled entry of MSI-X table.

That's my point. There's no such thing as 'enabled entries' in the spec. There are only masked and unmasked entries.

Current interface deals with gsi numbers so qemu had to work around this. The hack used there is removing gsi for masked vector which has 0 address and data. It works because this is what linux and windows guests happen to do, but it is out of spec: vector/data value for a masked entry have no meaning.

Since you are building a new interface, can design it without constraints...

Quoted 10 lines

The entries we had used for pci_enable_msix()(not necessary in sequence number) are already enabled, the others are disabled. When device's MSI-X is enabled and guest want to enable an disabled entry, we would go back to QEmu because this vector didn't exist in the routing table. Also due to pci_enable_msix() in kernel didn't allow us to enable vectors one by one, but all at once. So we have to disable MSI-X first, then enable it with new entries, which contained the new vector guest want to use. This situation is only happen when device is being initialized. After that, kernel can know and handle the mask bit of the enabled entry.

Quoted 3 lines

I've also considered handle all MMIO operation in kernel, and changing irq routing in kernel directly. But as long as irq routing is owned by QEmu, I think it's better to leave to it...

Yes, this is my suggestion, except we don't need no routing :) To inject MSI you just need address/data pairs.
Look at kvm_set_msi: given address/data you can just inject the interrupt. No need for table lookups.

Quoted 10 lines

Notice the mask/unmask bits must be handled together, either in kernel or in userspace. Because if kernel has handled enabled vector's mask bit directly, it would be unsync with QEmu's records. It doesn't matter when QEmu don't access the related record. And the only place QEmu want to consult it's enabled entries' mask bit state is writing to MSI addr/data. The writing should be discarded if the entry is unmasked. This checking has already been done by kernel in this patchset, so we are fine here.

If we want to access the enabled entries' mask bit in the future, we can directly access device's MMIO.

We really must implement this for correctness, btw. If you do not pass reads to the device, messages intended for the masked entry might still be in flight.

Quoted 209 lines

That's the reason why I have followed Michael's advice to use mask/unmask directly.
Hope this would make the patches more clear. I meant to add comments for this changeset, but miss it later.

--
regards
Yang, Sheng

Quoted 200 lines
---
 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60

+++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletions(-)

Documentation/kvm/api.txt +0 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 If any additional field gets added to this structure later on, a bit for

that additional piece of information will be set in the flags bitmap.

+4.47 KVM_ASSIGN_REG_MSIX_MMIO

Documentation/kvm/api.txt +19 -0
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would handle

MSI-X +mask bit in the kernel.

Documentation/kvm/api.txt +1 -0
+
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +6 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
+	case KVM_CAP_DEVICE_MSIX_MASK:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu
 *vcpu, return -EINVAL;
 
 	switch (cap->cap) {
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 	default:
 		r = -EINVAL;
 		break;
include/linux/kvm.h +4 -0
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct
 kvm_xen_hvm_config) #define KVM_SET_CLOCK             _IOW(KVMIO,  0x7b,
 struct kvm_clock_data) #define KVM_GET_CLOCK             _IOR(KVMIO, 

0x7c, struct kvm_clock_data) +#define KVM_ASSIGN_REG_MSIX_MMIO _IOW(KVMIO, 0x7d, \

include/linux/kvm.h +1 -0
+					struct kvm_assigned_msix_mmio)
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct

kvm_pit_state2) #define KVM_SET_PIT2 _IOW(KVMIO, 0xa0,
struct kvm_pit_state2) @@ -802,7 +808,7 @@ struct kvm_assigned_msix_mmio {

include/linux/kvm.h +1 -1
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
-	__u32 reserved[2];
+	__u64 reserved;
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +2 -0
index 5d2adc4..9573194 100644
@@ -17,6 +17,8 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/irqnr.h>
+
 #include "irq.h"
 
 static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct

list_head *head, @@ -169,6 +171,14 @@ static void deassign_host_irq(struct kvm *kvm, */

virt/kvm/assigned-dev.c +58 -0
 	if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
 		int i;
+#ifdef __KVM_HAVE_MSIX
+		if (assigned_dev->msix_mmio_base) {
+			mutex_lock(&kvm->slots_lock);
+			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
+					&assigned_dev->msix_mmio_dev);
+			mutex_unlock(&kvm->slots_lock);
+		}
+#endif
 		for (i = 0; i < assigned_dev->entries_nr; i++)
 			disable_irq_nosync(assigned_dev->
 					   host_msix_entries[i].vector);
@@ -318,6 +328,15 @@ static int assigned_device_enable_host_msix(struct kvm
 *kvm, goto err;
 	}
 
+	if (dev->msix_mmio_base) {
+		mutex_lock(&kvm->slots_lock);
+		r = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
+				&dev->msix_mmio_dev);
+		mutex_unlock(&kvm->slots_lock);
+		if (r)
+			goto err;
+	}
+
 	return 0;
 err:
 	for (i -= 1; i >= 0; i--)
@@ -870,6 +889,31 @@ static const struct kvm_io_device_ops msix_mmio_ops =
 { .write    = msix_mmio_write,
 };
 
+static int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm,
+				struct kvm_assigned_msix_mmio *msix_mmio)
+{
+	int r = 0;
+	struct kvm_assigned_dev_kernel *adev;
+
+	mutex_lock(&kvm->lock);
+	adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
+				      msix_mmio->assigned_dev_id);
+	if (!adev) {
+		r = -EINVAL;
+		goto out;
+	}
+	if (msix_mmio->base_addr == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+	adev->msix_mmio_base = msix_mmio->base_addr;
+
+	kvm_iodevice_init(&adev->msix_mmio_dev, &msix_mmio_ops);
+out:
+	mutex_unlock(&kvm->lock);
+
+	return r;
+}
 #endif
 
 long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,
@@ -982,6 +1026,22 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm,
 unsigned ioctl, goto out;
 		break;
 	}
+	case KVM_ASSIGN_REG_MSIX_MMIO: {
+		struct kvm_assigned_msix_mmio msix_mmio;
+
+		r = -EFAULT;
+		if (copy_from_user(&msix_mmio, argp, sizeof(msix_mmio)))
+			goto out;
+
+		r = -EINVAL;
+		if (msix_mmio.flags != 0 || msix_mmio.reserved != 0)
+			goto out;
+
+		r = kvm_vm_ioctl_register_msix_mmio(kvm, &msix_mmio);
+		if (r)
+			goto out;
+		break;
+	}
 #endif
 	}
 out:
author
#33 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

Quoted 29 lines · On Thursday 21 October 2010 16:39:07 Michael S. Tsirkin wrote:
Quoted 16 lines · Sheng Yang
Quoted 1 line · On Wednesday 20 October 2010 16:26:32 Sheng Yang wrote:

This patch enable per-vector mask for assigned devices using MSI-X.

The basic idea of kernel and QEmu's responsibilities are:

1. Because QEmu owned the irq routing table, so the change of table should still go to the QEmu, like we did in msix_mmio_write().

2. And the others things can be done in kernel, for performance. Here we covered the reading(converted entry from routing table and mask bit state of enabled MSI-X entries), and writing the mask bit for enabled MSI-X entries. Originally we only has mask bit handled in kernel, but later we found that Linux kernel would read MSI-X mmio just after every writing to mask bit, in order to flush the writing. So we add reading MSI data/addr as well.

Quoted 6 lines

3. Disabled entries's mask bit accessing would go to QEmu, because it may result in disable/enable MSI-X. Explained later.

4. Only QEmu has knowledge of PCI configuration space, so it's QEmu to decide enable/disable MSI-X for device.
.

Config space yes, but it's a simple global yes/no after all.

Quoted 2 lines

5. There is an distinction between enabled entry and disabled entry of MSI-X table.

Quoted 8 lines

That's my point. There's no such thing as 'enabled entries' in the spec. There are only masked and unmasked entries.

Current interface deals with gsi numbers so qemu had to work around this. The hack used there is removing gsi for masked vector which has 0 address and data. It works because this is what linux and windows guests happen to do, but it is out of spec: vector/data value for a masked entry have no meaning.

Well, I just realized something unnatural about the 0 contained data/address entry. So I checked spec again, and found the mask bit should be set after reset. So after fix this, I think unmasked 0 address/data entry shouldn't be there anymore.

Quoted 2 lines

Since you are building a new interface, can design it without constraints...

A constraint is pci_enable_msix(). We have to use it to allocate irq for each entry, as well as program the entry in the real hardware. pci_enable_msix() is only a yes/no choice. We can't add new enabled entries after pci_enable_msix(), and we can only enable/disable/mask/unmask one IRQ through kernel API, not the entry in the MSI-X table. And we still have to allocate new IRQ for new entry. When guest unmask "disabled entry", we have to disable and enable MSI-X again in order to use the new entry. That's why "enabled/disabled entry" concept existed.

So even guest only unmasked one entry, it's a totally different work for KVM underlaying. This logic won't change no matter where the MMIO handling is. And in fact I don't like this kind of tricky things in kernel...

Quoted 10 lines
Quoted 10 lines

The entries we had used for pci_enable_msix()(not necessary in sequence number) are already enabled, the others are disabled. When device's MSI-X is enabled and guest want to enable an disabled entry, we would go back to QEmu because this vector didn't exist in the routing table. Also due to pci_enable_msix() in kernel didn't allow us to enable vectors one by one, but all at once. So we have to disable MSI-X first, then enable it with new entries, which contained the new vector guest want to use. This situation is only happen when device is being initialized. After that, kernel can know and handle the mask bit of the enabled entry.

Quoted 8 lines
Quoted 3 lines

I've also considered handle all MMIO operation in kernel, and changing irq routing in kernel directly. But as long as irq routing is owned by QEmu, I think it's better to leave to it...

Yes, this is my suggestion, except we don't need no routing :) To inject MSI you just need address/data pairs.
Look at kvm_set_msi: given address/data you can just inject the interrupt. No need for table lookups.

You still need to look up data/address pair in the guest MSI-X table. The routing table used here is just an replacement for the table, because we can construct the entry according to the routing table. Two choices, using the routing table, or creating an new MSI-X table.

Still, the key is about who to own the routing/MSI-X table. If kernel own it, it would be straightforward to intercept all the MMIO in the kernel; but if it's QEmu, we still need go back to QEmu for it.

Quoted 9 lines
Quoted 9 lines

Notice the mask/unmask bits must be handled together, either in kernel or in userspace. Because if kernel has handled enabled vector's mask bit directly, it would be unsync with QEmu's records. It doesn't matter when QEmu don't access the related record. And the only place QEmu want to consult it's enabled entries' mask bit state is writing to MSI addr/data. The writing should be discarded if the entry is unmasked. This checking has already been done by kernel in this patchset, so we are fine here.

Quoted 6 lines
Quoted 2 lines

If we want to access the enabled entries' mask bit in the future, we can directly access device's MMIO.

We really must implement this for correctness, btw. If you do not pass reads to the device, messages intended for the masked entry might still be in flight.

Oh, yes, kernel would also mask the device as well. I would take this into consideration.

--
regards
Yang, Sheng

Quoted 13 lines
Quoted 13 lines

That's the reason why I have followed Michael's advice to use mask/unmask directly.
Hope this would make the patches more clear. I meant to add comments for this changeset, but miss it later.

--
regards
Yang, Sheng

Quoted 3 lines

---

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60

+++++++++++++++++++++++++++++++++++++++++++++ 4 files changed,

95

Quoted 239 lines
Quoted 239 lines
Quoted 239 lines

insertions(+), 1 deletions(-)

Documentation/kvm/api.txt +0 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 
 If any additional field gets added to this structure later on, a bit
 for
 

that additional piece of information will be set in the flags bitmap.

+4.47 KVM_ASSIGN_REG_MSIX_MMIO

Documentation/kvm/api.txt +19 -0
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would handle

MSI-X +mask bit in the kernel.

Documentation/kvm/api.txt +1 -0
+
 
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +2 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
 
+	case KVM_CAP_DEVICE_MSIX_MASK:
arch/x86/kvm/x86.c +4 -0
 		r = 1;
 		break;
 
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct
 kvm_vcpu *vcpu, return -EINVAL;
 
 	switch (cap->cap) {
 
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 
 	default:
arch/x86/kvm/x86.c +0 -0
 		r = -EINVAL;
 		break;
include/linux/kvm.h +4 -0
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
 
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 

#ifdef KVM_CAP_IRQ_ROUTING

include/linux/kvm.h +0 -0
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct
 
 kvm_xen_hvm_config) #define KVM_SET_CLOCK             _IOW(KVMIO, 

0x7b, struct kvm_clock_data) #define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data) +#define KVM_ASSIGN_REG_MSIX_MMIO _IOW(KVMIO, 0x7d, \

include/linux/kvm.h +1 -0
+					struct kvm_assigned_msix_mmio)
 
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct

kvm_pit_state2) #define KVM_SET_PIT2 _IOW(KVMIO, 0xa0,
struct kvm_pit_state2) @@ -802,7 +808,7 @@ struct
kvm_assigned_msix_mmio {

include/linux/kvm.h +1 -1
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
 
-	__u32 reserved[2];
+	__u64 reserved;
 
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +2 -0
index 5d2adc4..9573194 100644
@@ -17,6 +17,8 @@
 
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 
+#include <linux/irqnr.h>
+
 

#include "irq.h"

static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct

list_head *head, @@ -169,6 +171,14 @@ static void
deassign_host_irq(struct kvm *kvm, */

virt/kvm/assigned-dev.c +17 -0
 	if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
 
 		int i;
 
+#ifdef __KVM_HAVE_MSIX
+		if (assigned_dev->msix_mmio_base) {
+			mutex_lock(&kvm->slots_lock);
+			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
+					&assigned_dev->msix_mmio_dev);
+			mutex_unlock(&kvm->slots_lock);
+		}
+#endif
 
 		for (i = 0; i < assigned_dev->entries_nr; i++)
 
 			disable_irq_nosync(assigned_dev->
 
 					   host_msix_entries[i].vector);
@@ -318,6 +328,15 @@ static int assigned_device_enable_host_msix(struct
 kvm *kvm, goto err;
 
 	}
 
+	if (dev->msix_mmio_base) {
+		mutex_lock(&kvm->slots_lock);
+		r = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
+				&dev->msix_mmio_dev);
+		mutex_unlock(&kvm->slots_lock);
+		if (r)
+			goto err;
+	}
+
 
 	return 0;

err:

virt/kvm/assigned-dev.c +25 -0
 	for (i -= 1; i >= 0; i--)
@@ -870,6 +889,31 @@ static const struct kvm_io_device_ops
 msix_mmio_ops = { .write    = msix_mmio_write,
 
 };
 
+static int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm,
+				struct kvm_assigned_msix_mmio *msix_mmio)
+{
+	int r = 0;
+	struct kvm_assigned_dev_kernel *adev;
+
+	mutex_lock(&kvm->lock);
+	adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
+				      msix_mmio->assigned_dev_id);
+	if (!adev) {
+		r = -EINVAL;
+		goto out;
+	}
+	if (msix_mmio->base_addr == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+	adev->msix_mmio_base = msix_mmio->base_addr;
+
+	kvm_iodevice_init(&adev->msix_mmio_dev, &msix_mmio_ops);
+out:
+	mutex_unlock(&kvm->lock);
+
+	return r;
+}
 
 #endif

long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,

virt/kvm/assigned-dev.c +16 -0
@@ -982,6 +1026,22 @@ long kvm_vm_ioctl_assigned_device(struct kvm
 *kvm, unsigned ioctl, goto out;
 
 		break;
 
 	}
 
+	case KVM_ASSIGN_REG_MSIX_MMIO: {
+		struct kvm_assigned_msix_mmio msix_mmio;
+
+		r = -EFAULT;
+		if (copy_from_user(&msix_mmio, argp, sizeof(msix_mmio)))
+			goto out;
+
+		r = -EINVAL;
+		if (msix_mmio.flags != 0 || msix_mmio.reserved != 0)
+			goto out;
+
+		r = kvm_vm_ioctl_register_msix_mmio(kvm, &msix_mmio);
+		if (r)
+			goto out;
+		break;
+	}

#endif

}

out:

#34 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

Quoted 39 lines · Sheng Yang
Quoted 29 lines · On Thursday 21 October 2010 16:39:07 Michael S. Tsirkin wrote:
Quoted 16 lines · Sheng Yang
Quoted 1 line · On Wednesday 20 October 2010 16:26:32 Sheng Yang wrote:

This patch enable per-vector mask for assigned devices using MSI-X.

The basic idea of kernel and QEmu's responsibilities are:

1. Because QEmu owned the irq routing table, so the change of table should still go to the QEmu, like we did in msix_mmio_write().

2. And the others things can be done in kernel, for performance. Here we covered the reading(converted entry from routing table and mask bit state of enabled MSI-X entries), and writing the mask bit for enabled MSI-X entries. Originally we only has mask bit handled in kernel, but later we found that Linux kernel would read MSI-X mmio just after every writing to mask bit, in order to flush the writing. So we add reading MSI data/addr as well.

Quoted 6 lines

3. Disabled entries's mask bit accessing would go to QEmu, because it may result in disable/enable MSI-X. Explained later.

4. Only QEmu has knowledge of PCI configuration space, so it's QEmu to decide enable/disable MSI-X for device.
.

Config space yes, but it's a simple global yes/no after all.

Quoted 2 lines

5. There is an distinction between enabled entry and disabled entry of MSI-X table.

Quoted 8 lines

That's my point. There's no such thing as 'enabled entries' in the spec. There are only masked and unmasked entries.

Current interface deals with gsi numbers so qemu had to work around this. The hack used there is removing gsi for masked vector which has 0 address and data. It works because this is what linux and windows guests happen to do, but it is out of spec: vector/data value for a masked entry have no meaning.

Quoted 4 lines

Well, I just realized something unnatural about the 0 contained data/address entry. So I checked spec again, and found the mask bit should be set after reset. So after fix this, I think unmasked 0 address/data entry shouldn't be there anymore.

You are right that this 0 check is completely out of spec. But see below. The issue is the spec does not require you to mask an entry if the device does not use it. And we do not want to waste host vectors. One can imagine a logic where we would detect that an interrupt has not been used in a long while, mask it and give up
a host vector. Then check the pending bit once in a while to see whether device started using it.

Quoted 4 lines
Quoted 2 lines

Since you are building a new interface, can design it without constraints...

A constraint is pci_enable_msix().

It's an internal kernel API. No one prevents us from changing it.

Quoted 3 lines

We have to use it to allocate irq for each
entry, as well as program the entry in the real hardware. pci_enable_msix() is only a yes/no choice. We can't add new enabled entries after pci_enable_msix(),

With the current APIs.

Quoted 8 lines

and we can only enable/disable/mask/unmask one IRQ through kernel API, not the entry in the MSI-X table. And we still have to allocate new IRQ for new entry. When guest unmask "disabled entry", we have to disable and enable MSI-X again in order to use the new entry. That's why "enabled/disabled entry" concept existed.

So even guest only unmasked one entry, it's a totally different work for KVM underlaying. This logic won't change no matter where the MMIO handling is. And in fact I don't like this kind of tricky things in kernel...

A more fundamental problem is that host vectors are a limited resource, we don't want to waste them on entries that will end up unused. One can imagine some kind of logic where we check the pending bit on a masked entry and after a while give up the host vector.

This is what I said: making it spec compliant is harder as it will need core kernel changes. Still, it seems silly to design a kerne/userspace API around an internal API limitation ...

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines

The entries we had used for pci_enable_msix()(not necessary in sequence number) are already enabled, the others are disabled. When device's MSI-X is enabled and guest want to enable an disabled entry, we would go back to QEmu because this vector didn't exist in the routing table. Also due to pci_enable_msix() in kernel didn't allow us to enable vectors one by one, but all at once. So we have to disable MSI-X first, then enable it with new entries, which contained the new vector guest want to use. This situation is only happen when device is being initialized. After that, kernel can know and handle the mask bit of the enabled entry.

Quoted 8 lines
Quoted 3 lines

I've also considered handle all MMIO operation in kernel, and changing irq routing in kernel directly. But as long as irq routing is owned by QEmu, I think it's better to leave to it...

Yes, this is my suggestion, except we don't need no routing :) To inject MSI you just need address/data pairs.
Look at kvm_set_msi: given address/data you can just inject the interrupt. No need for table lookups.

Quoted 8 lines

You still need to look up data/address pair in the guest MSI-X table. The routing table used here is just an replacement for the table, because we can construct the entry according to the routing table. Two choices, using the routing table, or creating an new MSI-X table.

Still, the key is about who to own the routing/MSI-X table. If kernel own it, it would be straightforward to intercept all the MMIO in the kernel; but if it's QEmu, we still need go back to QEmu for it.

Looks cleaner to do it in kernel...

Quoted 282 lines
Quoted 9 lines
Quoted 9 lines

Notice the mask/unmask bits must be handled together, either in kernel or in userspace. Because if kernel has handled enabled vector's mask bit directly, it would be unsync with QEmu's records. It doesn't matter when QEmu don't access the related record. And the only place QEmu want to consult it's enabled entries' mask bit state is writing to MSI addr/data. The writing should be discarded if the entry is unmasked. This checking has already been done by kernel in this patchset, so we are fine here.

Quoted 6 lines
Quoted 2 lines

If we want to access the enabled entries' mask bit in the future, we can directly access device's MMIO.

We really must implement this for correctness, btw. If you do not pass reads to the device, messages intended for the masked entry might still be in flight.

Oh, yes, kernel would also mask the device as well. I would take this into consideration.

--
regards
Yang, Sheng

Quoted 13 lines
Quoted 13 lines

That's the reason why I have followed Michael's advice to use mask/unmask directly.
Hope this would make the patches more clear. I meant to add comments for this changeset, but miss it later.

--
regards
Yang, Sheng

Quoted 3 lines

---

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60

+++++++++++++++++++++++++++++++++++++++++++++ 4 files changed,

95

Quoted 239 lines
Quoted 239 lines
Quoted 239 lines

insertions(+), 1 deletions(-)

Documentation/kvm/api.txt +0 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 
 If any additional field gets added to this structure later on, a bit
 for
 

that additional piece of information will be set in the flags bitmap.

+4.47 KVM_ASSIGN_REG_MSIX_MMIO

Documentation/kvm/api.txt +19 -0
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would handle

MSI-X +mask bit in the kernel.

Documentation/kvm/api.txt +1 -0
+
 
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +2 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
 
+	case KVM_CAP_DEVICE_MSIX_MASK:
arch/x86/kvm/x86.c +4 -0
 		r = 1;
 		break;
 
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct
 kvm_vcpu *vcpu, return -EINVAL;
 
 	switch (cap->cap) {
 
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 
 	default:
arch/x86/kvm/x86.c +0 -0
 		r = -EINVAL;
 		break;
include/linux/kvm.h +4 -0
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
 
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 

#ifdef KVM_CAP_IRQ_ROUTING

include/linux/kvm.h +0 -0
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct
 
 kvm_xen_hvm_config) #define KVM_SET_CLOCK             _IOW(KVMIO, 

0x7b, struct kvm_clock_data) #define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data) +#define KVM_ASSIGN_REG_MSIX_MMIO _IOW(KVMIO, 0x7d, \

include/linux/kvm.h +1 -0
+					struct kvm_assigned_msix_mmio)
 
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct

kvm_pit_state2) #define KVM_SET_PIT2 _IOW(KVMIO, 0xa0,
struct kvm_pit_state2) @@ -802,7 +808,7 @@ struct
kvm_assigned_msix_mmio {

include/linux/kvm.h +1 -1
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
 
-	__u32 reserved[2];
+	__u64 reserved;
 
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +2 -0
index 5d2adc4..9573194 100644
@@ -17,6 +17,8 @@
 
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 
+#include <linux/irqnr.h>
+
 

#include "irq.h"

static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct

list_head *head, @@ -169,6 +171,14 @@ static void
deassign_host_irq(struct kvm *kvm, */

virt/kvm/assigned-dev.c +17 -0
 	if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
 
 		int i;
 
+#ifdef __KVM_HAVE_MSIX
+		if (assigned_dev->msix_mmio_base) {
+			mutex_lock(&kvm->slots_lock);
+			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
+					&assigned_dev->msix_mmio_dev);
+			mutex_unlock(&kvm->slots_lock);
+		}
+#endif
 
 		for (i = 0; i < assigned_dev->entries_nr; i++)
 
 			disable_irq_nosync(assigned_dev->
 
 					   host_msix_entries[i].vector);
@@ -318,6 +328,15 @@ static int assigned_device_enable_host_msix(struct
 kvm *kvm, goto err;
 
 	}
 
+	if (dev->msix_mmio_base) {
+		mutex_lock(&kvm->slots_lock);
+		r = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
+				&dev->msix_mmio_dev);
+		mutex_unlock(&kvm->slots_lock);
+		if (r)
+			goto err;
+	}
+
 
 	return 0;

err:

virt/kvm/assigned-dev.c +25 -0
 	for (i -= 1; i >= 0; i--)
@@ -870,6 +889,31 @@ static const struct kvm_io_device_ops
 msix_mmio_ops = { .write    = msix_mmio_write,
 
 };
 
+static int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm,
+				struct kvm_assigned_msix_mmio *msix_mmio)
+{
+	int r = 0;
+	struct kvm_assigned_dev_kernel *adev;
+
+	mutex_lock(&kvm->lock);
+	adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
+				      msix_mmio->assigned_dev_id);
+	if (!adev) {
+		r = -EINVAL;
+		goto out;
+	}
+	if (msix_mmio->base_addr == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+	adev->msix_mmio_base = msix_mmio->base_addr;
+
+	kvm_iodevice_init(&adev->msix_mmio_dev, &msix_mmio_ops);
+out:
+	mutex_unlock(&kvm->lock);
+
+	return r;
+}
 
 #endif

long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,

virt/kvm/assigned-dev.c +16 -0
@@ -982,6 +1026,22 @@ long kvm_vm_ioctl_assigned_device(struct kvm
 *kvm, unsigned ioctl, goto out;
 
 		break;
 
 	}
 
+	case KVM_ASSIGN_REG_MSIX_MMIO: {
+		struct kvm_assigned_msix_mmio msix_mmio;
+
+		r = -EFAULT;
+		if (copy_from_user(&msix_mmio, argp, sizeof(msix_mmio)))
+			goto out;
+
+		r = -EINVAL;
+		if (msix_mmio.flags != 0 || msix_mmio.reserved != 0)
+			goto out;
+
+		r = kvm_vm_ioctl_register_msix_mmio(kvm, &msix_mmio);
+		if (r)
+			goto out;
+		break;
+	}

#endif

}

out:

author
#35 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

Quoted 45 lines · On Friday 22 October 2010 18:17:05 Michael S. Tsirkin wrote:
Quoted 39 lines · Sheng Yang
Quoted 29 lines · On Thursday 21 October 2010 16:39:07 Michael S. Tsirkin wrote:
Quoted 16 lines · Sheng Yang
Quoted 1 line · On Wednesday 20 October 2010 16:26:32 Sheng Yang wrote:

This patch enable per-vector mask for assigned devices using MSI-X.

The basic idea of kernel and QEmu's responsibilities are:

1. Because QEmu owned the irq routing table, so the change of table should still go to the QEmu, like we did in msix_mmio_write().

2. And the others things can be done in kernel, for performance. Here we covered the reading(converted entry from routing table and mask bit state of enabled MSI-X entries), and writing the mask bit for enabled MSI-X entries. Originally we only has mask bit handled in kernel, but later we found that Linux kernel would read MSI-X mmio just after every writing to mask bit, in order to flush the writing. So we add reading MSI data/addr as well.

Quoted 6 lines

3. Disabled entries's mask bit accessing would go to QEmu, because it may result in disable/enable MSI-X. Explained later.

4. Only QEmu has knowledge of PCI configuration space, so it's QEmu to decide enable/disable MSI-X for device.
.

Config space yes, but it's a simple global yes/no after all.

Quoted 2 lines

5. There is an distinction between enabled entry and disabled entry of MSI-X table.

Quoted 8 lines

That's my point. There's no such thing as 'enabled entries' in the spec. There are only masked and unmasked entries.

Current interface deals with gsi numbers so qemu had to work around this. The hack used there is removing gsi for masked vector which has 0 address and data. It works because this is what linux and windows guests happen to do, but it is out of spec: vector/data value for a masked entry have no meaning.

Quoted 4 lines

Well, I just realized something unnatural about the 0 contained data/address entry. So I checked spec again, and found the mask bit should be set after reset. So after fix this, I think unmasked 0 address/data entry shouldn't be there anymore.

Quoted 7 lines

You are right that this 0 check is completely out of spec. But see below. The issue is the spec does not require you to mask an entry if the device does not use it. And we do not want to waste host vectors. One can imagine a logic where we would detect that an interrupt has not been used in a long while, mask it and give up
a host vector. Then check the pending bit once in a while to see whether device started using it.

I don't think introducing this complex and speculative logic makes sense. I haven't seen any alike scenario yet. What's the issue of current implemenation?

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

Since you are building a new interface, can design it without constraints...

A constraint is pci_enable_msix().

It's an internal kernel API. No one prevents us from changing it.

You can say no one. But I'm afraid if we want to overhaul this kind of core PCI functions, it may be take months to get it checked in upstream - also assume we can persuade them this overhaul is absolutely needed (I hope I'm wrong on this). This may also means we have to find out another user for this kind of change. The key issue is I don't know what we can gain for certain from it. Current disable/enable mechanism still works well. I don't know why we need spend a lot of effort on this just because spec don't say there is "enabled/disabled entries".

Yes it's not that elegant, but we need carefully think if the effort worth it.

Quoted 19 lines
Quoted 5 lines

We have to use it to allocate irq for each
entry, as well as program the entry in the real hardware. pci_enable_msix() is only a yes/no choice. We can't add new enabled entries after pci_enable_msix(),

With the current APIs.

Quoted 10 lines

and we can only enable/disable/mask/unmask one IRQ through kernel API, not the entry in the MSI-X table. And we still have to allocate new IRQ for new entry. When guest unmask "disabled entry", we have to disable and enable MSI-X again in order to use the new entry. That's why "enabled/disabled entry" concept existed.

So even guest only unmasked one entry, it's a totally different work for KVM underlaying. This logic won't change no matter where the MMIO handling is. And in fact I don't like this kind of tricky things in kernel...

Quoted 4 lines

A more fundamental problem is that host vectors are a limited resource, we don't want to waste them on entries that will end up unused. One can imagine some kind of logic where we check the pending bit on a masked entry and after a while give up the host vector.

For the data/address != 0 entries, I haven't seen any of them leave unused in the end. So I don't understand your point here.

Quoted 4 lines

This is what I said: making it spec compliant is harder as it will need core kernel changes. Still, it seems silly to design a kerne/userspace API around an internal API limitation ...

I still don't think we violate the spec here, in which case we fail to comply the spec? If some devices want to send 0 message to 0 address on x86, it's fail to comply the x86 spec.

And I know the current implementation is not elegant due to internal API limitation, but only the word "change" is not enough. Function to enable separate entry is of course good to have, but I still think we don't have enough reason for doing it.

--
regards
Yang, Sheng

Quoted 26 lines
Quoted 26 lines
Quoted 12 lines
Quoted 12 lines

The entries we had used for pci_enable_msix()(not necessary in sequence number) are already enabled, the others are disabled. When device's MSI-X is enabled and guest want to enable an disabled entry, we would go back to QEmu because this vector didn't exist in the routing table. Also due to pci_enable_msix() in kernel didn't allow us to enable vectors one by one, but all at once. So we have to disable MSI-X first, then enable it with new entries, which contained the new vector guest want to use. This situation is only happen when device is being initialized. After that, kernel can know and handle the mask bit of the enabled entry.

Quoted 8 lines
Quoted 3 lines

I've also considered handle all MMIO operation in kernel, and changing irq routing in kernel directly. But as long as irq routing is owned by QEmu, I think it's better to leave to it...

Yes, this is my suggestion, except we don't need no routing :) To inject MSI you just need address/data pairs.
Look at kvm_set_msi: given address/data you can just inject the interrupt. No need for table lookups.

You still need to look up data/address pair in the guest MSI-X table. The routing table used here is just an replacement for the table, because we can construct the entry according to the routing table. Two choices, using the routing table, or creating an new MSI-X table.

Quoted 47 lines
Quoted 3 lines

Still, the key is about who to own the routing/MSI-X table. If kernel own it, it would be straightforward to intercept all the MMIO in the kernel; but if it's QEmu, we still need go back to QEmu for it.

Looks cleaner to do it in kernel...

Quoted 41 lines
Quoted 9 lines
Quoted 9 lines

Notice the mask/unmask bits must be handled together, either in kernel or in userspace. Because if kernel has handled enabled vector's mask bit directly, it would be unsync with QEmu's records. It doesn't matter when QEmu don't access the related record. And the only place QEmu want to consult it's enabled entries' mask bit state is writing to MSI addr/data. The writing should be discarded if the entry is unmasked. This checking has already been done by kernel in this patchset, so we are fine here.

Quoted 6 lines
Quoted 2 lines

If we want to access the enabled entries' mask bit in the future, we can directly access device's MMIO.

We really must implement this for correctness, btw. If you do not pass reads to the device, messages intended for the masked entry might still be in flight.

Oh, yes, kernel would also mask the device as well. I would take this into consideration.

--
regards
Yang, Sheng

Quoted 18 lines
Quoted 18 lines

That's the reason why I have followed Michael's advice to use mask/unmask directly.
Hope this would make the patches more clear. I meant to add comments for this changeset, but miss it later.

--
regards
Yang, Sheng

Quoted 9 lines

---

 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60

+++++++++++++++++++++++++++++++++++++++++++++ 4 files

changed,

Quoted 244 lines
Quoted 244 lines

95

Quoted 241 lines
Quoted 241 lines
Quoted 241 lines

insertions(+), 1 deletions(-)

Documentation/kvm/api.txt +0 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 
 If any additional field gets added to this structure later on, a
 bit for
 

that additional piece of information will be set in the flags bitmap.

+4.47 KVM_ASSIGN_REG_MSIX_MMIO

Documentation/kvm/api.txt +19 -0
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would

handle MSI-X +mask bit in the kernel.

Documentation/kvm/api.txt +1 -0
+
 
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +2 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
 
+	case KVM_CAP_DEVICE_MSIX_MASK:
arch/x86/kvm/x86.c +4 -0
 		r = 1;
 		break;
 
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct
 kvm_vcpu *vcpu, return -EINVAL;
 
 	switch (cap->cap) {
 
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 
 	default:
arch/x86/kvm/x86.c +0 -0
 		r = -EINVAL;
 		break;
include/linux/kvm.h +4 -0
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
 
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 

#ifdef KVM_CAP_IRQ_ROUTING

include/linux/kvm.h +0 -0
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct
 
 kvm_xen_hvm_config) #define KVM_SET_CLOCK             _IOW(KVMIO,

0x7b, struct kvm_clock_data) #define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data) +#define KVM_ASSIGN_REG_MSIX_MMIO _IOW(KVMIO, 0x7d, \

include/linux/kvm.h +1 -0
+					struct kvm_assigned_msix_mmio)
 
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct

kvm_pit_state2) #define KVM_SET_PIT2 _IOW(KVMIO,
0xa0, struct kvm_pit_state2) @@ -802,7 +808,7 @@ struct kvm_assigned_msix_mmio {

include/linux/kvm.h +1 -1
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
 
-	__u32 reserved[2];
+	__u64 reserved;
 
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +2 -0
index 5d2adc4..9573194 100644
@@ -17,6 +17,8 @@
 
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 
+#include <linux/irqnr.h>
+
 

#include "irq.h"

static struct kvm_assigned_dev_kernel
*kvm_find_assigned_dev(struct

list_head *head, @@ -169,6 +171,14 @@ static void
deassign_host_irq(struct kvm *kvm, */

virt/kvm/assigned-dev.c +17 -0
 	if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
 
 		int i;
 
+#ifdef __KVM_HAVE_MSIX
+		if (assigned_dev->msix_mmio_base) {
+			mutex_lock(&kvm->slots_lock);
+			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
+					&assigned_dev->msix_mmio_dev);
+			mutex_unlock(&kvm->slots_lock);
+		}
+#endif
 
 		for (i = 0; i < assigned_dev->entries_nr; i++)
 
 			disable_irq_nosync(assigned_dev->
 
 					   host_msix_entries[i].vector);
@@ -318,6 +328,15 @@ static int
 assigned_device_enable_host_msix(struct kvm *kvm, goto err;
 
 	}
 
+	if (dev->msix_mmio_base) {
+		mutex_lock(&kvm->slots_lock);
+		r = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
+				&dev->msix_mmio_dev);
+		mutex_unlock(&kvm->slots_lock);
+		if (r)
+			goto err;
+	}
+
 
 	return 0;

err:

virt/kvm/assigned-dev.c +25 -0
 	for (i -= 1; i >= 0; i--)
@@ -870,6 +889,31 @@ static const struct kvm_io_device_ops
 msix_mmio_ops = { .write    = msix_mmio_write,
 
 };
 
+static int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm,
+				struct kvm_assigned_msix_mmio *msix_mmio)
+{
+	int r = 0;
+	struct kvm_assigned_dev_kernel *adev;
+
+	mutex_lock(&kvm->lock);
+	adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
+				      msix_mmio->assigned_dev_id);
+	if (!adev) {
+		r = -EINVAL;
+		goto out;
+	}
+	if (msix_mmio->base_addr == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+	adev->msix_mmio_base = msix_mmio->base_addr;
+
+	kvm_iodevice_init(&adev->msix_mmio_dev, &msix_mmio_ops);
+out:
+	mutex_unlock(&kvm->lock);
+
+	return r;
+}
 
 #endif

long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,

virt/kvm/assigned-dev.c +16 -0
@@ -982,6 +1026,22 @@ long kvm_vm_ioctl_assigned_device(struct kvm
 *kvm, unsigned ioctl, goto out;
 
 		break;
 
 	}
 
+	case KVM_ASSIGN_REG_MSIX_MMIO: {
+		struct kvm_assigned_msix_mmio msix_mmio;
+
+		r = -EFAULT;
+		if (copy_from_user(&msix_mmio, argp, sizeof(msix_mmio)))
+			goto out;
+
+		r = -EINVAL;
+		if (msix_mmio.flags != 0 || msix_mmio.reserved != 0)
+			goto out;
+
+		r = kvm_vm_ioctl_register_msix_mmio(kvm, &msix_mmio);
+		if (r)
+			goto out;
+		break;
+	}

#endif

}

out:

#36 lore raw

Re: [PATCH 8/8] KVM: Emulation MSI-X mask bits for assigned devices

Quoted 54 lines · Sheng Yang
Quoted 45 lines · On Friday 22 October 2010 18:17:05 Michael S. Tsirkin wrote:
Quoted 39 lines · Sheng Yang
Quoted 29 lines · On Thursday 21 October 2010 16:39:07 Michael S. Tsirkin wrote:
Quoted 16 lines · Sheng Yang
Quoted 1 line · On Wednesday 20 October 2010 16:26:32 Sheng Yang wrote:

This patch enable per-vector mask for assigned devices using MSI-X.

The basic idea of kernel and QEmu's responsibilities are:

1. Because QEmu owned the irq routing table, so the change of table should still go to the QEmu, like we did in msix_mmio_write().

2. And the others things can be done in kernel, for performance. Here we covered the reading(converted entry from routing table and mask bit state of enabled MSI-X entries), and writing the mask bit for enabled MSI-X entries. Originally we only has mask bit handled in kernel, but later we found that Linux kernel would read MSI-X mmio just after every writing to mask bit, in order to flush the writing. So we add reading MSI data/addr as well.

Quoted 6 lines

3. Disabled entries's mask bit accessing would go to QEmu, because it may result in disable/enable MSI-X. Explained later.

4. Only QEmu has knowledge of PCI configuration space, so it's QEmu to decide enable/disable MSI-X for device.
.

Config space yes, but it's a simple global yes/no after all.

Quoted 2 lines

5. There is an distinction between enabled entry and disabled entry of MSI-X table.

Quoted 8 lines

That's my point. There's no such thing as 'enabled entries' in the spec. There are only masked and unmasked entries.

Current interface deals with gsi numbers so qemu had to work around this. The hack used there is removing gsi for masked vector which has 0 address and data. It works because this is what linux and windows guests happen to do, but it is out of spec: vector/data value for a masked entry have no meaning.

Quoted 4 lines

Well, I just realized something unnatural about the 0 contained data/address entry. So I checked spec again, and found the mask bit should be set after reset. So after fix this, I think unmasked 0 address/data entry shouldn't be there anymore.

Quoted 7 lines

You are right that this 0 check is completely out of spec. But see below. The issue is the spec does not require you to mask an entry if the device does not use it. And we do not want to waste host vectors. One can imagine a logic where we would detect that an interrupt has not been used in a long while, mask it and give up
a host vector. Then check the pending bit once in a while to see whether device started using it.

Quoted 2 lines

I don't think introducing this complex and speculative logic makes sense. I haven't seen any alike scenario yet. What's the issue of current implemenation?

Main issue I think is that current implementation assumes that when MSI-X is enabled, all vectors that are masked or have 0 value will not be used anymore. It says so no where in spec.

Quoted 11 lines
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

Since you are building a new interface, can design it without constraints...

A constraint is pci_enable_msix().

It's an internal kernel API. No one prevents us from changing it.

You can say no one. But I'm afraid if we want to overhaul this kind of core PCI functions, it may be take months to get it checked in upstream - also assume we can persuade them this overhaul is absolutely needed (I hope I'm wrong on this). This may also means we have to find out another user for this kind of change.

No, usually a single user is enough to add internal APIs :) This is because we do not promise backwards compatibility there.

Quoted 3 lines

The
key issue is I don't know what we can gain for certain from it. Current disable/enable mechanism still works well.

Heh, given that things seem to work without mask support at all, I am not sure what does this mean.

Quoted 2 lines

I don't know why we need spend a lot of
effort on this just because spec don't say there is "enabled/disabled entries".

I would like to see an implementation using 100% architectural features. It's enough that we need to worry about device quirks. Relying on guests to only use them in a specific way means each bug we'll have to worry whether guest is doing something we did not expect.

If something is just too hard to implement, a temporary work around would be to still build an interfae to allow this, and then add a trace in kernel making this easy to detect. Then we can fix this fully in the next version without affecting userspace.

Quoted 1 line

Yes it's not that elegant, but we need carefully think if the effort worth it.

What I think we should be careful about is making sure we avoid making kerne/userspace API depend on an internal kernel one. Supporing such API long term when internal one changes will be much more painful.

Quoted 19 lines
Quoted 19 lines
Quoted 5 lines

We have to use it to allocate irq for each
entry, as well as program the entry in the real hardware. pci_enable_msix() is only a yes/no choice. We can't add new enabled entries after pci_enable_msix(),

With the current APIs.

Quoted 10 lines

and we can only enable/disable/mask/unmask one IRQ through kernel API, not the entry in the MSI-X table. And we still have to allocate new IRQ for new entry. When guest unmask "disabled entry", we have to disable and enable MSI-X again in order to use the new entry. That's why "enabled/disabled entry" concept existed.

So even guest only unmasked one entry, it's a totally different work for KVM underlaying. This logic won't change no matter where the MMIO handling is. And in fact I don't like this kind of tricky things in kernel...

Quoted 7 lines
Quoted 4 lines

A more fundamental problem is that host vectors are a limited resource, we don't want to waste them on entries that will end up unused. One can imagine some kind of logic where we check the pending bit on a masked entry and after a while give up the host vector.

For the data/address != 0 entries, I haven't seen any of them leave unused in the end. So I don't understand your point here.

I'll try to give another example.

Imagine a guest driver loaded and using N vectors. It gets unloaded, then another driver is loaded using N-1 vectors. vector N is unused but it is != 0 so we will think it is used and will allocate an entry for it.

Quoted 8 lines
Quoted 4 lines

This is what I said: making it spec compliant is harder as it will need core kernel changes. Still, it seems silly to design a kerne/userspace API around an internal API limitation ...

I still don't think we violate the spec here, in which case we fail to comply the spec? If some devices want to send 0 message to 0 address on x86, it's fail to comply the x86 spec.

Guest can write a non-0 value there and use the vector afterwards. If we allocate vectors upfront we won't be ablke to support this.

In other words, it works but it's just a heuristic. It's nice that it works usually, on x86, but we are just lucky and I suspect it will fail in strange scenarious such as driver change, because it is not architectural in the spec.

Quoted 4 lines

And I know the current implementation is not elegant due to internal API limitation, but only the word "change" is not enough. Function to enable separate entry is of course good to have, but I still think we don't have enough reason for doing it.

Implementation is one thing, kernel/user API is another one. I think we should try to make the second one futureproof
but the implementation can be partial, have some TODO
items and it's okay, and even a good idea to build things step by step.

Quoted 321 lines

--
regards
Yang, Sheng

Quoted 26 lines
Quoted 26 lines
Quoted 12 lines
Quoted 12 lines

The entries we had used for pci_enable_msix()(not necessary in sequence number) are already enabled, the others are disabled. When device's MSI-X is enabled and guest want to enable an disabled entry, we would go back to QEmu because this vector didn't exist in the routing table. Also due to pci_enable_msix() in kernel didn't allow us to enable vectors one by one, but all at once. So we have to disable MSI-X first, then enable it with new entries, which contained the new vector guest want to use. This situation is only happen when device is being initialized. After that, kernel can know and handle the mask bit of the enabled entry.

Quoted 8 lines
Quoted 3 lines

I've also considered handle all MMIO operation in kernel, and changing irq routing in kernel directly. But as long as irq routing is owned by QEmu, I think it's better to leave to it...

Yes, this is my suggestion, except we don't need no routing :) To inject MSI you just need address/data pairs.
Look at kvm_set_msi: given address/data you can just inject the interrupt. No need for table lookups.

You still need to look up data/address pair in the guest MSI-X table. The routing table used here is just an replacement for the table, because we can construct the entry according to the routing table. Two choices, using the routing table, or creating an new MSI-X table.

Quoted 47 lines
Quoted 3 lines

Still, the key is about who to own the routing/MSI-X table. If kernel own it, it would be straightforward to intercept all the MMIO in the kernel; but if it's QEmu, we still need go back to QEmu for it.

Looks cleaner to do it in kernel...

Quoted 41 lines
Quoted 9 lines
Quoted 9 lines

Notice the mask/unmask bits must be handled together, either in kernel or in userspace. Because if kernel has handled enabled vector's mask bit directly, it would be unsync with QEmu's records. It doesn't matter when QEmu don't access the related record. And the only place QEmu want to consult it's enabled entries' mask bit state is writing to MSI addr/data. The writing should be discarded if the entry is unmasked. This checking has already been done by kernel in this patchset, so we are fine here.

Quoted 6 lines
Quoted 2 lines

If we want to access the enabled entries' mask bit in the future, we can directly access device's MMIO.

We really must implement this for correctness, btw. If you do not pass reads to the device, messages intended for the masked entry might still be in flight.

Oh, yes, kernel would also mask the device as well. I would take this into consideration.

--
regards
Yang, Sheng

Quoted 18 lines
Quoted 18 lines

That's the reason why I have followed Michael's advice to use mask/unmask directly.
Hope this would make the patches more clear. I meant to add comments for this changeset, but miss it later.

--
regards
Yang, Sheng

Quoted 9 lines

---

 Documentation/kvm/api.txt |   22 ++++++++++++++++
 arch/x86/kvm/x86.c        |    6 ++++
 include/linux/kvm.h       |    8 +++++-
 virt/kvm/assigned-dev.c   |   60

+++++++++++++++++++++++++++++++++++++++++++++ 4 files

changed,

Quoted 244 lines
Quoted 244 lines

95

Quoted 241 lines
Quoted 241 lines
Quoted 241 lines

insertions(+), 1 deletions(-)

Documentation/kvm/api.txt +0 -0
index d82d637..f324a50 100644
@@ -1087,6 +1087,28 @@ of 4 instructions that make up a hypercall.
 
 If any additional field gets added to this structure later on, a
 bit for
 

that additional piece of information will be set in the flags bitmap.

+4.47 KVM_ASSIGN_REG_MSIX_MMIO

Documentation/kvm/api.txt +19 -0
+
+Capability: KVM_CAP_DEVICE_MSIX_MASK
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_assigned_msix_mmio (in)
+Returns: 0 on success, !0 on error
+
+struct kvm_assigned_msix_mmio {
+	/* Assigned device's ID */
+	__u32 assigned_dev_id;
+	/* MSI-X table MMIO address */
+	__u64 base_addr;
+	/* Must be 0 */
+	__u32 flags;
+	/* Must be 0, reserved for future use */
+	__u64 reserved;
+};
+
+This ioctl would enable in-kernel MSI-X emulation, which would

handle MSI-X +mask bit in the kernel.

Documentation/kvm/api.txt +1 -0
+
 
 5. The kvm_run structure
 
 Application code obtains a pointer to the kvm_run structure by
arch/x86/kvm/x86.c +2 -0
index fc62546..ba07a2f 100644
@@ -1927,6 +1927,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
 	case KVM_CAP_XSAVE:
 
 	case KVM_CAP_ENABLE_CAP:
+	case KVM_CAP_DEVICE_MSIX_EXT:
 
+	case KVM_CAP_DEVICE_MSIX_MASK:
arch/x86/kvm/x86.c +4 -0
 		r = 1;
 		break;
 
 	case KVM_CAP_COALESCED_MMIO:
@@ -2717,6 +2719,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct
 kvm_vcpu *vcpu, return -EINVAL;
 
 	switch (cap->cap) {
 
+	case KVM_CAP_DEVICE_MSIX_EXT:
+		vcpu->kvm->arch.msix_flags_enabled = true;
+		r = 0;
+		break;
 
 	default:
arch/x86/kvm/x86.c +0 -0
 		r = -EINVAL;
 		break;
include/linux/kvm.h +4 -0
index 0a7bd34..1494ed0 100644
@@ -540,6 +540,10 @@ struct kvm_ppc_pvinfo {
 
 #endif
 #define KVM_CAP_PPC_GET_PVINFO 57
 #define KVM_CAP_PPC_IRQ_LEVEL 58
 
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX_EXT 59
+#define KVM_CAP_DEVICE_MSIX_MASK 60
+#endif
 

#ifdef KVM_CAP_IRQ_ROUTING

include/linux/kvm.h +0 -0
@@ -671,6 +675,8 @@ struct kvm_clock_data {
 
 #define KVM_XEN_HVM_CONFIG        _IOW(KVMIO,  0x7a, struct
 
 kvm_xen_hvm_config) #define KVM_SET_CLOCK             _IOW(KVMIO,

0x7b, struct kvm_clock_data) #define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data) +#define KVM_ASSIGN_REG_MSIX_MMIO _IOW(KVMIO, 0x7d, \

include/linux/kvm.h +1 -0
+					struct kvm_assigned_msix_mmio)
 
 /* Available with KVM_CAP_PIT_STATE2 */
 #define KVM_GET_PIT2              _IOR(KVMIO,  0x9f, struct

kvm_pit_state2) #define KVM_SET_PIT2 _IOW(KVMIO,
0xa0, struct kvm_pit_state2) @@ -802,7 +808,7 @@ struct kvm_assigned_msix_mmio {

include/linux/kvm.h +1 -1
 	__u32 assigned_dev_id;
 	__u64 base_addr;
 	__u32 flags;
 
-	__u32 reserved[2];
+	__u64 reserved;
 
 };
 
 #endif /* __LINUX_KVM_H */
virt/kvm/assigned-dev.c +2 -0
index 5d2adc4..9573194 100644
@@ -17,6 +17,8 @@
 
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 
+#include <linux/irqnr.h>
+
 

#include "irq.h"

static struct kvm_assigned_dev_kernel
*kvm_find_assigned_dev(struct

list_head *head, @@ -169,6 +171,14 @@ static void
deassign_host_irq(struct kvm *kvm, */

virt/kvm/assigned-dev.c +17 -0
 	if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
 
 		int i;
 
+#ifdef __KVM_HAVE_MSIX
+		if (assigned_dev->msix_mmio_base) {
+			mutex_lock(&kvm->slots_lock);
+			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
+					&assigned_dev->msix_mmio_dev);
+			mutex_unlock(&kvm->slots_lock);
+		}
+#endif
 
 		for (i = 0; i < assigned_dev->entries_nr; i++)
 
 			disable_irq_nosync(assigned_dev->
 
 					   host_msix_entries[i].vector);
@@ -318,6 +328,15 @@ static int
 assigned_device_enable_host_msix(struct kvm *kvm, goto err;
 
 	}
 
+	if (dev->msix_mmio_base) {
+		mutex_lock(&kvm->slots_lock);
+		r = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS,
+				&dev->msix_mmio_dev);
+		mutex_unlock(&kvm->slots_lock);
+		if (r)
+			goto err;
+	}
+
 
 	return 0;

err:

virt/kvm/assigned-dev.c +25 -0
 	for (i -= 1; i >= 0; i--)
@@ -870,6 +889,31 @@ static const struct kvm_io_device_ops
 msix_mmio_ops = { .write    = msix_mmio_write,
 
 };
 
+static int kvm_vm_ioctl_register_msix_mmio(struct kvm *kvm,
+				struct kvm_assigned_msix_mmio *msix_mmio)
+{
+	int r = 0;
+	struct kvm_assigned_dev_kernel *adev;
+
+	mutex_lock(&kvm->lock);
+	adev = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
+				      msix_mmio->assigned_dev_id);
+	if (!adev) {
+		r = -EINVAL;
+		goto out;
+	}
+	if (msix_mmio->base_addr == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+	adev->msix_mmio_base = msix_mmio->base_addr;
+
+	kvm_iodevice_init(&adev->msix_mmio_dev, &msix_mmio_ops);
+out:
+	mutex_unlock(&kvm->lock);
+
+	return r;
+}
 
 #endif

long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl,

virt/kvm/assigned-dev.c +16 -0
@@ -982,6 +1026,22 @@ long kvm_vm_ioctl_assigned_device(struct kvm
 *kvm, unsigned ioctl, goto out;
 
 		break;
 
 	}
 
+	case KVM_ASSIGN_REG_MSIX_MMIO: {
+		struct kvm_assigned_msix_mmio msix_mmio;
+
+		r = -EFAULT;
+		if (copy_from_user(&msix_mmio, argp, sizeof(msix_mmio)))
+			goto out;
+
+		r = -EINVAL;
+		if (msix_mmio.flags != 0 || msix_mmio.reserved != 0)
+			goto out;
+
+		r = kvm_vm_ioctl_register_msix_mmio(kvm, &msix_mmio);
+		if (r)
+			goto out;
+		break;
+	}

#endif

}

out:

#37 lore raw
In reply to Sheng Yang
Quoted 6 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

Quoted 8 lines

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Alex, Michael, how would you do this with vfio?

Signature · 1 line
error compiling committee.c: too many arguments to function
#38 lore raw
Quoted 10 lines · Avi Kivity
Quoted 9 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

Quoted 8 lines
Quoted 5 lines

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Likely any old linux.

Quoted 1 line

Alex, Michael, how would you do this with vfio?

With current VFIO we would catch mask writes in qemu and call a KVM ioctl. We would also need an ioctl to retrieve pending bits long term.

I think that it is unfortunate that we need to do this in userspace while rest of configuration is done in kernel.
I would be much happier with userspace simply forwarding everything to VFIO, so emulation does not have to
be split. That would be a clean interface: just mmap MSIX BAR and forget about it.

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
#39 lore raw
Quoted 5 lines · Michael S. Tsirkin
Quoted 3 lines

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Likely any old linux.

I meant that people are likely to virtualize and expect high performance from.

What about RHEL 3? Does it support msi? How about RHEL 5 - has it fixed this problem?

Quoted 4 lines
Quoted 1 line

Alex, Michael, how would you do this with vfio?

With current VFIO we would catch mask writes in qemu and call a KVM ioctl.

Doing what? Updating the irq routing to include/exclude the interrupt? Disconnecting the irqfd?

Note you could disconnect the irqfd from either vfio or kvm.

Quoted 2 lines

We would also need an ioctl to retrieve
pending bits long term.

Suppose you disconnect the irqfd. Isn't the value of the eventfd equivalent to the pending bit?

Quoted 6 lines

I think that it is unfortunate that we need to do this in userspace while rest of configuration is done in kernel.
I would be much happier with userspace simply forwarding everything to VFIO, so emulation does not have to
be split. That would be a clean interface: just mmap MSIX BAR and forget about it.

Agree.

Quoted 5 lines

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

But if we emulate the entire msix bar in vfio, that's not needed, right?

How far away is vfio? If it's merged soon, we might avoid making changes to the old assigned device infrastructure and instead update vfio. On the other hand, changes to the old infrastructure are much more amenable to backporting for long term support distro kernels, so we may need to actively develop both for a while.

Signature · 1 line
error compiling committee.c: too many arguments to function
#40 lore raw
Quoted 11 lines · Avi Kivity
Quoted 5 lines · Michael S. Tsirkin
Quoted 3 lines

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Likely any old linux.

I meant that people are likely to virtualize and expect high performance from.

What about RHEL 3? Does it support msi?

Yes. I think it's the same.

Quoted 2 lines

How about RHEL 5 - has it
fixed this problem?

Yes, it has reduced the problem. Instead of masking immediately it records the masked state and masks the first time it gets an interrupt. Most devices mask and unmask immediately so the chance of this happening is small.

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Alex, Michael, how would you do this with vfio?

With current VFIO we would catch mask writes in qemu and call a KVM ioctl.

Doing what? Updating the irq routing to include/exclude the interrupt? Disconnecting the irqfd?

No. I mean call the new mask ioctl.

Quoted 1 line

Note you could disconnect the irqfd from either vfio or kvm.

This is what current code does, implementing mask in userspace. But it is an rcu write side so it is a slow path operation.

Quoted 5 lines
Quoted 2 lines

We would also need an ioctl to retrieve
pending bits long term.

Suppose you disconnect the irqfd. Isn't the value of the eventfd
equivalent to the pending bit?

Yes. This is what current code does.

Quoted 9 lines
Quoted 6 lines

I think that it is unfortunate that we need to do this in userspace while rest of configuration is done in kernel.
I would be much happier with userspace simply forwarding everything to VFIO, so emulation does not have to
be split. That would be a clean interface: just mmap MSIX BAR and forget about it.

Agree.

Quoted 7 lines
Quoted 5 lines

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

But if we emulate the entire msix bar in vfio, that's not needed, right?

Yes, I think it is. How does kvm know which interrupt to inject? Either vfio needs to pass that info to qemu and qemu would pass it to kvm, or vfio would have some way to pass that info to kvm directly.

Quoted 3 lines

How far away is vfio? If it's merged soon, we might avoid making changes to the old assigned device infrastructure and instead update vfio.

Hard to be sure, hopefully 2.6.38 material.

Some issues off the top of my head are
- readonly/virtualized table correctness
hopefully will start converging now that
we are switching to standard registers from pci_regs.h
- work out some capability negotiation mechanism so userspace/kernel can detect bug fixes/missing features and recover or fail gracefully
- with multiple assigned devices in a guest:
I don't think we have figured out how do they share an iommu context
- maybe: reset handling (flr support) - need to look a it

Quoted 3 lines

On the other hand, changes to the old infrastructure are much more amenable to backporting for long term support distro kernels, so we may need to actively develop both for a while.

Right.

Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
#41 lore raw
Quoted 7 lines · Michael S. Tsirkin
Quoted 3 lines · Avi Kivity

How far away is vfio? If it's merged soon, we might avoid making changes to the old assigned device infrastructure and instead update vfio.

Hard to be sure, hopefully 2.6.38 material.

Quoted 8 lines

Some issues off the top of my head are
- readonly/virtualized table correctness
hopefully will start converging now that
we are switching to standard registers from pci_regs.h
- work out some capability negotiation mechanism so userspace/kernel can detect bug fixes/missing features and recover or fail gracefully
- with multiple assigned devices in a guest:
I don't think we have figured out how do they share an iommu context

A single UIOMMU fd can be used for multiple devices. The code I wrote supports this, but it's really only meant to support a uiommufd passed via the command line for libvirt usage. Since libvirt doesn't yet support vfio, it's never been tested.

Quoted 1 line

- maybe: reset handling (flr support) - need to look a it

I'd add that for the qemu vfio driver, we need to work out the KVM interrupt optimizations, otherwise we suffer extra latency vs current code.

Quoted 4 lines
Quoted 4 lines

On the other hand, changes to the old infrastructure are much more amenable to backporting for long term support distro kernels, so we may need to actively develop both for a while.

Yep, I think we'll need to continue development and probably maintain them both for a while.

Alex

#42 lore raw
Quoted 17 lines · Alex Williamson
Quoted 7 lines · Michael S. Tsirkin
Quoted 3 lines · Avi Kivity

How far away is vfio? If it's merged soon, we might avoid making changes to the old assigned device infrastructure and instead update vfio.

Hard to be sure, hopefully 2.6.38 material.

Quoted 8 lines

Some issues off the top of my head are
- readonly/virtualized table correctness
hopefully will start converging now that
we are switching to standard registers from pci_regs.h
- work out some capability negotiation mechanism so userspace/kernel can detect bug fixes/missing features and recover or fail gracefully
- with multiple assigned devices in a guest:
I don't think we have figured out how do they share an iommu context

Quoted 4 lines

A single UIOMMU fd can be used for multiple devices. The code I wrote supports this, but it's really only meant to support a uiommufd passed via the command line for libvirt usage. Since libvirt doesn't yet support vfio, it's never been tested.

I think there was some issue that programming was done through vfio fd and got lost if you try to hot unplug device which programmed it. Maybe I'm wrong ...

Quoted 5 lines
Quoted 1 line

- maybe: reset handling (flr support) - need to look a it

I'd add that for the qemu vfio driver, we need to work out the KVM interrupt optimizations, otherwise we suffer extra latency vs current code.

You see this in some benchmark?

Quoted 9 lines
Quoted 4 lines
Quoted 4 lines

On the other hand, changes to the old infrastructure are much more amenable to backporting for long term support distro kernels, so we may need to actively develop both for a while.

Yep, I think we'll need to continue development and probably maintain them both for a while.

Alex

#43 lore raw
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Alex Williamson
Quoted 15 lines · Michael S. Tsirkin
Quoted 3 lines · Avi Kivity

How far away is vfio? If it's merged soon, we might avoid making changes to the old assigned device infrastructure and instead update vfio.

Hard to be sure, hopefully 2.6.38 material.

Some issues off the top of my head are
- readonly/virtualized table correctness
hopefully will start converging now that
we are switching to standard registers from pci_regs.h
- work out some capability negotiation mechanism so userspace/kernel can detect bug fixes/missing features and recover or fail gracefully
- with multiple assigned devices in a guest:
I don't think we have figured out how do they share an iommu context

Quoted 8 lines
Quoted 4 lines

A single UIOMMU fd can be used for multiple devices. The code I wrote supports this, but it's really only meant to support a uiommufd passed via the command line for libvirt usage. Since libvirt doesn't yet support vfio, it's never been tested.

I think there was some issue that programming was done through vfio fd and got lost if you try to hot unplug device which programmed it. Maybe I'm wrong ...

Hasn't been tested, so bugs are probably. Sounds fixable though. Sharing an IOMMU context is at least part of the design.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

- maybe: reset handling (flr support) - need to look a it

I'd add that for the qemu vfio driver, we need to work out the KVM interrupt optimizations, otherwise we suffer extra latency vs current code.

You see this in some benchmark?

I haven't done any formal benchmarks on VFIO, but I recall that while both can achieve line rate on a 1G link, VFIO currently requires more CPU to do so. Bouncing interrupts through QEMU seems like an obvious candidate for that. Thanks,

Alex

#44 lore raw
In reply to Michael S. Tsirkin
Quoted 6 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 5 lines

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

Quoted 6 lines
Quoted 1 line

But if we emulate the entire msix bar in vfio, that's not needed, right?

Yes, I think it is. How does kvm know which interrupt to inject? Either vfio needs to pass that info to qemu and qemu would pass it to kvm, or vfio would have some way to pass that info to kvm directly.

Wait. We can't emulate the BAR in vfio, we have to emulate it in kvm where we emulate the write instruction. We then need to tell vfio, perhaps via userspace, that masking state has changed.

Seems very intrusive.

We can perhaps use the new semaphore capability in eventfd to pass mask/unmask information from kvm to userspace. We'd usually hook it to the corresponding vfio facility.

Quoted 5 lines
Quoted 3 lines

How far away is vfio? If it's merged soon, we might avoid making changes to the old assigned device infrastructure and instead update vfio.

Hard to be sure, hopefully 2.6.38 material.

My question was how to implement this with vfio. But your list is interesting as well.

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#45 lore raw
Quoted 14 lines · Avi Kivity
Quoted 6 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 5 lines

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

Quoted 6 lines
Quoted 1 line

But if we emulate the entire msix bar in vfio, that's not needed, right?

Yes, I think it is. How does kvm know which interrupt to inject? Either vfio needs to pass that info to qemu and qemu would pass it to kvm, or vfio would have some way to pass that info to kvm directly.

Quoted 5 lines

Wait. We can't emulate the BAR in vfio, we have to emulate it in kvm where we emulate the write instruction. We then need to tell vfio, perhaps via userspace, that masking state has changed.

Seems very intrusive.

We wouldn't direct map the vector table or pending bits, so we could trap and emulate in qemu, which could then call into reads/writes in vfio.

Alex

Quoted 14 lines

We can perhaps use the new semaphore capability in eventfd to pass mask/unmask information from kvm to userspace. We'd usually hook it to the corresponding vfio facility.

Quoted 5 lines
Quoted 3 lines

How far away is vfio? If it's merged soon, we might avoid making changes to the old assigned device infrastructure and instead update vfio.

Hard to be sure, hopefully 2.6.38 material.

My question was how to implement this with vfio. But your list is interesting as well.

#46 lore raw
Quoted 19 lines · Alex Williamson
Quoted 18 lines · Avi Kivity
Quoted 6 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 5 lines

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

Quoted 6 lines
Quoted 1 line

But if we emulate the entire msix bar in vfio, that's not needed, right?

Yes, I think it is. How does kvm know which interrupt to inject? Either vfio needs to pass that info to qemu and qemu would pass it to kvm, or vfio would have some way to pass that info to kvm directly.

Wait. We can't emulate the BAR in vfio, we have to emulate it in kvm where we emulate the write instruction. We then need to tell vfio, perhaps via userspace, that masking state has changed.

Quoted 6 lines
Quoted 1 line

Seems very intrusive.

We wouldn't direct map the vector table or pending bits, so we could trap and emulate in qemu, which could then call into reads/writes in vfio.

That's 100% unintrusive for kvm, but that's what we do today, which is deemed too slow.

Another option is to fake an interrupt remapping device and do a direct map. Will those older guests recognize and use it? I imagine not.

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#47 lore raw
Quoted 27 lines · Avi Kivity
Quoted 19 lines · Alex Williamson
Quoted 18 lines · Avi Kivity
Quoted 6 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 5 lines

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

Quoted 6 lines
Quoted 1 line

But if we emulate the entire msix bar in vfio, that's not needed, right?

Yes, I think it is. How does kvm know which interrupt to inject? Either vfio needs to pass that info to qemu and qemu would pass it to kvm, or vfio would have some way to pass that info to kvm directly.

Wait. We can't emulate the BAR in vfio, we have to emulate it in kvm where we emulate the write instruction. We then need to tell vfio, perhaps via userspace, that masking state has changed.

Quoted 6 lines
Quoted 1 line

Seems very intrusive.

We wouldn't direct map the vector table or pending bits, so we could trap and emulate in qemu, which could then call into reads/writes in vfio.

Quoted 5 lines

That's 100% unintrusive for kvm, but that's what we do today, which is deemed too slow.

Another option is to fake an interrupt remapping device and do a direct map. Will those older guests recognize and use it? I imagine not.

Nope, I think it's a non-starter to think anything but the latest current guests will have support for that.

Alex

#48 lore raw
In reply to Michael S. Tsirkin
Quoted 20 lines · Michael S. Tsirkin
Quoted 10 lines · Avi Kivity
Quoted 9 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

Quoted 8 lines
Quoted 5 lines

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Quoted 7 lines

Likely any old linux.

Quoted 1 line

Alex, Michael, how would you do this with vfio?

With current VFIO we would catch mask writes in qemu and call a KVM ioctl. We would also need an ioctl to retrieve pending bits long term.

Ugh, no. VFIO us currently independent of KVM. I'd like to keep it that way. We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

Quoted 11 lines

I think that it is unfortunate that we need to do this in userspace while rest of configuration is done in kernel.
I would be much happier with userspace simply forwarding everything to VFIO, so emulation does not have to
be split. That would be a clean interface: just mmap MSIX BAR and forget about it.

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

Sounds good except that I'd like to get away from forcing device assignment interfaces into KVM.

Alex

#49 lore raw
Quoted 25 lines · Alex Williamson
Quoted 24 lines · Michael S. Tsirkin
Quoted 10 lines · Avi Kivity
Quoted 9 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

Quoted 8 lines
Quoted 5 lines

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Likely any old linux.

Quoted 1 line

Alex, Michael, how would you do this with vfio?

Quoted 8 lines
Quoted 3 lines

With current VFIO we would catch mask writes in qemu and call a KVM ioctl. We would also need an ioctl to retrieve pending bits long term.

Ugh, no. VFIO us currently independent of KVM. I'd like to keep it that way. We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

So ideally masking would be optimizeable too.

Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

Why not mask/unmask with a write?

Quoted 12 lines
Quoted 11 lines

I think that it is unfortunate that we need to do this in userspace while rest of configuration is done in kernel.
I would be much happier with userspace simply forwarding everything to VFIO, so emulation does not have to
be split. That would be a clean interface: just mmap MSIX BAR and forget about it.

If instead of eventfd we had a file descriptor that can pass vector information from vfio to kvm and back, that would fix it, as we would not need to set us GSIs at all,
and not need for userspace to handle MSIX specially.

Quoted 4 lines

Sounds good except that I'd like to get away from forcing device assignment interfaces into KVM.

Alex

How to split up the modules is an implementation detail though.

Signature · 1 line
MST
#50 lore raw
Quoted 30 lines · Michael S. Tsirkin
Quoted 29 lines · Alex Williamson
Quoted 27 lines · Michael S. Tsirkin
Quoted 10 lines · Avi Kivity
Quoted 9 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

Quoted 8 lines
Quoted 5 lines

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Likely any old linux.

Quoted 1 line

Alex, Michael, how would you do this with vfio?

With current VFIO we would catch mask writes in qemu and call a KVM ioctl. We would also need an ioctl to retrieve pending bits long term.

Quoted 6 lines
Quoted 4 lines

Ugh, no. VFIO us currently independent of KVM. I'd like to keep it that way. We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

So ideally masking would be optimizeable too.

What does KVM add to the masking? VFIO owns the interrupt handler, which can decide if the interrupt is masked and set a pending bit in an emulated PBA, or if unmasked it sends it to qemu via eventfd. For KVM, I think we'd just augment that last bit to relay the interrupt to KVM for direct guest injection.

Quoted 5 lines
Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

Why not mask/unmask with a write?

That would be possible too, only trouble is then we have QEMU intercepting and interpreting the write as well as VFIO intercepting and interpreting the write. If VFIO is only masking off the mask bit, that'd be pretty trivial though.

Alex

#51 lore raw
Quoted 36 lines · Alex Williamson
Quoted 35 lines · Michael S. Tsirkin
Quoted 33 lines · Alex Williamson
Quoted 20 lines · Michael S. Tsirkin
Quoted 10 lines · Avi Kivity
Quoted 9 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

Quoted 8 lines
Quoted 5 lines

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

Quoted 7 lines

Likely any old linux.

Quoted 1 line

Alex, Michael, how would you do this with vfio?

With current VFIO we would catch mask writes in qemu and call a KVM ioctl. We would also need an ioctl to retrieve pending bits long term.

Ugh, no. VFIO us currently independent of KVM. I'd like to keep it that way. We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

Quoted 7 lines
Quoted 1 line

So ideally masking would be optimizeable too.

What does KVM add to the masking? VFIO owns the interrupt handler, which can decide if the interrupt is masked and set a pending bit in an emulated PBA, or if unmasked it sends it to qemu via eventfd. For KVM, I think we'd just augment that last bit to relay the interrupt to KVM for direct guest injection.

Right. MAsking in KVM is good for old style assignment and for vhost-net.

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

Why not mask/unmask with a write?

Quoted 7 lines

That would be possible too, only trouble is then we have QEMU intercepting and interpreting the write as well as VFIO intercepting and interpreting the write.
If VFIO is only masking off the mask bit,
that'd be pretty trivial though.

Alex

I just mean write() instead of an ioctl()

Signature · 1 line
MST
#52 lore raw
Quoted 9 lines · Michael S. Tsirkin
Quoted 8 lines · Alex Williamson
Quoted 6 lines · Michael S. Tsirkin
Quoted 3 lines · Alex Williamson

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

Why not mask/unmask with a write?

Quoted 7 lines
Quoted 5 lines

That would be possible too, only trouble is then we have QEMU intercepting and interpreting the write as well as VFIO intercepting and interpreting the write.
If VFIO is only masking off the mask bit,
that'd be pretty trivial though.

I just mean write() instead of an ioctl()

Hmm, looking back through my vfio driver, I actually have some code that passes guest writes of the vector control field down to vfio. With interrupt remapping support, vfio should pass these to the device masking the interrupt at the source so we don't even need pending bit emulation. Then we just need to make sure we only filter out the vector table for read/write of the page it lives on so we can support the PBA being on the same page.

Alex

#53 lore raw
Quoted 16 lines · Alex Williamson
Quoted 15 lines · Michael S. Tsirkin
Quoted 13 lines · Alex Williamson
Quoted 6 lines · Michael S. Tsirkin
Quoted 3 lines · Alex Williamson

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

Why not mask/unmask with a write?

That would be possible too, only trouble is then we have QEMU intercepting and interpreting the write as well as VFIO intercepting and interpreting the write.
If VFIO is only masking off the mask bit,
that'd be pretty trivial though.

Quoted 7 lines
Quoted 1 line

I just mean write() instead of an ioctl()

Hmm, looking back through my vfio driver, I actually have some code that passes guest writes of the vector control field down to vfio. With interrupt remapping support, vfio should pass these to the device masking the interrupt at the source so we don't even need pending bit emulation.

No, that would conflict with kernel using mask bits.
If we do this this direct access is very wrong, we must use kernel APIs for this.

Quoted 7 lines

Then we just need to make sure we only filter out the vector
table for read/write of the page it lives on so we can support the PBA being on the same page.

Alex

#54 lore raw
In reply to Alex Williamson
Quoted 7 lines · Alex Williamson
Quoted 4 lines

With current VFIO we would catch mask writes in qemu and call a KVM ioctl. We would also need an ioctl to retrieve pending bits long term.

Ugh, no. VFIO us currently independent of KVM. I'd like to keep it that way.

Me, too. Perhaps even more than you.

Quoted 3 lines

We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

For level-triggered interrupts only, yes? MSI EOI does not involve any device or interrupt controller visible action?

Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

I would much like to see in-band information (which mask/unmask is for older Linux) done via eventfds so userspace is not involved.

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#55 lore raw
Quoted 9 lines · Avi Kivity
Quoted 7 lines · Alex Williamson
Quoted 4 lines

With current VFIO we would catch mask writes in qemu and call a KVM ioctl. We would also need an ioctl to retrieve pending bits long term.

Ugh, no. VFIO us currently independent of KVM. I'd like to keep it that way.

Quoted 8 lines

Me, too. Perhaps even more than you.

Quoted 3 lines

We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

For level-triggered interrupts only, yes? MSI EOI does not involve any device or interrupt controller visible action?

Right, the EOI is for legacy interrupts only. Perhaps we don't care enough about performance of those to route it through KVM so long as we can co-exist with the KVM APIC. MSIs are currently still bouncing from VFIO to QEMU to the guest, which seems inefficient.

Quoted 6 lines
Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

I would much like to see in-band information (which mask/unmask is for older Linux) done via eventfds so userspace is not involved.

Hmm, I'm not sure how to do that yet.

Alex

#56 lore raw
Quoted 10 lines · Alex Williamson
Quoted 6 lines
Quoted 3 lines

We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

For level-triggered interrupts only, yes? MSI EOI does not involve any device or interrupt controller visible action?

Right, the EOI is for legacy interrupts only. Perhaps we don't care enough about performance of those to route it through KVM so long as we can co-exist with the KVM APIC.

We will need a way to get the EOI out to userspace, then.

Perhaps that will give us motivation to split the ioapic from the kernel (though I suppose fear of regressions will stop us).

Anyway EOI notifiers are also useful for timekeeping.

Quoted 2 lines

MSIs are currently still bouncing from
VFIO to QEMU to the guest, which seems inefficient.

Why is that? Can't you use irqfd like vhost-net?

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

I would much like to see in-band information (which mask/unmask is for older Linux) done via eventfds so userspace is not involved.

Hmm, I'm not sure how to do that yet.

One ugly way is to use two eventfds, one counting mask events, one counting unmask events. The difference is the value if the masked bit.

Another option is to use http://permalink.gmane.org/gmane.linux.kernel.commits.head/188038 which allows kvm to maintain a counter using eventfd. The vfio state machine then looks like:

interrupt:
if counter == 0 mask interrupts, set pending bit, wait for counter to
become 1
if counter == 1 forward it
counter becomes 1: unmask, forward interrupt if pending

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#57 lore raw
Quoted 42 lines · Avi Kivity
Quoted 10 lines · Alex Williamson
Quoted 6 lines
Quoted 3 lines

We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

For level-triggered interrupts only, yes? MSI EOI does not involve any device or interrupt controller visible action?

Right, the EOI is for legacy interrupts only. Perhaps we don't care enough about performance of those to route it through KVM so long as we can co-exist with the KVM APIC.

We will need a way to get the EOI out to userspace, then.

Perhaps that will give us motivation to split the ioapic from the kernel (though I suppose fear of regressions will stop us).

Anyway EOI notifiers are also useful for timekeeping.

Quoted 2 lines

MSIs are currently still bouncing from
VFIO to QEMU to the guest, which seems inefficient.

Why is that? Can't you use irqfd like vhost-net?

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

I would much like to see in-band information (which mask/unmask is for older Linux) done via eventfds so userspace is not involved.

Hmm, I'm not sure how to do that yet.

One ugly way is to use two eventfds, one counting mask events, one counting unmask events. The difference is the value if the masked bit.

Another option is to use
http://permalink.gmane.org/gmane.linux.kernel.commits.head/188038
which allows kvm to maintain a counter using eventfd. The vfio state machine then looks like:

Quoted 5 lines

interrupt:
if counter == 0 mask interrupts, set pending bit, wait for counter
to become 1
if counter == 1 forward it
counter becomes 1: unmask, forward interrupt if pending

One issue is that this page has a ton of other info. KVM would have to keep all that in kernel...

Signature · 1 line
MST
#58 lore raw
Quoted 10 lines · Michael S. Tsirkin
Quoted 10 lines

One ugly way is to use two eventfds, one counting mask events, one counting unmask events. The difference is the value if the masked bit.

Another option is to use
http://permalink.gmane.org/gmane.linux.kernel.commits.head/188038
which allows kvm to maintain a counter using eventfd. The vfio state machine then looks like:

Quoted 8 lines
Quoted 5 lines

interrupt:
if counter == 0 mask interrupts, set pending bit, wait for counter
to become 1
if counter == 1 forward it
counter becomes 1: unmask, forward interrupt if pending

One issue is that this page has a ton of other info. KVM would have to keep all that in kernel...

Or we'd make kvm an "accelerator" and pass through other accesses to userspace. I don't like it because it's a blurry programming model. At the very least it has to be documented very well.

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#59 lore raw
Quoted 17 lines · Avi Kivity
Quoted 16 lines · Michael S. Tsirkin
Quoted 15 lines

One ugly way is to use two eventfds, one counting mask events, one counting unmask events. The difference is the value if the masked bit.

Another option is to use
http://permalink.gmane.org/gmane.linux.kernel.commits.head/188038
which allows kvm to maintain a counter using eventfd. The vfio state machine then looks like:

interrupt:
if counter == 0 mask interrupts, set pending bit, wait for counter
to become 1
if counter == 1 forward it
counter becomes 1: unmask, forward interrupt if pending

Quoted 5 lines
Quoted 2 lines

One issue is that this page has a ton of other info. KVM would have to keep all that in kernel...

Or we'd make kvm an "accelerator" and pass through other accesses to userspace. I don't like it because it's a blurry programming model.

Not sure I understand.

Quoted 5 lines

At the very least it has to be documented very well.

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#60 lore raw
In reply to Michael S. Tsirkin
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines · Avi Kivity
Quoted 10 lines · Alex Williamson
Quoted 6 lines
Quoted 3 lines

We'll need to optimize interrupt injection and eoi via KVM, but it should only be a performance optimization, not a functional requirement.

For level-triggered interrupts only, yes? MSI EOI does not involve any device or interrupt controller visible action?

Right, the EOI is for legacy interrupts only. Perhaps we don't care enough about performance of those to route it through KVM so long as we can co-exist with the KVM APIC.

We will need a way to get the EOI out to userspace, then.

Perhaps that will give us motivation to split the ioapic from the kernel (though I suppose fear of regressions will stop us).

Quoted 6 lines
Quoted 6 lines

Anyway EOI notifiers are also useful for timekeeping.

Quoted 2 lines

MSIs are currently still bouncing from
VFIO to QEMU to the guest, which seems inefficient.

Why is that? Can't you use irqfd like vhost-net?

Hope so, just haven't done it yet.

Quoted 18 lines
Quoted 18 lines
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

It would probably make sense to request a mask/unmask ioctl in VFIO for MSI-X, then perhaps the pending bits would only support read/write (no mmap), so we could avoid an ioctl there.

I would much like to see in-band information (which mask/unmask is for older Linux) done via eventfds so userspace is not involved.

Hmm, I'm not sure how to do that yet.

One ugly way is to use two eventfds, one counting mask events, one counting unmask events. The difference is the value if the masked bit.

Another option is to use
http://permalink.gmane.org/gmane.linux.kernel.commits.head/188038
which allows kvm to maintain a counter using eventfd. The vfio state machine then looks like:

Quoted 8 lines
Quoted 5 lines

interrupt:
if counter == 0 mask interrupts, set pending bit, wait for counter
to become 1
if counter == 1 forward it
counter becomes 1: unmask, forward interrupt if pending

One issue is that this page has a ton of other info. KVM would have to keep all that in kernel...

Yeah, I'm a little confused about who is setting and who is receiving the eventfd. KVM is going to trap that page, filter out the mask bit, do the eventfd thing for the msix vector table, and forward the rest to qemu? I'd like to see some benchmarks to know if that's really worthwhile.

Alex

author
#61 lore raw
In reply to Avi Kivity
Quoted 10 lines · On Wednesday 20 October 2010 17:51:01 Avi Kivity wrote:
Quoted 9 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

Quoted 7 lines
Quoted 4 lines

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Any performance numbers? What are the affected guests? just RHEL 4, or any others?

At least current RHEL5 series would be affected. I have done an simple benchmark on RHEL5u5 guest with 512m memory and 1 cpu. Device is one 10G NIC with SRIOV. One VF was assigned to guest to communicate the PF in the host. 3 threads had been used in iperf of guest to push the CPU utilization to 100%. In this condition, QEmu method's bandwidth is about 20% lower than in-kernel one(~7.5G vs ~9G). Interrupt rate in this condition is about 20k/sec.

The reason is 2.6.18 kernel used mask_msi in ack() for MSI chip, caused significant mask bit operations when interrupt rate is high.

We have also reproduced the issue on some large scale benchmark on the guest with newer kernel like 2.6.30 on Xen, under very high interrupt rate, due to some interrupt rate limitation mechanism in kernel.

Quoted 2 lines

Alex, Michael, how would you do this with vfio?

--
regards
Yang, Sheng

#62 lore raw
In reply to Sheng Yang
Quoted 14 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Quoted 9 lines

Sheng Yang (8):
PCI: MSI: Move MSI-X entry definition to pci_regs.h
irq: Export irq_to_desc() to modules
KVM: x86: Enable ENABLE_CAP capability for x86
KVM: Move struct kvm_io_device to kvm_host.h
KVM: Add kvm_get_irq_routing_entry() func
KVM: assigned dev: Preparation for mask support in userspace
KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing
KVM: Emulation MSI-X mask bits for assigned devices

Why does the current scheme, without msix per-vector mask support, is functional at all? Luck?

author
#63 lore raw
Quoted 25 lines · On Thursday 21 October 2010 03:02:24 Marcelo Tosatti wrote:
Quoted 14 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Quoted 9 lines

Sheng Yang (8):
PCI: MSI: Move MSI-X entry definition to pci_regs.h
irq: Export irq_to_desc() to modules
KVM: x86: Enable ENABLE_CAP capability for x86
KVM: Move struct kvm_io_device to kvm_host.h
KVM: Add kvm_get_irq_routing_entry() func
KVM: assigned dev: Preparation for mask support in userspace
KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing
KVM: Emulation MSI-X mask bits for assigned devices

Quoted 2 lines

Why does the current scheme, without msix per-vector mask support, is functional at all? Luck?

Well, I believe we are lucky... We just ignored the operation in the past.

I had raised this issue when Michael begin to work on MSI-X support in QEmu long ago, but then I was busy on some other things. Until now when Eddie want to add MSI-X in-kernel acceleration, we back to it...

And about the "flush_work()" you commented, I still think even for the native device, it's possible that the short time after OS write to the mask bit, the interrupt may be delivered, if the device already send the message out on the bus(I just guess, haven't observed)... The spec didn't say that the finish of writing mask bit behavior would also get all message on the bus delivered. So I think leave the work a little late would also be fine.

--
regards
Yang, Sheng

#64 lore raw
Quoted 35 lines · Sheng Yang
Quoted 25 lines · On Thursday 21 October 2010 03:02:24 Marcelo Tosatti wrote:
Quoted 14 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

I've taken Michael's advice to use mask/unmask directly, but unsure about exporting irq_to_desc() for module...

Also add flush_work() according to Marcelo's comments.

Quoted 9 lines

Sheng Yang (8):
PCI: MSI: Move MSI-X entry definition to pci_regs.h
irq: Export irq_to_desc() to modules
KVM: x86: Enable ENABLE_CAP capability for x86
KVM: Move struct kvm_io_device to kvm_host.h
KVM: Add kvm_get_irq_routing_entry() func
KVM: assigned dev: Preparation for mask support in userspace
KVM: assigned dev: Introduce io_device for MSI-X MMIO accessing
KVM: Emulation MSI-X mask bits for assigned devices

Quoted 2 lines

Why does the current scheme, without msix per-vector mask support, is functional at all? Luck?

Well, I believe we are lucky... We just ignored the operation in the past.

I had raised this issue when Michael begin to work on MSI-X support in QEmu long ago, but then I was busy on some other things. Until now when Eddie want to add MSI-X in-kernel acceleration, we back to it...

Quoted 6 lines

And about the "flush_work()" you commented, I still think even for the native device, it's possible that the short time after OS write to the mask bit, the interrupt may be delivered, if the device already send the message out on the bus(I just guess, haven't observed)... The spec didn't say that the finish of writing mask bit behavior would also get all message on the bus delivered. So I think leave the work a little late would also be fine.

Yes ... but e.g. bus read afterwards would have to flush the write up the bus...

Quoted 3 lines

--
regards
Yang, Sheng

#65 lore raw
In reply to Sheng Yang
Quoted 8 lines · Sheng Yang

Here is v2.

Changelog:

v1->v2

The major change from v1 is I've added the in-kernel MSI-X mask emulation support, as well as adding shortcuts for reading MSI-X table.

The major thing everyone here is asking is whether this in-kernel emulation can be shown to improve performance as measured some guest/benchmark. Do you know?

Signature · 1 line
MST