[PATCH 3/3] virtio_pci: optional MSI-X support
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
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 =