virtio: find_vqs/del_vqs virtio operations
Subsystems (13)
Michael S. Tsirkin
maintainerPATCH v5 1/3 virtio: find_vqs/del_vqs virtio operations
In reply to a message not in the archiveThis replaces find_vq/del_vq with find_vqs/del_vqs virtio operations, and updates all drivers. This is needed for MSI support, because MSI needs to know the total number of vectors upfront.
--- drivers/block/virtio_blk.c | 6 ++-- drivers/char/hw_random/virtio-rng.c | 6 ++-- drivers/char/virtio_console.c | 26 ++++++++----------- drivers/lguest/lguest_device.c | 36 +++++++++++++++++++++++++- drivers/net/virtio_net.c | 45 +++++++++++++-------------------- drivers/s390/kvm/kvm_virtio.c | 36 +++++++++++++++++++++++++- drivers/virtio/virtio_balloon.c | 27 ++++++++------------ drivers/virtio/virtio_pci.c | 37 ++++++++++++++++++++++----- include/linux/virtio_config.h | 46 ++++++++++++++++++++++++++-------- net/9p/trans_virtio.c | 2 +- 10 files changed, 180 insertions(+), 87 deletions(-)
drivers/block/virtio_blk.c +3 -3
@@ -224,7 +224,7 @@ static int virtblk_probe(struct virtio_device *vdev)sg_init_table (vblk->sg, vblk->sg_elems);/* We expect one virtqueue, for output. */ - vblk->vq = vdev->config->find_vq (vdev,0 , blk_done,"requests" ); + vblk->vq =virtio_find_single_vq (vdev, blk_done,"requests" );if (IS_ERR (vblk->vq)) { err =PTR_ERR (vblk->vq); goto out_free_vblk;
@@ -323,7 +323,7 @@ out_put_disk: out_mempool:mempool_destroy (vblk->pool); out_free_vq: - vdev->config->del_vq (vblk->vq); + vdev->config->del_vqs (vdev); out_free_vblk:kfree (vblk); out:
@@ -344,7 +344,7 @@ static void virtblk_remove(struct virtio_device *vdev)blk_cleanup_queue (vblk->disk->queue);put_disk (vblk->disk);mempool_destroy (vblk->pool); - vdev->config->del_vq (vblk->vq); + vdev->config->del_vqs (vdev);kfree (vblk); }
drivers/char/hw_random/virtio-rng.c +3 -3
@@ -94,13 +94,13 @@ static int virtrng_probe(struct virtio_device *vdev)int err;/* We expect a single virtqueue. */ - vq = vdev->config->find_vq (vdev,0 , random_recv_done,"input" ); + vq =virtio_find_single_vq (vdev, random_recv_done,"input" );if (IS_ERR (vq))return PTR_ERR (vq); err =hwrng_register (&virtio_hwrng);if (err) { - vdev->config->del_vq (vq); + vdev->config->del_vqs (vdev);return err; }
@@ -112,7 +112,7 @@ static void virtrng_remove(struct virtio_device *vdev) { vdev->config->reset (vdev);hwrng_unregister (&virtio_hwrng); - vdev->config->del_vq (vq); + vdev->config->del_vqs (vdev); }static struct virtio_device_id id_table[] = {
drivers/char/virtio_console.c +11 -15
@@ -188,6 +188,9 @@ static void hvc_handle_input(struct virtqueue *vq)* Finally we put our input buffer in the input queue, ready to receive. */ static int __devinit virtcons_probe (struct virtio_device *dev) { +vq_callback_t *callbacks[] = { hvc_handle_input,NULL }; +const char *names[] = {"input" ,"output" }; +struct virtqueue *vqs[2 ];int err; vdev = dev;
@@ -199,20 +202,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev) goto fail; } -/* Find the input queue. */ +/* Find the queues. */ /* FIXME: This is why we want to wean off hvc: we do nothing * when input comes in. */ - in_vq = vdev->config->find_vq (vdev,0 , hvc_handle_input,"input" ); -if (IS_ERR (in_vq)) { - err =PTR_ERR (in_vq); + err = vdev->config->find_vqs (vdev,2 , vqs, callbacks, names); +if (err) goto free; - } - out_vq = vdev->config->find_vq (vdev,1 ,NULL ,"output" ); -if (IS_ERR (out_vq)) { - err =PTR_ERR (out_vq); - goto free_in_vq; - } + in_vq = vqs[0 ]; + out_vq = vqs[1 ];/* Start using the new console output. */ virtio_cons.get_chars = get_chars;
@@ -233,17 +231,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev) hvc =hvc_alloc (0 ,0 , &virtio_cons,PAGE_SIZE );if (IS_ERR (hvc)) { err =PTR_ERR (hvc); - goto free_out_vq; + goto free_vqs; }/* Register the input buffer the first time. */ add_inbuf ();return 0 ; -free_out_vq: - vdev->config->del_vq (out_vq); -free_in_vq: - vdev->config->del_vq (in_vq); +free_vqs: + vdev->config->del_vqs (vdev); free:kfree (inbuf); fail:
drivers/lguest/lguest_device.c +34 -2
@@ -313,6 +313,38 @@ static void lg_del_vq(struct virtqueue *vq)kfree (lvq); } +static void lg_del_vqs (struct virtio_device *vdev) +{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +kvm_del_vq (vq); +} + +static int lg_find_vqs (struct virtio_device *vdev,unsigned nvqs, + struct virtqueue *vqs[] + vq_callback_t *callbacks[], +const char *names[]) +{ +struct lguest_device *ldev =to_lgdev (vdev); +int i; + +/* We must have this many virtqueues. */ +if (nvqs > ldev->desc->num_vq) +return ERR_PTR (-ENOENT ); + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =lg_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +vp_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +/* The ops structure which hooks everything together. */ static struct virtio_config_ops lguest_config_ops = { .get_features = lg_get_features,
@@ -322,8 +354,8 @@ static struct virtio_config_ops lguest_config_ops = { .get_status = lg_get_status, .set_status = lg_set_status, .reset = lg_reset, - .find_vq = lg_find_vq, - .del_vq = lg_del_vq, + .find_vqs = lg_find_vqs, + .del_vqs = lg_del_vqs, };/* The root device for the lguest virtio devices. This makes them appear as
drivers/net/virtio_net.c +18 -27
81 lines not on this page · raw message
drivers/s390/kvm/kvm_virtio.c +34 -2
@@ -227,6 +227,38 @@ static void kvm_del_vq(struct virtqueue *vq) KVM_S390_VIRTIO_RING_ALIGN)); } +static void vp_del_vqs (struct virtio_device *vdev) +{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +kvm_del_vq (vq); +} + +static int kvm_find_vqs (struct virtio_device *vdev,unsigned nvqs, +struct virtqueue *vqs[], +vq_callback_t *callbacks[], +const char *names[]) +{ +struct kvm_device *kdev =to_kvmdev (vdev); +int i; + +/* We must have this many virtqueues. */ +if (nvqs > kdev->desc->num_vq) +return -ENOENT ; + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =kvm_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +kvm_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +/* * The config ops structure as defined by virtio config */
@@ -238,8 +270,8 @@ static struct virtio_config_ops kvm_vq_configspace_ops = { .get_status = kvm_get_status, .set_status = kvm_set_status, .reset = kvm_reset, - .find_vq = kvm_find_vq, - .del_vq = kvm_del_vq, + .find_vqs = kvm_find_vqs, + .del_vqs = kvm_del_vqs, };/*
drivers/virtio/virtio_balloon.c +11 -16
@@ -204,6 +204,9 @@ static int balloon(void *_vballoon)static int virtballoon_probe (struct virtio_device *vdev) {struct virtio_balloon *vb; +struct virtqueue *vqs[2 ]; +vq_callback_t *callbacks[] = { balloon_ack, balloon_ack }; +const char *names[] = {"inflate" ,"deflate" };int err; vdev->priv = vb =kmalloc (sizeof (*vb),GFP_KERNEL );
@@ -218,22 +221,17 @@ static int virtballoon_probe(struct virtio_device *vdev) vb->vdev = vdev;/* We expect two virtqueues. */ - vb->inflate_vq = vdev->config->find_vq (vdev,0 , balloon_ack,"inflate" ); -if (IS_ERR (vb->inflate_vq)) { - err =PTR_ERR (vb->inflate_vq); + err = vdev->config->find_vqs (vdev,2 , vqs, callbacks, names); +if (err) goto out_free_vb; - } - vb->deflate_vq = vdev->config->find_vq (vdev,1 , balloon_ack,"deflate" ); -if (IS_ERR (vb->deflate_vq)) { - err =PTR_ERR (vb->deflate_vq); - goto out_del_inflate_vq; - } + vb->inflate_vq = vqs[0 ]; + vb->deflate_vq = vqs[1 ]; vb->thread =kthread_run (balloon, vb,"vballoon" );if (IS_ERR (vb->thread)) { err =PTR_ERR (vb->thread); - goto out_del_deflate_vq; + goto out_del_vqs; } vb->tell_host_first
@@ -241,10 +239,8 @@ static int virtballoon_probe(struct virtio_device *vdev)return 0 ; -out_del_deflate_vq: - vdev->config->del_vq (vb->deflate_vq); -out_del_inflate_vq: - vdev->config->del_vq (vb->inflate_vq); +out_del_vqs: + vdev->config->del_vqs (vdev); out_free_vb:kfree (vb); out:
@@ -264,8 +260,7 @@ static void virtballoon_remove(struct virtio_device *vdev)/* Now we reset the device so we can clean up the queues. */ vdev->config->reset (vdev); - vdev->config->del_vq (vb->deflate_vq); - vdev->config->del_vq (vb->inflate_vq); + vdev->config->del_vqs (vdev);kfree (vb); }
drivers/virtio/virtio_pci.c +30 -7
@@ -276,11 +276,7 @@ static void vp_del_vq(struct virtqueue *vq) {struct virtio_pci_device *vp_dev =to_vp_device (vq->vdev);struct virtio_pci_vq_info *info = vq->priv; -unsigned long flags, size; - -spin_lock_irqsave (&vp_dev->lock, flags); -list_del (&info->node); -spin_unlock_irqrestore (&vp_dev->lock, flags); +unsigned long size;vring_del_virtqueue (vq);
@@ -293,14 +289,41 @@ static void vp_del_vq(struct virtqueue *vq)kfree (info); } +static void vp_del_vqs (struct virtio_device *vdev) +{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +vp_del_vq (vq); +} + +static int vp_find_vqs (struct virtio_device *vdev,unsigned nvqs, +struct virtqueue *vqs[], +vq_callback_t *callbacks[], +const char *names[]) +{ +int i; + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =vp_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +vp_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +static struct virtio_config_ops virtio_pci_config_ops = { .get = vp_get, .set = vp_set, .get_status = vp_get_status, .set_status = vp_set_status, .reset = vp_reset, - .find_vq = vp_find_vq, - .del_vq = vp_del_vq, + .find_vqs = vp_find_vqs, + .del_vqs = vp_del_vqs, .get_features = vp_get_features, .finalize_features = vp_finalize_features, };
include/linux/virtio_config.h +35 -11
73 lines not on this page · raw message
net/9p/trans_virtio.c +1 -1
@@ -246,7 +246,7 @@ static int p9_virtio_probe(struct virtio_device *vdev) chan->vdev = vdev;/* We expect one virtqueue, for requests. */ - chan->vq = vdev->config->find_vq (vdev,0 , req_done,"requests" ); + chan->vq =virtio_find_single_vq (vdev, req_done,"requests" );if (IS_ERR (chan->vq)) { err =PTR_ERR (chan->vq); goto out_free_vq;
Signature · 1 line
1.6.3.rc3.1.g830204
Michael S. Tsirkin
maintainerPATCH v5 2/3 virtio_pci: split up vp_interrupt
In reply to a message not in the archiveThis reorganizes virtio-pci code in vp_interrupt slightly, so that it's easier to add per-vq MSI support on top.
--- drivers/virtio/virtio_pci.c | 53 +++++++++++++++++++++++++++--------------- 1 files changed, 34 insertions(+), 19 deletions(-)
drivers/virtio/virtio_pci.c +34 -19
@@ -164,6 +164,37 @@ static void vp_notify(struct virtqueue *vq)iowrite16 (info->queue_index, vp_dev->ioaddr +VIRTIO_PCI_QUEUE_NOTIFY ); } +/* Handle a configuration change: Tell driver if it wants to know. */ +static irqreturn_t vp_config_changed (int irq,void *opaque) +{ +struct virtio_pci_device *vp_dev = opaque; +struct virtio_driver *drv; + drv =container_of (vp_dev->vdev.dev.driver, + struct virtio_driver, driver); + +if (drv && drv->config_changed) + drv->config_changed (&vp_dev->vdev); +return IRQ_HANDLED ; +} + +/* Notify all virtqueues on an interrupt. */ +static irqreturn_t vp_vring_interrupt (int irq,void *opaque) +{ +struct virtio_pci_device *vp_dev = opaque; +struct virtio_pci_vq_info *info; +irqreturn_t ret =IRQ_NONE ; +unsigned long flags; + +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 ; + } +spin_unlock_irqrestore (&vp_dev->lock, flags); + +return ret; +} +/* A small wrapper to also acknowledge the interrupt when it's handled. * I really need an EIO hook for the vring so I can ack the interrupt once we * know that we'll be handling the IRQ but before we invoke the callback since
@@ -173,9 +204,6 @@ static void vp_notify(struct virtqueue *vq)static irqreturn_t vp_interrupt (int irq,void *opaque) {struct virtio_pci_device *vp_dev = opaque; -struct virtio_pci_vq_info *info; -irqreturn_t ret =IRQ_NONE ; -unsigned long flags;u8 isr;/* reading the ISR has the effect of also clearing it so it's very
@@ -187,23 +215,10 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)return IRQ_NONE ;/* Configuration change? Tell driver if it wants to know. */ -if (isr &VIRTIO_PCI_ISR_CONFIG ) { -struct virtio_driver *drv; - drv =container_of (vp_dev->vdev.dev.driver, - struct virtio_driver, driver); - -if (drv && drv->config_changed) - drv->config_changed (&vp_dev->vdev); - } +if (isr &VIRTIO_PCI_ISR_CONFIG ) +vp_config_changed (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 ; - } -spin_unlock_irqrestore (&vp_dev->lock, flags); - -return ret; +return vp_vring_interrupt (irq, opaque); }/* the config->find_vq() implementation */
Signature · 1 line
1.6.3.rc3.1.g830204
Michael S. Tsirkin
maintainerPATCH v5 1/3 virtio: find_vqs/del_vqs virtio operations
In reply to a message not in the archiveThis replaces find_vq/del_vq with find_vqs/del_vqs virtio operations, and updates all drivers. This is needed for MSI support, because MSI needs to know the total number of vectors upfront.
--- drivers/block/virtio_blk.c | 6 ++-- drivers/char/hw_random/virtio-rng.c | 6 ++-- drivers/char/virtio_console.c | 26 ++++++++----------- drivers/lguest/lguest_device.c | 36 +++++++++++++++++++++++++- drivers/net/virtio_net.c | 45 +++++++++++++-------------------- drivers/s390/kvm/kvm_virtio.c | 36 +++++++++++++++++++++++++- drivers/virtio/virtio_balloon.c | 27 ++++++++------------ drivers/virtio/virtio_pci.c | 37 ++++++++++++++++++++++----- include/linux/virtio_config.h | 46 ++++++++++++++++++++++++++-------- net/9p/trans_virtio.c | 2 +- 10 files changed, 180 insertions(+), 87 deletions(-)
drivers/block/virtio_blk.c +3 -3
@@ -224,7 +224,7 @@ static int virtblk_probe(struct virtio_device *vdev)sg_init_table (vblk->sg, vblk->sg_elems);/* We expect one virtqueue, for output. */ - vblk->vq = vdev->config->find_vq (vdev,0 , blk_done,"requests" ); + vblk->vq =virtio_find_single_vq (vdev, blk_done,"requests" );if (IS_ERR (vblk->vq)) { err =PTR_ERR (vblk->vq); goto out_free_vblk;
@@ -323,7 +323,7 @@ out_put_disk: out_mempool:mempool_destroy (vblk->pool); out_free_vq: - vdev->config->del_vq (vblk->vq); + vdev->config->del_vqs (vdev); out_free_vblk:kfree (vblk); out:
@@ -344,7 +344,7 @@ static void virtblk_remove(struct virtio_device *vdev)blk_cleanup_queue (vblk->disk->queue);put_disk (vblk->disk);mempool_destroy (vblk->pool); - vdev->config->del_vq (vblk->vq); + vdev->config->del_vqs (vdev);kfree (vblk); }
drivers/char/hw_random/virtio-rng.c +3 -3
@@ -94,13 +94,13 @@ static int virtrng_probe(struct virtio_device *vdev)int err;/* We expect a single virtqueue. */ - vq = vdev->config->find_vq (vdev,0 , random_recv_done,"input" ); + vq =virtio_find_single_vq (vdev, random_recv_done,"input" );if (IS_ERR (vq))return PTR_ERR (vq); err =hwrng_register (&virtio_hwrng);if (err) { - vdev->config->del_vq (vq); + vdev->config->del_vqs (vdev);return err; }
@@ -112,7 +112,7 @@ static void virtrng_remove(struct virtio_device *vdev) { vdev->config->reset (vdev);hwrng_unregister (&virtio_hwrng); - vdev->config->del_vq (vq); + vdev->config->del_vqs (vdev); }static struct virtio_device_id id_table[] = {
drivers/char/virtio_console.c +11 -15
@@ -188,6 +188,9 @@ static void hvc_handle_input(struct virtqueue *vq)* Finally we put our input buffer in the input queue, ready to receive. */ static int __devinit virtcons_probe (struct virtio_device *dev) { +vq_callback_t *callbacks[] = { hvc_handle_input,NULL }; +const char *names[] = {"input" ,"output" }; +struct virtqueue *vqs[2 ];int err; vdev = dev;
@@ -199,20 +202,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev) goto fail; } -/* Find the input queue. */ +/* Find the queues. */ /* FIXME: This is why we want to wean off hvc: we do nothing * when input comes in. */ - in_vq = vdev->config->find_vq (vdev,0 , hvc_handle_input,"input" ); -if (IS_ERR (in_vq)) { - err =PTR_ERR (in_vq); + err = vdev->config->find_vqs (vdev,2 , vqs, callbacks, names); +if (err) goto free; - } - out_vq = vdev->config->find_vq (vdev,1 ,NULL ,"output" ); -if (IS_ERR (out_vq)) { - err =PTR_ERR (out_vq); - goto free_in_vq; - } + in_vq = vqs[0 ]; + out_vq = vqs[1 ];/* Start using the new console output. */ virtio_cons.get_chars = get_chars;
@@ -233,17 +231,15 @@ static int __devinit virtcons_probe(struct virtio_device *dev) hvc =hvc_alloc (0 ,0 , &virtio_cons,PAGE_SIZE );if (IS_ERR (hvc)) { err =PTR_ERR (hvc); - goto free_out_vq; + goto free_vqs; }/* Register the input buffer the first time. */ add_inbuf ();return 0 ; -free_out_vq: - vdev->config->del_vq (out_vq); -free_in_vq: - vdev->config->del_vq (in_vq); +free_vqs: + vdev->config->del_vqs (vdev); free:kfree (inbuf); fail:
drivers/lguest/lguest_device.c +34 -2
@@ -313,6 +313,38 @@ static void lg_del_vq(struct virtqueue *vq)kfree (lvq); } +static void lg_del_vqs (struct virtio_device *vdev) +{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +kvm_del_vq (vq); +} + +static int lg_find_vqs (struct virtio_device *vdev,unsigned nvqs, + struct virtqueue *vqs[] + vq_callback_t *callbacks[], +const char *names[]) +{ +struct lguest_device *ldev =to_lgdev (vdev); +int i; + +/* We must have this many virtqueues. */ +if (nvqs > ldev->desc->num_vq) +return ERR_PTR (-ENOENT ); + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =lg_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +vp_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +/* The ops structure which hooks everything together. */ static struct virtio_config_ops lguest_config_ops = { .get_features = lg_get_features,
@@ -322,8 +354,8 @@ static struct virtio_config_ops lguest_config_ops = { .get_status = lg_get_status, .set_status = lg_set_status, .reset = lg_reset, - .find_vq = lg_find_vq, - .del_vq = lg_del_vq, + .find_vqs = lg_find_vqs, + .del_vqs = lg_del_vqs, };/* The root device for the lguest virtio devices. This makes them appear as
drivers/net/virtio_net.c +18 -27
81 lines not on this page · raw message
drivers/s390/kvm/kvm_virtio.c +34 -2
@@ -227,6 +227,38 @@ static void kvm_del_vq(struct virtqueue *vq) KVM_S390_VIRTIO_RING_ALIGN)); } +static void vp_del_vqs (struct virtio_device *vdev) +{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +kvm_del_vq (vq); +} + +static int kvm_find_vqs (struct virtio_device *vdev,unsigned nvqs, +struct virtqueue *vqs[], +vq_callback_t *callbacks[], +const char *names[]) +{ +struct kvm_device *kdev =to_kvmdev (vdev); +int i; + +/* We must have this many virtqueues. */ +if (nvqs > kdev->desc->num_vq) +return -ENOENT ; + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =kvm_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +kvm_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +/* * The config ops structure as defined by virtio config */
@@ -238,8 +270,8 @@ static struct virtio_config_ops kvm_vq_configspace_ops = { .get_status = kvm_get_status, .set_status = kvm_set_status, .reset = kvm_reset, - .find_vq = kvm_find_vq, - .del_vq = kvm_del_vq, + .find_vqs = kvm_find_vqs, + .del_vqs = kvm_del_vqs, };/*
drivers/virtio/virtio_balloon.c +11 -16
@@ -204,6 +204,9 @@ static int balloon(void *_vballoon)static int virtballoon_probe (struct virtio_device *vdev) {struct virtio_balloon *vb; +struct virtqueue *vqs[2 ]; +vq_callback_t *callbacks[] = { balloon_ack, balloon_ack }; +const char *names[] = {"inflate" ,"deflate" };int err; vdev->priv = vb =kmalloc (sizeof (*vb),GFP_KERNEL );
@@ -218,22 +221,17 @@ static int virtballoon_probe(struct virtio_device *vdev) vb->vdev = vdev;/* We expect two virtqueues. */ - vb->inflate_vq = vdev->config->find_vq (vdev,0 , balloon_ack,"inflate" ); -if (IS_ERR (vb->inflate_vq)) { - err =PTR_ERR (vb->inflate_vq); + err = vdev->config->find_vqs (vdev,2 , vqs, callbacks, names); +if (err) goto out_free_vb; - } - vb->deflate_vq = vdev->config->find_vq (vdev,1 , balloon_ack,"deflate" ); -if (IS_ERR (vb->deflate_vq)) { - err =PTR_ERR (vb->deflate_vq); - goto out_del_inflate_vq; - } + vb->inflate_vq = vqs[0 ]; + vb->deflate_vq = vqs[1 ]; vb->thread =kthread_run (balloon, vb,"vballoon" );if (IS_ERR (vb->thread)) { err =PTR_ERR (vb->thread); - goto out_del_deflate_vq; + goto out_del_vqs; } vb->tell_host_first
@@ -241,10 +239,8 @@ static int virtballoon_probe(struct virtio_device *vdev)return 0 ; -out_del_deflate_vq: - vdev->config->del_vq (vb->deflate_vq); -out_del_inflate_vq: - vdev->config->del_vq (vb->inflate_vq); +out_del_vqs: + vdev->config->del_vqs (vdev); out_free_vb:kfree (vb); out:
@@ -264,8 +260,7 @@ static void virtballoon_remove(struct virtio_device *vdev)/* Now we reset the device so we can clean up the queues. */ vdev->config->reset (vdev); - vdev->config->del_vq (vb->deflate_vq); - vdev->config->del_vq (vb->inflate_vq); + vdev->config->del_vqs (vdev);kfree (vb); }
drivers/virtio/virtio_pci.c +30 -7
@@ -276,11 +276,7 @@ static void vp_del_vq(struct virtqueue *vq) {struct virtio_pci_device *vp_dev =to_vp_device (vq->vdev);struct virtio_pci_vq_info *info = vq->priv; -unsigned long flags, size; - -spin_lock_irqsave (&vp_dev->lock, flags); -list_del (&info->node); -spin_unlock_irqrestore (&vp_dev->lock, flags); +unsigned long size;vring_del_virtqueue (vq);
@@ -293,14 +289,41 @@ static void vp_del_vq(struct virtqueue *vq)kfree (info); } +static void vp_del_vqs (struct virtio_device *vdev) +{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +vp_del_vq (vq); +} + +static int vp_find_vqs (struct virtio_device *vdev,unsigned nvqs, +struct virtqueue *vqs[], +vq_callback_t *callbacks[], +const char *names[]) +{ +int i; + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =vp_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +vp_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +static struct virtio_config_ops virtio_pci_config_ops = { .get = vp_get, .set = vp_set, .get_status = vp_get_status, .set_status = vp_set_status, .reset = vp_reset, - .find_vq = vp_find_vq, - .del_vq = vp_del_vq, + .find_vqs = vp_find_vqs, + .del_vqs = vp_del_vqs, .get_features = vp_get_features, .finalize_features = vp_finalize_features, };
include/linux/virtio_config.h +35 -11
73 lines not on this page · raw message
net/9p/trans_virtio.c +1 -1
@@ -246,7 +246,7 @@ static int p9_virtio_probe(struct virtio_device *vdev) chan->vdev = vdev;/* We expect one virtqueue, for requests. */ - chan->vq = vdev->config->find_vq (vdev,0 , req_done,"requests" ); + chan->vq =virtio_find_single_vq (vdev, req_done,"requests" );if (IS_ERR (chan->vq)) { err =PTR_ERR (chan->vq); goto out_free_vq;
Signature · 1 line
1.6.3.rc3.1.g830204
Michael S. Tsirkin
maintainerPATCH v5 3/3 virtio_pci: optional MSI-X support
In reply to a message not in the archiveThis 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.
--- drivers/virtio/virtio_pci.c | 227 +++++++++++++++++++++++++++++++++++++++---- include/linux/virtio_pci.h | 10 ++- 2 files changed, 217 insertions(+), 20 deletions(-)
drivers/virtio/virtio_pci.c +208 -19
@@ -42,6 +42,26 @@ 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 available vectors */ +unsigned msix_vectors; +/* Vectors allocated */ +unsigned msix_used_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 , };struct virtio_pci_vq_info
@@ -60,6 +80,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 +132,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 +147,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 +246,121 @@ 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_used_vectors; ++i) +free_irq (vp_dev->msix_entries[i].vector, vp_dev); + vp_dev->msix_used_vectors =0 ; + +if (vp_dev->msix_enabled) { +/* Disable the vector used for configuration */ +iowrite16 (VIRTIO_MSI_NO_VECTOR , + vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); +/* Flush the write out to device */ +ioread16 (vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); + + vp_dev->msix_enabled =0 ; +pci_disable_msix (vp_dev->pci_dev); + } +} + +static int vp_enable_msix (struct pci_dev *dev,struct msix_entry *entries, +int *options,int noptions) +{ +int i; +for (i =0 ; i < noptions; ++i) +if (!pci_enable_msix (dev, entries, options[i])) +return options[i]; +return -EBUSY ; +} + +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, v; +int err = -ENOMEM ; +/* We want at most one vector per queue and one for config changes. +* Fallback to separate vectors for config and a shared for queues. +* Finally fall back to regular interrupts. */ +int options[] = { max_vqs +1 ,2 }; +int nvectors =max (options[0 ], options[1 ]); + + vp_dev->msix_entries =kmalloc (nvectors *sizeof *vp_dev->msix_entries, +GFP_KERNEL ); +if (!vp_dev->msix_entries) + goto error_entries; + vp_dev->msix_names =kmalloc (nvectors *sizeof *vp_dev->msix_names, +GFP_KERNEL ); +if (!vp_dev->msix_names) + goto error_names; + +for (i =0 ; i < nvectors; ++i) + vp_dev->msix_entries[i].entry = i; + + err =vp_enable_msix (vp_dev->pci_dev, vp_dev->msix_entries, + options,ARRAY_SIZE (options)); +if (err <0 ) { +/* Can't allocate enough MSI-X vectors, use regular interrupt */ + vp_dev->msix_vectors =0 ; + err =request_irq (vp_dev->pci_dev->irq, vp_interrupt, +IRQF_SHARED , name, vp_dev); +if (err) + goto error_irq; + vp_dev->intx_enabled =1 ; + }else { + vp_dev->msix_vectors = err; + vp_dev->msix_enabled =1 ; + +/* Set the vector used for configuration */ + v = vp_dev->msix_used_vectors; +snprintf (vp_dev->msix_names[v],sizeof *vp_dev->msix_names, +"%s-config" , name); + err =request_irq (vp_dev->msix_entries[v].vector, + vp_config_changed,0 , vp_dev->msix_names[v], + vp_dev); +if (err) + goto error_irq; + ++vp_dev->msix_used_vectors; + +iowrite16 (v, vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); +/* Verify we had enough resources to assign the vector */ + v =ioread16 (vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); +if (v ==VIRTIO_MSI_NO_VECTOR ) { + err = -EBUSY ; + goto error_irq; + } + } + +if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs +1 ) { +/* Shared vector for all VQs */ + v = vp_dev->msix_used_vectors; +snprintf (vp_dev->msix_names[v],sizeof *vp_dev->msix_names, +"%s-virtqueues" , name); + err =request_irq (vp_dev->msix_entries[v].vector, + vp_vring_interrupt,0 , vp_dev->msix_names[v], + vp_dev); +if (err) + goto error_irq; + ++vp_dev->msix_used_vectors; + } +return 0 ; +error_irq: +vp_free_vectors (vdev); +kfree (vp_dev->msix_names); +error_names: +kfree (vp_dev->msix_entries); +error_entries: +return err; +} +static struct virtqueue *vp_find_vq (struct virtio_device *vdev,unsigned index,void (*callback)(struct virtqueue *vq),const char *name)
@@ -230,7 +369,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,struct virtio_pci_vq_info *info;struct virtqueue *vq;unsigned long flags, size; -u16 num; +u16 num, vector;int err;/* Select the queue we're interested in */
@@ -249,6 +388,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, info->queue_index = index; info->num = num; + info->vector =VIRTIO_MSI_NO_VECTOR ; size =PAGE_ALIGN (vring_size (num,VIRTIO_PCI_VRING_ALIGN )); info->queue =alloc_pages_exact (size,GFP_KERNEL |__GFP_ZERO);
@@ -272,12 +412,43 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, vq->priv = info; info->vq = vq; +/* allocate per-vq vector if available and necessary */ +if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) { + vector = vp_dev->msix_used_vectors; +snprintf (vp_dev->msix_names[vector],sizeof *vp_dev->msix_names, +"%s-%s" ,dev_name (&vp_dev->vdev.dev), name); + err =request_irq (vp_dev->msix_entries[vector].vector, + vring_interrupt,0 , + vp_dev->msix_names[vector], vq); +if (err) + goto out_request_irq; + info->vector = vector; + ++vp_dev->msix_used_vectors; + }else + vector =VP_MSIX_VQ_VECTOR ; + +if (callback && vp_dev->msix_enabled) { +iowrite16 (vector, vp_dev->ioaddr +VIRTIO_MSI_QUEUE_VECTOR ); + vector =ioread16 (vp_dev->ioaddr +VIRTIO_MSI_QUEUE_VECTOR ); +if (vector ==VIRTIO_MSI_NO_VECTOR ) { + err = -EBUSY ; + goto out_assign; + } + } +spin_lock_irqsave (&vp_dev->lock, flags);list_add (&info->node, &vp_dev->virtqueues);spin_unlock_irqrestore (&vp_dev->lock, flags);return vq; +out_assign: +if (info->vector !=VIRTIO_MSI_NO_VECTOR ) { +free_irq (vp_dev->msix_entries[info->vector].vector, vq); + --vp_dev->msix_used_vectors; + } +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);
@@ -286,17 +457,27 @@ out_info:return ERR_PTR (err); } -/* the config->del_vq() implementation */ static void vp_del_vq (struct virtqueue *vq) {struct virtio_pci_device *vp_dev =to_vp_device (vq->vdev);struct virtio_pci_vq_info *info = vq->priv;unsigned long size; +iowrite16 (info->queue_index, vp_dev->ioaddr +VIRTIO_PCI_QUEUE_SEL ); + +if (info->vector !=VIRTIO_MSI_NO_VECTOR ) +free_irq (vp_dev->msix_entries[info->vector].vector, vq); + +if (vp_dev->msix_enabled) { +iowrite16 (VIRTIO_MSI_NO_VECTOR , + vp_dev->ioaddr +VIRTIO_MSI_QUEUE_VECTOR ); +/* Flush the write out to device */ +ioread8 (vp_dev->ioaddr +VIRTIO_PCI_ISR ); + } +vring_del_virtqueue (vq);/* Select and deactivate the queue */ -iowrite16 (info->queue_index, vp_dev->ioaddr +VIRTIO_PCI_QUEUE_SEL );iowrite32 (0 , vp_dev->ioaddr +VIRTIO_PCI_QUEUE_PFN ); size =PAGE_ALIGN (vring_size (info->num,VIRTIO_PCI_VRING_ALIGN ));
@@ -304,30 +485,46 @@ static void vp_del_vq(struct virtqueue *vq)kfree (info); } +/* the config->del_vqs() implementation */ static void vp_del_vqs (struct virtio_device *vdev) {struct virtqueue *vq, *n;list_for_each_entry_safe (vq, n, &vdev->vqs, list)vp_del_vq (vq); + +vp_free_vectors (vdev); } +/* 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[]) { -int i; +int vectors =0 ; +int i, err; + +/* How many vectors would we like? */ +for (i =0 ; i < nvqs; ++i) +if (callbacks[i]) + ++vectors; + + err =vp_request_vectors (vdev, vectors); +if (err) + goto error_request;for (i =0 ; i < nvqs; ++i) { vqs[i] =vp_find_vq (vdev, i, callbacks[i], names[i]);if (IS_ERR (vqs[i])) - goto error; + goto error_find; }return 0 ; -error: +error_find:vp_del_vqs (vdev); + +error_request:return PTR_ERR (vqs[i]); }
@@ -349,7 +546,7 @@ static void virtio_pci_release_dev(struct device *_d)struct virtio_pci_device *vp_dev =to_vp_device (dev);struct pci_dev *pci_dev = vp_dev->pci_dev; -free_irq (pci_dev->irq, vp_dev); +vp_del_vqs (dev);pci_set_drvdata (pci_dev,NULL );pci_iounmap (pci_dev, vp_dev->ioaddr);pci_release_regions (pci_dev);
@@ -408,21 +605,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);
include/linux/virtio_pci.h +9 -1
@@ -47,9 +47,17 @@/* 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_VECTOR 20 +/* A 16-bit vector for selected queue notifications. */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 +/* Vector value used to disable MSI for queue */ +#define VIRTIO_MSI_NO_VECTOR 0xffff +/* 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
Signature · 1 line
1.6.3.rc3.1.g830204
Michael S. Tsirkin
maintainerPATCH v5 2/3 virtio_pci: split up vp_interrupt
In reply to a message not in the archiveThis reorganizes virtio-pci code in vp_interrupt slightly, so that it's easier to add per-vq MSI support on top.
--- drivers/virtio/virtio_pci.c | 53 +++++++++++++++++++++++++++--------------- 1 files changed, 34 insertions(+), 19 deletions(-)
drivers/virtio/virtio_pci.c +34 -19
@@ -164,6 +164,37 @@ static void vp_notify(struct virtqueue *vq)iowrite16 (info->queue_index, vp_dev->ioaddr +VIRTIO_PCI_QUEUE_NOTIFY ); } +/* Handle a configuration change: Tell driver if it wants to know. */ +static irqreturn_t vp_config_changed (int irq,void *opaque) +{ +struct virtio_pci_device *vp_dev = opaque; +struct virtio_driver *drv; + drv =container_of (vp_dev->vdev.dev.driver, + struct virtio_driver, driver); + +if (drv && drv->config_changed) + drv->config_changed (&vp_dev->vdev); +return IRQ_HANDLED ; +} + +/* Notify all virtqueues on an interrupt. */ +static irqreturn_t vp_vring_interrupt (int irq,void *opaque) +{ +struct virtio_pci_device *vp_dev = opaque; +struct virtio_pci_vq_info *info; +irqreturn_t ret =IRQ_NONE ; +unsigned long flags; + +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 ; + } +spin_unlock_irqrestore (&vp_dev->lock, flags); + +return ret; +} +/* A small wrapper to also acknowledge the interrupt when it's handled. * I really need an EIO hook for the vring so I can ack the interrupt once we * know that we'll be handling the IRQ but before we invoke the callback since
@@ -173,9 +204,6 @@ static void vp_notify(struct virtqueue *vq)static irqreturn_t vp_interrupt (int irq,void *opaque) {struct virtio_pci_device *vp_dev = opaque; -struct virtio_pci_vq_info *info; -irqreturn_t ret =IRQ_NONE ; -unsigned long flags;u8 isr;/* reading the ISR has the effect of also clearing it so it's very
@@ -187,23 +215,10 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)return IRQ_NONE ;/* Configuration change? Tell driver if it wants to know. */ -if (isr &VIRTIO_PCI_ISR_CONFIG ) { -struct virtio_driver *drv; - drv =container_of (vp_dev->vdev.dev.driver, - struct virtio_driver, driver); - -if (drv && drv->config_changed) - drv->config_changed (&vp_dev->vdev); - } +if (isr &VIRTIO_PCI_ISR_CONFIG ) +vp_config_changed (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 ; - } -spin_unlock_irqrestore (&vp_dev->lock, flags); - -return ret; +return vp_vring_interrupt (irq, opaque); }/* the config->find_vq() implementation */
Signature · 1 line
1.6.3.rc3.1.g830204
Michael S. Tsirkin
maintainerPATCH v5 3/3 virtio_pci: optional MSI-X support
In reply to a message not in the archiveThis 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.
--- drivers/virtio/virtio_pci.c | 227 +++++++++++++++++++++++++++++++++++++++---- include/linux/virtio_pci.h | 10 ++- 2 files changed, 217 insertions(+), 20 deletions(-)
drivers/virtio/virtio_pci.c +208 -19
@@ -42,6 +42,26 @@ 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 available vectors */ +unsigned msix_vectors; +/* Vectors allocated */ +unsigned msix_used_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 , };struct virtio_pci_vq_info
@@ -60,6 +80,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 +132,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 +147,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 +246,121 @@ 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_used_vectors; ++i) +free_irq (vp_dev->msix_entries[i].vector, vp_dev); + vp_dev->msix_used_vectors =0 ; + +if (vp_dev->msix_enabled) { +/* Disable the vector used for configuration */ +iowrite16 (VIRTIO_MSI_NO_VECTOR , + vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); +/* Flush the write out to device */ +ioread16 (vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); + + vp_dev->msix_enabled =0 ; +pci_disable_msix (vp_dev->pci_dev); + } +} + +static int vp_enable_msix (struct pci_dev *dev,struct msix_entry *entries, +int *options,int noptions) +{ +int i; +for (i =0 ; i < noptions; ++i) +if (!pci_enable_msix (dev, entries, options[i])) +return options[i]; +return -EBUSY ; +} + +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, v; +int err = -ENOMEM ; +/* We want at most one vector per queue and one for config changes. +* Fallback to separate vectors for config and a shared for queues. +* Finally fall back to regular interrupts. */ +int options[] = { max_vqs +1 ,2 }; +int nvectors =max (options[0 ], options[1 ]); + + vp_dev->msix_entries =kmalloc (nvectors *sizeof *vp_dev->msix_entries, +GFP_KERNEL ); +if (!vp_dev->msix_entries) + goto error_entries; + vp_dev->msix_names =kmalloc (nvectors *sizeof *vp_dev->msix_names, +GFP_KERNEL ); +if (!vp_dev->msix_names) + goto error_names; + +for (i =0 ; i < nvectors; ++i) + vp_dev->msix_entries[i].entry = i; + + err =vp_enable_msix (vp_dev->pci_dev, vp_dev->msix_entries, + options,ARRAY_SIZE (options)); +if (err <0 ) { +/* Can't allocate enough MSI-X vectors, use regular interrupt */ + vp_dev->msix_vectors =0 ; + err =request_irq (vp_dev->pci_dev->irq, vp_interrupt, +IRQF_SHARED , name, vp_dev); +if (err) + goto error_irq; + vp_dev->intx_enabled =1 ; + }else { + vp_dev->msix_vectors = err; + vp_dev->msix_enabled =1 ; + +/* Set the vector used for configuration */ + v = vp_dev->msix_used_vectors; +snprintf (vp_dev->msix_names[v],sizeof *vp_dev->msix_names, +"%s-config" , name); + err =request_irq (vp_dev->msix_entries[v].vector, + vp_config_changed,0 , vp_dev->msix_names[v], + vp_dev); +if (err) + goto error_irq; + ++vp_dev->msix_used_vectors; + +iowrite16 (v, vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); +/* Verify we had enough resources to assign the vector */ + v =ioread16 (vp_dev->ioaddr +VIRTIO_MSI_CONFIG_VECTOR ); +if (v ==VIRTIO_MSI_NO_VECTOR ) { + err = -EBUSY ; + goto error_irq; + } + } + +if (vp_dev->msix_vectors && vp_dev->msix_vectors != max_vqs +1 ) { +/* Shared vector for all VQs */ + v = vp_dev->msix_used_vectors; +snprintf (vp_dev->msix_names[v],sizeof *vp_dev->msix_names, +"%s-virtqueues" , name); + err =request_irq (vp_dev->msix_entries[v].vector, + vp_vring_interrupt,0 , vp_dev->msix_names[v], + vp_dev); +if (err) + goto error_irq; + ++vp_dev->msix_used_vectors; + } +return 0 ; +error_irq: +vp_free_vectors (vdev); +kfree (vp_dev->msix_names); +error_names: +kfree (vp_dev->msix_entries); +error_entries: +return err; +} +static struct virtqueue *vp_find_vq (struct virtio_device *vdev,unsigned index,void (*callback)(struct virtqueue *vq),const char *name)
@@ -230,7 +369,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,struct virtio_pci_vq_info *info;struct virtqueue *vq;unsigned long flags, size; -u16 num; +u16 num, vector;int err;/* Select the queue we're interested in */
@@ -249,6 +388,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, info->queue_index = index; info->num = num; + info->vector =VIRTIO_MSI_NO_VECTOR ; size =PAGE_ALIGN (vring_size (num,VIRTIO_PCI_VRING_ALIGN )); info->queue =alloc_pages_exact (size,GFP_KERNEL |__GFP_ZERO);
@@ -272,12 +412,43 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, vq->priv = info; info->vq = vq; +/* allocate per-vq vector if available and necessary */ +if (callback && vp_dev->msix_used_vectors < vp_dev->msix_vectors) { + vector = vp_dev->msix_used_vectors; +snprintf (vp_dev->msix_names[vector],sizeof *vp_dev->msix_names, +"%s-%s" ,dev_name (&vp_dev->vdev.dev), name); + err =request_irq (vp_dev->msix_entries[vector].vector, + vring_interrupt,0 , + vp_dev->msix_names[vector], vq); +if (err) + goto out_request_irq; + info->vector = vector; + ++vp_dev->msix_used_vectors; + }else + vector =VP_MSIX_VQ_VECTOR ; + +if (callback && vp_dev->msix_enabled) { +iowrite16 (vector, vp_dev->ioaddr +VIRTIO_MSI_QUEUE_VECTOR ); + vector =ioread16 (vp_dev->ioaddr +VIRTIO_MSI_QUEUE_VECTOR ); +if (vector ==VIRTIO_MSI_NO_VECTOR ) { + err = -EBUSY ; + goto out_assign; + } + } +spin_lock_irqsave (&vp_dev->lock, flags);list_add (&info->node, &vp_dev->virtqueues);spin_unlock_irqrestore (&vp_dev->lock, flags);return vq; +out_assign: +if (info->vector !=VIRTIO_MSI_NO_VECTOR ) { +free_irq (vp_dev->msix_entries[info->vector].vector, vq); + --vp_dev->msix_used_vectors; + } +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);
@@ -286,17 +457,27 @@ out_info:return ERR_PTR (err); } -/* the config->del_vq() implementation */ static void vp_del_vq (struct virtqueue *vq) {struct virtio_pci_device *vp_dev =to_vp_device (vq->vdev);struct virtio_pci_vq_info *info = vq->priv;unsigned long size; +iowrite16 (info->queue_index, vp_dev->ioaddr +VIRTIO_PCI_QUEUE_SEL ); + +if (info->vector !=VIRTIO_MSI_NO_VECTOR ) +free_irq (vp_dev->msix_entries[info->vector].vector, vq); + +if (vp_dev->msix_enabled) { +iowrite16 (VIRTIO_MSI_NO_VECTOR , + vp_dev->ioaddr +VIRTIO_MSI_QUEUE_VECTOR ); +/* Flush the write out to device */ +ioread8 (vp_dev->ioaddr +VIRTIO_PCI_ISR ); + } +vring_del_virtqueue (vq);/* Select and deactivate the queue */ -iowrite16 (info->queue_index, vp_dev->ioaddr +VIRTIO_PCI_QUEUE_SEL );iowrite32 (0 , vp_dev->ioaddr +VIRTIO_PCI_QUEUE_PFN ); size =PAGE_ALIGN (vring_size (info->num,VIRTIO_PCI_VRING_ALIGN ));
@@ -304,30 +485,46 @@ static void vp_del_vq(struct virtqueue *vq)kfree (info); } +/* the config->del_vqs() implementation */ static void vp_del_vqs (struct virtio_device *vdev) {struct virtqueue *vq, *n;list_for_each_entry_safe (vq, n, &vdev->vqs, list)vp_del_vq (vq); + +vp_free_vectors (vdev); } +/* 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[]) { -int i; +int vectors =0 ; +int i, err; + +/* How many vectors would we like? */ +for (i =0 ; i < nvqs; ++i) +if (callbacks[i]) + ++vectors; + + err =vp_request_vectors (vdev, vectors); +if (err) + goto error_request;for (i =0 ; i < nvqs; ++i) { vqs[i] =vp_find_vq (vdev, i, callbacks[i], names[i]);if (IS_ERR (vqs[i])) - goto error; + goto error_find; }return 0 ; -error: +error_find:vp_del_vqs (vdev); + +error_request:return PTR_ERR (vqs[i]); }
@@ -349,7 +546,7 @@ static void virtio_pci_release_dev(struct device *_d)struct virtio_pci_device *vp_dev =to_vp_device (dev);struct pci_dev *pci_dev = vp_dev->pci_dev; -free_irq (pci_dev->irq, vp_dev); +vp_del_vqs (dev);pci_set_drvdata (pci_dev,NULL );pci_iounmap (pci_dev, vp_dev->ioaddr);pci_release_regions (pci_dev);
@@ -408,21 +605,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);
include/linux/virtio_pci.h +9 -1
@@ -47,9 +47,17 @@/* 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_VECTOR 20 +/* A 16-bit vector for selected queue notifications. */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 +/* Vector value used to disable MSI for queue */ +#define VIRTIO_MSI_NO_VECTOR 0xffff +/* 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
Signature · 1 line
1.6.3.rc3.1.g830204
Re: [PATCHv5 3/3] virtio_pci: optional MSI-X support
Quoted 8 lines · 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 <[email protected]>
Regards,
Anthony Liguori
Re: [PATCHv5 3/3] virtio_pci: optional MSI-X support
In reply to Michael S. TsirkinQuoted 8 lines · 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 <[email protected]>
Regards,
Anthony Liguori
Am Wednesday 13 May 2009 21:08:58 schrieb Michael S. Tsirkin:
Quoted 3 lines
This replaces find_vq/del_vq with find_vqs/del_vqs virtio operations, and updates all drivers. This is needed for MSI support, because MSI needs to know the total number of vectors upfront.
[...]
drivers/s390/kvm/kvm_virtio.c static void kvm_del_vq(struct virtqueue *vq) · line 230
Quoted 7 lines
drivers/s390/kvm/kvm_virtio.c +1
-0@@ -227,6 +227,38 @@ static void kvm_del_vq(struct virtqueue *vq) KVM_S390_VIRTIO_RING_ALIGN)); } +static void vp_del_vqs (struct virtio_device *vdev)
s/vp_del_vqs/kvm_del_vqs/ :
drivers/s390/kvm/kvm_virtio.c: In function 'kvm_find_vqs':drivers/s390/kvm/kvm_virtio.c:258: error: implicit declaration of function 'kvm_del_vqs' drivers/s390/kvm/kvm_virtio.c: At top level:drivers/s390/kvm/kvm_virtio.c:274: error: 'kvm_del_vqs' undeclared here (not in a function) make[2]: *** [drivers/s390/kvm/kvm_virtio.o] Error 1
include/linux/virtio_config.h
Quoted 49 lines
drivers/s390/kvm/kvm_virtio.c +33
-2+{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +kvm_del_vq (vq); +} + +static int kvm_find_vqs (struct virtio_device *vdev,unsigned nvqs, +struct virtqueue *vqs[], +vq_callback_t *callbacks[], +const char *names[]) +{ +struct kvm_device *kdev =to_kvmdev (vdev); +int i; + +/* We must have this many virtqueues. */ +if (nvqs > kdev->desc->num_vq) +return -ENOENT ; + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =kvm_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +kvm_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +/* * The config ops structure as defined by virtio config */ @@ -238,8 +270,8 @@ static struct virtio_config_ops kvm_vq_configspace_ops = { .get_status = kvm_get_status, .set_status = kvm_set_status, .reset = kvm_reset, - .find_vq = kvm_find_vq, - .del_vq = kvm_del_vq, + .find_vqs = kvm_find_vqs, + .del_vqs = kvm_del_vqs, };/* include/linux/virtio_config.h +0
-0
[...]
needs an
#ifdef __KERNEL__
+#include <linux/err.h>
#include <linux/virtio.h>
[...]
include/linux/virtio_config.h static inline int virtio_config_buf(struct virtio_device *vdev, · line 152
Quoted 3 lines
include/linux/virtio_config.h +3
-0+int err = vdev->config->find_vqs (vdev,1 , &vq, callbacks, names); +if (err <0 ) +return ERR_PTR (err);
Otherwise ERR_PTR does not compile.
Am Wednesday 13 May 2009 21:08:58 schrieb Michael S. Tsirkin:
Quoted 3 lines
This replaces find_vq/del_vq with find_vqs/del_vqs virtio operations, and updates all drivers. This is needed for MSI support, because MSI needs to know the total number of vectors upfront.
[...]
drivers/s390/kvm/kvm_virtio.c static void kvm_del_vq(struct virtqueue *vq) · line 230
Quoted 7 lines
drivers/s390/kvm/kvm_virtio.c +1
-0@@ -227,6 +227,38 @@ static void kvm_del_vq(struct virtqueue *vq) KVM_S390_VIRTIO_RING_ALIGN)); } +static void vp_del_vqs (struct virtio_device *vdev)
s/vp_del_vqs/kvm_del_vqs/ :
drivers/s390/kvm/kvm_virtio.c: In function 'kvm_find_vqs':drivers/s390/kvm/kvm_virtio.c:258: error: implicit declaration of function 'kvm_del_vqs' drivers/s390/kvm/kvm_virtio.c: At top level:drivers/s390/kvm/kvm_virtio.c:274: error: 'kvm_del_vqs' undeclared here (not in a function) make[2]: *** [drivers/s390/kvm/kvm_virtio.o] Error 1
include/linux/virtio_config.h
Quoted 49 lines
drivers/s390/kvm/kvm_virtio.c +33
-2+{ +struct virtqueue *vq, *n; + +list_for_each_entry_safe (vq, n, &vdev->vqs, list) +kvm_del_vq (vq); +} + +static int kvm_find_vqs (struct virtio_device *vdev,unsigned nvqs, +struct virtqueue *vqs[], +vq_callback_t *callbacks[], +const char *names[]) +{ +struct kvm_device *kdev =to_kvmdev (vdev); +int i; + +/* We must have this many virtqueues. */ +if (nvqs > kdev->desc->num_vq) +return -ENOENT ; + +for (i =0 ; i < nvqs; ++i) { + vqs[i] =kvm_find_vq (vdev, i, callbacks[i], names[i]); +if (IS_ERR (vqs[i])) + goto error; + } +return 0 ; + +error: +kvm_del_vqs (vdev); +return PTR_ERR (vqs[i]); +} +/* * The config ops structure as defined by virtio config */ @@ -238,8 +270,8 @@ static struct virtio_config_ops kvm_vq_configspace_ops = { .get_status = kvm_get_status, .set_status = kvm_set_status, .reset = kvm_reset, - .find_vq = kvm_find_vq, - .del_vq = kvm_del_vq, + .find_vqs = kvm_find_vqs, + .del_vqs = kvm_del_vqs, };/* include/linux/virtio_config.h +0
-0
[...]
needs an
#ifdef __KERNEL__
+#include <linux/err.h>
#include <linux/virtio.h>
[...]
include/linux/virtio_config.h static inline int virtio_config_buf(struct virtio_device *vdev, · line 152
Quoted 3 lines
include/linux/virtio_config.h +3
-0+int err = vdev->config->find_vqs (vdev,1 , &vq, callbacks, names); +if (err <0 ) +return ERR_PTR (err);
Otherwise ERR_PTR does not compile.