Implement find_vqs_irq() to reduce number of vectors. It can be used to specify which vq's need their own irqs, and which can share irqs with other vq's. Signed-off-by: krkumar2@xxxxxxxxxx --- drivers/virtio/virtio_pci.c | 108 ++++++++++++++++++++++++-------- include/linux/virtio_config.h | 14 ++++ 2 files changed, 95 insertions(+), 27 deletions(-) diff -ruNp org/drivers/virtio/virtio_pci.c new/drivers/virtio/virtio_pci.c --- org/drivers/virtio/virtio_pci.c 2011-11-11 16:45:09.000000000 +0530 +++ new/drivers/virtio/virtio_pci.c 2011-11-11 16:54:35.000000000 +0530 @@ -40,7 +40,7 @@ struct virtio_pci_device /* the IO mapping for the PCI config space */ void __iomem *ioaddr; - /* a list of queues so we can dispatch IRQs */ + /* a list of queues which have registered to receive IRQs */ spinlock_t lock; struct list_head virtqueues; @@ -196,7 +196,7 @@ static irqreturn_t vp_config_changed(int return IRQ_HANDLED; } -/* Notify all virtqueues on an interrupt. */ +/* Notify all vq's on 'virtqueues' list on an interrupt. */ static irqreturn_t vp_vring_interrupt(int irq, void *opaque) { struct virtio_pci_device *vp_dev = opaque; @@ -358,7 +358,7 @@ static struct virtqueue *setup_vq(struct struct virtio_pci_device *vp_dev = to_vp_device(vdev); struct virtio_pci_vq_info *info; struct virtqueue *vq; - unsigned long flags, size; + unsigned long size; u16 num; int err; @@ -378,6 +378,7 @@ static struct virtqueue *setup_vq(struct info->num = num; info->msix_vector = msix_vec; + INIT_LIST_HEAD(&info->node); size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN)); info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO); @@ -411,14 +412,6 @@ static struct virtqueue *setup_vq(struct } } - if (callback) { - spin_lock_irqsave(&vp_dev->lock, flags); - list_add(&info->node, &vp_dev->virtqueues); - spin_unlock_irqrestore(&vp_dev->lock, flags); - } else { - INIT_LIST_HEAD(&info->node); - } - return vq; out_assign: @@ -472,7 +465,8 @@ static void vp_del_vqs(struct virtio_dev if (vp_dev->per_vq_vectors && info->msix_vector != VIRTIO_MSI_NO_VECTOR) free_irq(vp_dev->msix_entries[info->msix_vector].vector, - vq); + list_empty(&info->node) ? + (void *)vq : (void *)vp_dev); vp_del_vq(vq); } vp_dev->per_vq_vectors = false; @@ -480,16 +474,37 @@ static void vp_del_vqs(struct virtio_dev vp_free_vectors(vdev); } +static void add_vq_to_list(struct virtqueue *vq, + struct virtio_pci_device *vp_dev, + vq_callback_t *cb) +{ + struct virtio_pci_vq_info *info = vq->priv; + unsigned long flags; + + if (cb) { + spin_lock_irqsave(&vp_dev->lock, flags); + list_add(&info->node, &vp_dev->virtqueues); + spin_unlock_irqrestore(&vp_dev->lock, flags); + } +} + +/* Return true if flags is NULL, or 'bit'# in flags is clear */ +static bool bit_clear(unsigned long *flags, int bit) +{ + return flags ? !test_bit(bit, flags) : true; +} + static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char *names[], bool use_msix, - bool per_vq_vectors) + bool per_vq_vectors, unsigned long *flags) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); u16 msix_vec; int i, err, nvectors, allocated_vectors; + int count = 0; /* Count of vq's using shared irq's */ if (!use_msix) { /* Old style: one normal interrupt for change and all vqs. */ @@ -500,9 +515,19 @@ static int vp_try_to_find_vqs(struct vir if (per_vq_vectors) { /* Best option: one for change interrupt, one per vq. */ nvectors = 1; - for (i = 0; i < nvqs; ++i) - if (callbacks[i]) + for (i = 0; i < nvqs; ++i) { + bool alloc_irq = bit_clear(flags, i); + + /* + * We allocate a vector if cb is present, + * AND (driver requested a vector OR this + * is the first shared vector). + */ + if (callbacks[i] && + (alloc_irq || ++count == 1)) ++nvectors; + } + count = 0; } else { /* Second best: one for change, shared for all vqs. */ nvectors = 2; @@ -516,20 +541,38 @@ static int vp_try_to_find_vqs(struct vir vp_dev->per_vq_vectors = per_vq_vectors; allocated_vectors = vp_dev->msix_used_vectors; for (i = 0; i < nvqs; ++i) { - if (!callbacks[i] || !vp_dev->msix_enabled) + bool alloc_irq = bit_clear(flags, i); + irq_handler_t irq_handler; + void *data; + + if (!callbacks[i] || !vp_dev->msix_enabled || + !(alloc_irq || ++count == 1)) msix_vec = VIRTIO_MSI_NO_VECTOR; else if (vp_dev->per_vq_vectors) msix_vec = allocated_vectors++; else msix_vec = VP_MSIX_VQ_VECTOR; + vqs[i] = setup_vq(vdev, i, callbacks[i], names[i], msix_vec); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto error_find; } - if (!vp_dev->per_vq_vectors || msix_vec == VIRTIO_MSI_NO_VECTOR) + if (!vp_dev->per_vq_vectors || + msix_vec == VIRTIO_MSI_NO_VECTOR) { + add_vq_to_list(vqs[i], vp_dev, callbacks[i]); continue; + } + + if (alloc_irq) { + irq_handler = vring_interrupt; + data = vqs[i]; + } else { + add_vq_to_list(vqs[i], vp_dev, callbacks[i]); + irq_handler = vp_vring_interrupt; + data = vp_dev; + } /* allocate per-vq irq if available and necessary */ snprintf(vp_dev->msix_names[msix_vec], @@ -537,9 +580,9 @@ static int vp_try_to_find_vqs(struct vir "%s-%s", dev_name(&vp_dev->vdev.dev), names[i]); err = request_irq(vp_dev->msix_entries[msix_vec].vector, - vring_interrupt, 0, + irq_handler, 0, vp_dev->msix_names[msix_vec], - vqs[i]); + data); if (err) { vp_del_vq(vqs[i]); goto error_find; @@ -554,26 +597,36 @@ error_request: return err; } -/* the config->find_vqs() implementation */ -static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char *names[]) +/* the config->find_vqs_irq() implementation */ +static int vp_find_vqs_irq(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[], unsigned long *flags) { int err; /* Try MSI-X with one vector per queue. */ - err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, true, true); + err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, true, true, + flags); if (!err) return 0; /* Fallback: MSI-X with one vector for config, one shared for queues. */ err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, - true, false); + true, false, NULL); if (!err) return 0; /* Finally fall back to regular interrupts. */ return vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, - false, false); + false, false, NULL); +} + +/* the config->find_vqs() implementation */ +static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + return vp_find_vqs_irq(vdev, nvqs, vqs, callbacks, names, NULL); } static struct virtio_config_ops virtio_pci_config_ops = { @@ -583,6 +636,7 @@ static struct virtio_config_ops virtio_p .set_status = vp_set_status, .reset = vp_reset, .find_vqs = vp_find_vqs, + .find_vqs_irq = vp_find_vqs_irq, .del_vqs = vp_del_vqs, .get_features = vp_get_features, .finalize_features = vp_finalize_features, diff -ruNp org/include/linux/virtio_config.h new/include/linux/virtio_config.h --- org/include/linux/virtio_config.h 2011-11-11 16:45:09.000000000 +0530 +++ new/include/linux/virtio_config.h 2011-11-11 16:45:21.000000000 +0530 @@ -92,6 +92,16 @@ * callbacks: array of callbacks, for each virtqueue * names: array of virtqueue names (mainly for debugging) * Returns 0 on success or error status + * @find_vqs_irq: find virtqueues and instantiate them. The flags parameter + * indicates the vq's that can share irq's. + * vdev: the virtio_device + * nvqs: the number of virtqueues to find + * vqs: on success, includes new virtqueues + * callbacks: array of callbacks, for each virtqueue + * names: array of virtqueue names (mainly for debugging) + * flags: indicates which vq's need their own irq and which can share. + * See example usage in virtio_net.c + * Returns 0 on success or error status * @del_vqs: free virtqueues found by find_vqs(). * @get_features: get the array of feature bits for this device. * vdev: the virtio_device @@ -114,6 +124,10 @@ struct virtio_config_ops { struct virtqueue *vqs[], vq_callback_t *callbacks[], const char *names[]); + int (*find_vqs_irq)(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[], unsigned long *flags); void (*del_vqs)(struct virtio_device *); u32 (*get_features)(struct virtio_device *vdev); void (*finalize_features)(struct virtio_device *vdev); -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html