[PATCH 3/3] virtio_pci: optional MSI-X support

2009-05-11 Thread Michael S. Tsirkin
This implements optional MSI-X support in virtio_pci.
MSI-X is used whenever the host supports at least 2 MSI-X
vectors: 1 for configuration changes and 1 for virtqueues.
Per-virtqueue vectors are allocated if enough vectors
available.

Signed-off-by: Michael S. Tsirkin m...@redhat.com
---
 drivers/virtio/virtio_pci.c |  211 +++
 include/linux/virtio_pci.h  |8 ++-
 2 files changed, 199 insertions(+), 20 deletions(-)

diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index a6bebe2..d21e2e6 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -42,6 +42,29 @@ struct virtio_pci_device
/* a list of queues so we can dispatch IRQs */
spinlock_t lock;
struct list_head virtqueues;
+
+   /* MSI-X support */
+   int msix_enabled;
+   int intx_enabled;
+   struct msix_entry *msix_entries;
+   /* Name strings for interrupts. This size should be enough,
+* and I'm too lazy to allocate each name separately. */
+   char (*msix_names)[256];
+   /* Number of vectors configured at startup (excludes per-virtqueue
+* vectors if any) */
+   unsigned msix_preset_vectors;
+   /* Number of per-virtqueue vectors if any. */
+   unsigned msix_per_vq_vectors;
+};
+
+/* Constants for MSI-X */
+/* Use first vector for configuration changes, second and the rest for
+ * virtqueues Thus, we need at least 2 vectors for MSI. */
+enum {
+   VP_MSIX_CONFIG_VECTOR = 0,
+   VP_MSIX_VQ_VECTOR = 1,
+   VP_MSIX_MIN_VECTORS = 2,
+   VP_MSIX_NO_VECTOR = 0x,
 };
 
 struct virtio_pci_vq_info
@@ -60,6 +83,9 @@ struct virtio_pci_vq_info
 
/* the list node for the virtqueues list */
struct list_head node;
+
+   /* MSI-X vector (or none) */
+   unsigned vector;
 };
 
 /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
