All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael S. Tsirkin" <mst@redhat.com>
To: Rusty Russell <rusty@rustcorp.com.au>,
	virtualization@lists.linux-foundation.org,
	Anthony Liguori <anthony@codemonkey.ws>,
	kvm@vger.kernel.org, avi@redhat.com
Subject: [PATCH 8/8] virtio_pci: optional MSI-X support
Date: Mon, 27 Apr 2009 15:33:24 +0300	[thread overview]
Message-ID: <20090427123324.GA1196@redhat.com> (raw)
In-Reply-To: <cover.1240832196.git.mst@redhat.com>

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 <mst@redhat.com>
---
 drivers/virtio/virtio_pci.c |  147 ++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 132 insertions(+), 15 deletions(-)

diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 151538c..20bdc8c 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -42,8 +42,33 @@ struct virtio_pci_device
 	/* a list of queues so we can dispatch IRQs */
 	spinlock_t lock;
 	struct list_head virtqueues;
+
+	/* MSI-X support */
+	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
 };
 
+static inline int vq_vector(int index)
+{
+	return index + VP_MSIX_VQ_VECTOR;
+}
+
 struct virtio_pci_vq_info
 {
 	/* the actual virtqueue */
@@ -221,14 +246,92 @@ 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_vqs(struct virtio_device *vdev) {
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	int i;
+
+	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);
+}
+
+/* the config->request_vqs() implementation */
+static int vp_request_vqs(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 = 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[vq_vector(i)],
+			 sizeof *vp_dev->msix_names, "%s-vq-%d", name, i);
+
+	vp_dev->msix_preset_vectors = 1;
+	vp_dev->msix_per_vq_vectors = max_vqs;
+	for (;;) {
+		err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries,
+				      vectors);
+		/* Error out if not enough vectors */
+		if (err > 0 && err < VP_MSIX_MIN_VECTORS)
+			err = -EBUSY;
+		if (err <= 0)
+			break;
+		/* Not enough vectors for all queues. Retry, disabling
+		 * per-queue interrupts */
+		vectors = VP_MSIX_MIN_VECTORS;
+		vp_dev->msix_preset_vectors = VP_MSIX_MIN_VECTORS;
+		vp_dev->msix_per_vq_vectors = 0;
+		snprintf(vp_dev->msix_names[VP_MSIX_VQ_VECTOR],
+			 sizeof *vp_dev->msix_names, "%s-vq", name);
 	}
-	spin_unlock_irqrestore(&vp_dev->lock, flags);
 
-	return ret;
+	if (err) {
+		/* Can't allocate enough MSI-X vectors, use regular interrupt */
+		vp_dev->msix_preset_vectors = 0;
+		vp_dev->msix_per_vq_vectors = 0;
+		/* Register a handler for the queue with the PCI device's
+		 * interrupt */
+		err = request_irq(vp_dev->pci_dev->irq, vp_interrupt,
+				  IRQF_SHARED, name, vp_dev);
+		if (err)
+			goto error_irq;
+	}
+	for (i = 0; i < vp_dev->msix_preset_vectors; ++i) {
+		err = request_irq(vp_dev->msix_entries[i].vector,
+				  i == VP_MSIX_CONFIG_VECTOR ?
+				  vp_config_changed : vp_vring_interrupt,
+				  0, vp_dev->msix_names[i], vp_dev);
+		if (err) {
+			/* Set msix_preset_vectors so that only vectors we
+			 * already allocated will be freed by vp_free_vqs. */
+			vp_dev->msix_preset_vectors = i;
+			goto error_irq;
+		}
+	}
+	return 0;
+error_irq:
+	vp_free_vqs(vdev);
+	kfree(vp_dev->msix_names);
+error_names:
+	kfree(vp_dev->msix_entries);
+error_entries:
+	return err;
 }
 
 /* the config->find_vq() implementation */
@@ -242,6 +345,10 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
 	u16 num;
 	int err;
 
+	/* If using IRQ vector per vq, make sure we have enough vectors */
+	if (vp_dev->msix_per_vq_vectors && vp_dev->msix_per_vq_vectors <= index)
+		return ERR_PTR(-ENOENT);
+
 	/* Select the queue we're interested in */
 	iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
 
@@ -285,8 +392,19 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
 	list_add(&info->node, &vp_dev->virtqueues);
 	spin_unlock_irqrestore(&vp_dev->lock, flags);
 
