diff mbox

[resend] kvm tools: MSI-X fixes

Message ID 1314127733-12145-1-git-send-email-levinsasha928@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sasha Levin Aug. 23, 2011, 7:28 p.m. UTC
Several fixes in this patch:

* Don't ignore function level and per-vector masking. We're not
supposed to signal when masked and not doing so will improve
performance a bit (in addition to behaving correctly).

* Implement the missing PBA array. 'lspci -vv' will now show the correct
output:
	Capabilities: [40] MSI-X: Enable+ Count=5 Masked-
		Vector table: BAR=1 offset=00000000
		PBA: BAR=3 offset=00000000

* Checking whether MSI-X is enabled or not is done by probing the
corresponding bit within the PCI header instead of trying to track
the status ourselves.

* Fallback to INTx if MSI-X isn't enabled.

* Use correct max size for MSI-X table.

Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
---
 tools/kvm/include/kvm/pci.h        |    4 +-
 tools/kvm/include/kvm/virtio-pci.h |    4 +-
 tools/kvm/virtio/pci.c             |   89 ++++++++++++++++++++++++++++--------
 3 files changed, 74 insertions(+), 23 deletions(-)
diff mbox

Patch

diff --git a/tools/kvm/include/kvm/pci.h b/tools/kvm/include/kvm/pci.h
index 2ab5291..e74d3ec 100644
--- a/tools/kvm/include/kvm/pci.h
+++ b/tools/kvm/include/kvm/pci.h
@@ -34,9 +34,9 @@  struct msix_table {
 struct msix_cap {
 	u8 cap;
 	u8 next;
-	u16 table_size;
+	u16 ctrl;
 	u32 table_offset;
-	struct msix_table table[3];
+	u32 pba_offset;
 };
 
 struct pci_device_header {
diff --git a/tools/kvm/include/kvm/virtio-pci.h b/tools/kvm/include/kvm/virtio-pci.h
index 4524a7f..0c2a035 100644
--- a/tools/kvm/include/kvm/virtio-pci.h
+++ b/tools/kvm/include/kvm/virtio-pci.h
@@ -37,7 +37,9 @@  struct virtio_pci {
 	u32			vq_vector[VIRTIO_PCI_MAX_VQ];
 	u32			gsis[VIRTIO_PCI_MAX_VQ];
 	u32			msix_io_block;
-	int			msix_enabled;
+	u32			msix_pba_block;
+	u64			msix_pba;
+	struct msix_table	msix_table[VIRTIO_PCI_MAX_VQ + 1];
 
 	/* virtio queue */
 	u16			queue_selector;
diff --git a/tools/kvm/virtio/pci.c b/tools/kvm/virtio/pci.c
index 6d086fa..7181123 100644
--- a/tools/kvm/virtio/pci.c
+++ b/tools/kvm/virtio/pci.c
@@ -9,11 +9,17 @@ 
 #include <linux/virtio_pci.h>
 #include <string.h>
 
+static inline bool virtio_pci__msix_enabled(struct virtio_pci *vpci)
+{
+	return vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_ENABLE;
+}
+
 static bool virtio_pci__specific_io_in(struct kvm *kvm, struct virtio_pci *vpci, u16 port,
 					void *data, int size, int offset)
 {
 	u32 config_offset;
-	int type = virtio__get_dev_specific_field(offset - 20, vpci->msix_enabled,
+	int type = virtio__get_dev_specific_field(offset - 20,
+							virtio_pci__msix_enabled(vpci),
 							0, &config_offset);
 	if (type == VIRTIO_PCI_O_MSIX) {
 		switch (offset) {
@@ -81,7 +87,7 @@  static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_pci *vpci
 					void *data, int size, int offset)
 {
 	u32 config_offset, gsi, vec;
-	int type = virtio__get_dev_specific_field(offset - 20, vpci->msix_enabled,
+	int type = virtio__get_dev_specific_field(offset - 20, virtio_pci__msix_enabled(vpci),
 							0, &config_offset);
 	if (type == VIRTIO_PCI_O_MSIX) {
 		switch (offset) {
@@ -89,9 +95,9 @@  static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_pci *vpci
 			vec = vpci->config_vector = ioport__read16(data);
 
 			gsi = irq__add_msix_route(kvm,
-						  vpci->pci_hdr.msix.table[vec].low,
-						  vpci->pci_hdr.msix.table[vec].high,
-						  vpci->pci_hdr.msix.table[vec].data);
+						  vpci->msix_table[vec].low,
+						  vpci->msix_table[vec].high,
+						  vpci->msix_table[vec].data);
 
 			vpci->config_gsi = gsi;
 			break;
@@ -99,9 +105,9 @@  static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_pci *vpci
 			vec = vpci->vq_vector[vpci->queue_selector] = ioport__read16(data);
 
 			gsi = irq__add_msix_route(kvm,
-						  vpci->pci_hdr.msix.table[vec].low,
-						  vpci->pci_hdr.msix.table[vec].high,
-						  vpci->pci_hdr.msix.table[vec].data);
+						  vpci->msix_table[vec].low,
+						  vpci->msix_table[vec].high,
+						  vpci->msix_table[vec].data);
 			vpci->gsis[vpci->queue_selector] = gsi;
 			break;
 		}
@@ -159,28 +165,64 @@  static struct ioport_operations virtio_pci__io_ops = {
 	.io_out	= virtio_pci__io_out,
 };
 
-static void callback_mmio(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
+static void callback_mmio_table(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
 {
 	struct virtio_pci *vpci = ptr;
-	void *table = &vpci->pci_hdr.msix.table;
+	void *table = &vpci->msix_table;
 
-	vpci->msix_enabled = 1;
 	if (is_write)
 		memcpy(table + addr - vpci->msix_io_block, data, len);
 	else
 		memcpy(data, table + addr - vpci->msix_io_block, len);
 }
 
+static void callback_mmio_pba(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
+{
+	struct virtio_pci *vpci = ptr;
+	void *pba = &vpci->msix_pba;
+
+	if (is_write)
+		memcpy(pba + addr - vpci->msix_pba_block, data, len);
+	else
+		memcpy(data, pba + addr - vpci->msix_pba_block, len);
+}
+
 int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_pci *vpci, u32 vq)
 {
-	kvm__irq_line(kvm, vpci->gsis[vq], VIRTIO_IRQ_HIGH);
+	int tbl = vpci->vq_vector[vq];
 
+	if (virtio_pci__msix_enabled(vpci)) {
+		if (vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_MASKALL ||
+			vpci->msix_table[tbl].ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
+
+			vpci->msix_pba |= 1 << tbl;
+			return 0;
+		}
+
+		kvm__irq_trigger(kvm, vpci->gsis[vq]);
+	} else {
+		kvm__irq_trigger(kvm, vpci->pci_hdr.irq_line);
+	}
 	return 0;
 }
 
 int virtio_pci__signal_config(struct kvm *kvm, struct virtio_pci *vpci)
 {
-	kvm__irq_line(kvm, vpci->config_gsi, VIRTIO_IRQ_HIGH);
+	int tbl = vpci->config_vector;
+
+	if (virtio_pci__msix_enabled(vpci)) {
+		if (vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_MASKALL ||
+			vpci->msix_table[tbl].ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
+
+			vpci->msix_pba |= 1 << tbl;
+			return 0;
+		}
+
+		kvm__irq_trigger(kvm, vpci->config_gsi);
+	} else {
+		vpci->isr = VIRTIO_PCI_ISR_CONFIG;
+		kvm__irq_trigger(kvm, vpci->pci_hdr.irq_line);
+	}
 
 	return 0;
 }
@@ -192,9 +234,11 @@  int virtio_pci__init(struct kvm *kvm, struct virtio_pci *vpci, void *dev,
 
 	vpci->dev = dev;
 	vpci->msix_io_block = pci_get_io_space_block();
+	vpci->msix_pba_block = pci_get_io_space_block();
 
 	vpci->base_addr = ioport__register(IOPORT_EMPTY, &virtio_pci__io_ops, IOPORT_SIZE, vpci);
-	kvm__register_mmio(kvm, vpci->msix_io_block, 0x100, callback_mmio, vpci);
+	kvm__register_mmio(kvm, vpci->msix_io_block, 0x100, callback_mmio_table, vpci);
+	kvm__register_mmio(kvm, vpci->msix_pba_block, 0x100, callback_mmio_pba, vpci);
 
 	vpci->pci_hdr = (struct pci_device_header) {
 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
@@ -205,19 +249,24 @@  int virtio_pci__init(struct kvm *kvm, struct virtio_pci *vpci, void *dev,
 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
 		.subsys_id		= subsys_id,
 		.bar[0]			= vpci->base_addr | PCI_BASE_ADDRESS_SPACE_IO,
-		.bar[1]			= vpci->msix_io_block |
-					PCI_BASE_ADDRESS_SPACE_MEMORY |
-					PCI_BASE_ADDRESS_MEM_TYPE_64,
-		/* bar[2] is the continuation of bar[1] for 64bit addressing */
-		.bar[2]			= 0,
+		.bar[1]			= vpci->msix_io_block | PCI_BASE_ADDRESS_SPACE_MEMORY
+					| PCI_BASE_ADDRESS_MEM_TYPE_64,
+		.bar[3]			= vpci->msix_pba_block | PCI_BASE_ADDRESS_SPACE_MEMORY
+					| PCI_BASE_ADDRESS_MEM_TYPE_64,
 		.status			= PCI_STATUS_CAP_LIST,
 		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
 	};
 
 	vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
 	vpci->pci_hdr.msix.next = 0;
-	vpci->pci_hdr.msix.table_size = (VIRTIO_PCI_MAX_VQ + 1) | PCI_MSIX_FLAGS_ENABLE;
+	vpci->pci_hdr.msix.ctrl = (VIRTIO_PCI_MAX_VQ + 1);
+
+	/*
+	 * Both table and PBA could be mapped on the same BAR, but for now
+	 * we're not in short of BARs
+	 */
 	vpci->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
+	vpci->pci_hdr.msix.pba_offset = 3; /* Use BAR 3 */
 	vpci->config_vector = 0;
 
 	if (irq__register_device(VIRTIO_ID_RNG, &ndev, &pin, &line) < 0)