@@ -109,7 +135,8 @@ static void vp_get(struct virtio_device *vdev, unsigned 
offset,
   void *buf, unsigned len)
 {
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-   void __iomem *ioaddr = vp_dev-ioaddr + VIRTIO_PCI_CONFIG + offset;
+   void __iomem *ioaddr = vp_dev-ioaddr +
+   VIRTIO_PCI_CONFIG(vp_dev) + offset;
u8 *ptr = buf;
int i;
 
@@ -123,7 +150,8 @@ static void vp_set(struct virtio_device *vdev, unsigned 
offset,
   const void *buf, unsigned len)
 {
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-   void __iomem *ioaddr = vp_dev-ioaddr + VIRTIO_PCI_CONFIG + offset;
+   void __iomem *ioaddr = vp_dev-ioaddr +
+  VIRTIO_PCI_CONFIG(vp_dev) + offset;
const u8 *ptr = buf;
int i;
 
@@ -221,7 +249,116 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
return vp_vring_interrupt(irq, opaque);
 }
 
-/* the config-find_vq() implementation */
+static void vp_free_vectors(struct virtio_device *vdev) {
+   struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+   int i;
+
+   if (vp_dev-intx_enabled) {
+   free_irq(vp_dev-pci_dev-irq, vp_dev);
+   vp_dev-intx_enabled = 0;
+   }
+
+   for (i = 0; i  vp_dev-msix_preset_vectors; ++i)
+   free_irq(vp_dev-msix_entries[i].vector, vp_dev);
+   vp_dev-msix_preset_vectors = 0;
+
+   if (vp_dev-msix_enabled) {
+   /* Disable the vector used for configuration */
+   iowrite16(VP_MSIX_NO_VECTOR,
+ vp_dev-ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+
+   vp_dev-msix_enabled = 0;
+   pci_disable_msix(vp_dev-pci_dev);
+   }
+}
+
+static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs)
+{
+   struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+   const char *name = dev_name(vp_dev-vdev.dev);
+   unsigned i, vectors;
+   int err = -ENOMEM;
+
+   /* We need at most one vector per queue and one for config changes */
+   vectors = VP_MSIX_VQ_VECTOR + max_vqs;
+   vp_dev-msix_entries = kmalloc(vectors * sizeof *vp_dev-msix_entries,
+  GFP_KERNEL);
+   if (!vp_dev-msix_entries)
+   goto error_entries;
+   vp_dev-msix_names = kmalloc(vectors * sizeof *vp_dev-msix_names,
+GFP_KERNEL);
+   if (!vp_dev-msix_names)
+   goto error_names;
+
+   snprintf(vp_dev-msix_names[VP_MSIX_CONFIG_VECTOR],
+sizeof *vp_dev-msix_names, %s-config, name);
+   for (i = 0; i  max_vqs; ++i)
+   snprintf(vp_dev-msix_names[i + VP_MSIX_VQ_VECTOR],
+sizeof *vp_dev-msix_names, %s-vq-%d, name, i);
+   for (i = 0; i  vectors; ++i)
+   vp_dev-msix_entries[i].entry = i;
+
+   vp_dev-msix_preset_vectors = 1;
+   vp_dev-msix_per_vq_vectors = max_vqs;
+   for (;;) {
+   

[PATCH 3/3] virtio_pci: optional MSI-X support

2009-05-07 Thread Michael S. Tsirkin
This implements optional MSI-X support in virtio_pci.
MSI-X is used whenever the host supports at least 2 MSI-X
vectors: 1 for configuration changes and 1 for virtqueues.
Per-virtqueue vectors are allocated if enough vectors
available.

Signed-off-by: Michael S. Tsirkin m...@redhat.com
---
 drivers/virtio/virtio_pci.c |  209 +--
 include/linux/virtio_pci.h  |8 ++-
 2 files changed, 190 insertions(+), 27 deletions(-)

diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h
index cd0fd5d..4a0275b 100644
--- a/include/linux/virtio_pci.h
+++ b/include/linux/virtio_pci.h
@@ -47,9 +47,15 @@
 /* The bit of the ISR which indicates a device configuration change. */
 #define VIRTIO_PCI_ISR_CONFIG  0x2
 
+/* MSI-X registers: only enabled if MSI-X is enabled. */
+/* A 16-bit vector for configuration changes. */
+#define VIRTIO_MSI_CONFIG_VECTOR20
+/* A 16-bit vector for selected queue notifications. */
+#define VIRTIO_MSI_QUEUE_VECTOR 22
+
 /* The remaining space is defined by each driver as the per-driver
  * configuration space */
-#define VIRTIO_PCI_CONFIG  20
+#define VIRTIO_PCI_CONFIG(dev) ((dev)-msix_enabled ? 24 : 20)
 
 /* Virtio ABI version, this must match exactly */
 #define VIRTIO_PCI_ABI_VERSION 0
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index f7b79a2..2b6333c 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -42,6 +42,28 @@ struct virtio_pci_device
/* a list of queues so we can dispatch IRQs */
spinlock_t lock;
struct list_head virtqueues;
+
+   /* MSI-X support */
+   int msix_enabled;
+   struct msix_entry *msix_entries;
+   /* Name strings for interrupts. This size should be enough,
+* and I'm too lazy to allocate each name separately. */
+   char (*msix_names)[256];
+   /* Number of vectors configured at startup (excludes per-virtqueue
+* vectors if any) */
+   unsigned msix_preset_vectors;
+   /* Number of per-virtqueue vectors if any. */
+   unsigned msix_per_vq_vectors;
+};
+
+/* Constants for MSI-X */
+/* Use first vector for configuration changes, second and the rest for
+ * virtqueues Thus, we need at least 2 vectors for MSI. */
+enum {
+   VP_MSIX_CONFIG_VECTOR = 0,
+   VP_MSIX_VQ_VECTOR = 1,
+   VP_MSIX_MIN_VECTORS = 2,
+   VP_MSIX_NO_VECTOR = 0x,
 };
 
 struct virtio_pci_vq_info
@@ -60,6 +82,9 @@ struct virtio_pci_vq_info
 
/* the list node for the virtqueues list */
struct list_head node;
+
+   /* MSI-X vector (or none) */
+   unsigned vector;
 };
 
 /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
@@ -109,7 +134,8 @@ static void vp_get(struct virtio_device *vdev, unsigned 
offset,
   void *buf, unsigned len)
 {
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-   void __iomem *ioaddr = vp_dev-ioaddr + VIRTIO_PCI_CONFIG + offset;
+   void __iomem *ioaddr = vp_dev-ioaddr +
+   VIRTIO_PCI_CONFIG(vp_dev) + offset;
u8 *ptr = buf;
int i;
 
@@ -123,7 +149,8 @@ static void vp_set(struct virtio_device *vdev, unsigned 
offset,
   const void *buf, unsigned len)
 {
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-   void __iomem *ioaddr = vp_dev-ioaddr + VIRTIO_PCI_CONFIG + offset;
+   void __iomem *ioaddr = vp_dev-ioaddr +
+  VIRTIO_PCI_CONFIG(vp_dev) + offset;
const u8 *ptr = buf;
int i;
 
@@ -221,17 +248,110 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
return vp_vring_interrupt(irq, opaque);
 }
 
-   spin_lock_irqsave(vp_dev-lock, flags);
-   list_for_each_entry(info, vp_dev-virtqueues, node) {
-   if (vring_interrupt(irq, info-vq) == IRQ_HANDLED)
-   ret = IRQ_HANDLED;
+/* the config-free_vqs() implementation */
+static void vp_free_vectors(struct virtio_device *vdev) {
+   struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+   int i;
+
+   /* Disable the vector used for configuration */
+   iowrite16(VP_MSIX_NO_VECTOR,
+ vp_dev-ioaddr + VIRTIO_MSI_CONFIG_VECTOR);
+
+   for (i = 0; i  vp_dev-msix_preset_vectors; ++i)
+   free_irq(vp_dev-msix_entries[i].vector, vp_dev);
+
+   if (!vp_dev-msix_preset_vectors)
+   free_irq(vp_dev-pci_dev-irq, vp_dev);
+
+   if (vp_dev-msix_enabled) {
+   vp_dev-msix_enabled = 0;
+   pci_disable_msix(vp_dev-pci_dev);
}
-   spin_unlock_irqrestore(vp_dev-lock, flags);
+}
 
-   return ret;
+static int vp_request_vectors(struct virtio_device *vdev, unsigned max_vqs)
+{
+   struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+   const char *name = dev_name(vp_dev-vdev.dev);
+   unsigned i, vectors;
+   int err =