+	/* allocate per-vq irq if neccessary */
+	if (vp_dev->msix_per_vq_vectors) {
+		err = request_irq(vp_dev->msix_entries[vq_vector(index)].vector,
+				  vring_interrupt, 0,
+				  vp_dev->msix_names[vq_vector(index)], vq);
+		if (err)
+			goto out_request_irq;
+	}
+
 	return vq;
 
+out_request_irq:
+	vring_del_virtqueue(vq);
 out_activate_queue:
 	iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 	free_pages_exact(info->queue, size);
@@ -302,6 +420,11 @@ static void vp_del_vq(struct virtqueue *vq)
 	struct virtio_pci_vq_info *info = vq->priv;
 	unsigned long flags, size;
 
+	if (vp_dev->msix_per_vq_vectors) {
+		int vector = vq_vector(info->queue_index);
+		free_irq(vp_dev->msix_entries[vector].vector, vq);
+	}
+
 	spin_lock_irqsave(&vp_dev->lock, flags);
 	list_del(&info->node);
 	spin_unlock_irqrestore(&vp_dev->lock, flags);
@@ -323,6 +446,8 @@ static struct virtio_config_ops virtio_pci_config_ops = {
 	.get_status	= vp_get_status,
 	.set_status	= vp_set_status,
 	.reset		= vp_reset,
+	.request_vqs	= vp_request_vqs,
+	.free_vqs	= vp_free_vqs,
 	.find_vq	= vp_find_vq,
 	.del_vq		= vp_del_vq,
 	.get_features	= vp_get_features,
@@ -394,21 +519,13 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
 	vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
 	vp_dev->vdev.id.device = pci_dev->subsystem_device;
 
-	/* register a handler for the queue with the PCI device's interrupt */
-	err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
-			  dev_name(&vp_dev->vdev.dev), vp_dev);
-	if (err)
-		goto out_set_drvdata;
-
 	/* finally register the virtio device */
 	err = register_virtio_device(&vp_dev->vdev);
 	if (err)
-		goto out_req_irq;
+		goto out_set_drvdata;
 
 	return 0;
 
-out_req_irq:
-	free_irq(pci_dev->irq, vp_dev);
 out_set_drvdata:
 	pci_set_drvdata(pci_dev, NULL);
 	pci_iounmap(pci_dev, vp_dev->ioaddr);
-- 
1.6.0.6

  parent reply	other threads:[~2009-04-27 12:35 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <cover.1240832196.git.mst@redhat.com>
2009-04-27 12:31 ` [PATCH 1/8] virtio: add request_vqs/free_vqs operations Michael S. Tsirkin
2009-05-04 11:32   ` Rusty Russell
2009-05-04 11:51     ` Michael S. Tsirkin
2009-05-04 11:51     ` Michael S. Tsirkin
2009-05-04 11:32   ` Rusty Russell
2009-04-27 12:31 ` Michael S. Tsirkin
2009-04-27 12:32 ` [PATCH 2/8] virtio_blk: add request_vqs/free_vqs calls Michael S. Tsirkin
2009-04-27 12:32 ` Michael S. Tsirkin
2009-04-27 12:32 ` [PATCH 3/8] virtio-rng: " Michael S. Tsirkin
2009-04-27 12:32 ` Michael S. Tsirkin
2009-04-27 12:32 ` [PATCH 4/8] virtio_console: " Michael S. Tsirkin
2009-04-27 12:32 ` Michael S. Tsirkin
2009-04-27 12:32 ` [PATCH 5/8] virtio_net: " Michael S. Tsirkin
2009-04-27 12:32 ` Michael S. Tsirkin
2009-04-27 12:33 ` [PATCH 6/8] virtio_balloon: " Michael S. Tsirkin
2009-04-27 12:33 ` Michael S. Tsirkin
2009-04-27 12:33 ` [PATCH 7/8] virtio_pci: split up vp_interrupt Michael S. Tsirkin
2009-04-27 12:33 ` Michael S. Tsirkin
2009-04-27 12:33 ` Michael S. Tsirkin [this message]
2009-04-27 12:33 ` [PATCH 8/8] virtio_pci: optional MSI-X support Michael S. Tsirkin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090427123324.GA1196@redhat.com \
    --to=mst@redhat.com \
    --cc=anthony@codemonkey.ws \
    --cc=avi@redhat.com \
    --cc=kvm@vger.kernel.org \
    --cc=rusty@rustcorp.com.au \
    --cc=virtualization@lists.linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.