virtio: add names to virtqueue struct, mapping from devices to queues.
Subsystems (13)
A message that is not in the archive (sent off-list or not yet received): [email protected]
Michael S. Tsirkin
maintainerPATCH v6 1/4 virtio: add names to virtqueue struct, mapping from devices to queues.
From: Rusty Russell <[email protected]>
Add a linked list of all virtqueues for a virtio device: this helps for debugging and is also needed for upcoming interface change.
Also, add a "name" field for clearer debug messages.
---
including this Rusty's patch here for completeness.
drivers/block/virtio_blk.c | 2 +- drivers/char/hw_random/virtio-rng.c | 2 +- drivers/char/virtio_console.c | 4 ++-- drivers/lguest/lguest_device.c | 5 +++-- drivers/net/virtio_net.c | 6 +++--- drivers/s390/kvm/kvm_virtio.c | 7 ++++--- drivers/virtio/virtio.c | 2 ++ drivers/virtio/virtio_balloon.c | 4 ++-- drivers/virtio/virtio_pci.c | 5 +++-- drivers/virtio/virtio_ring.c | 25 +++++++++++++++++++------ include/linux/virtio.h | 12 ++++++++---- include/linux/virtio_config.h | 6 ++++-- include/linux/virtio_ring.h | 3 ++- net/9p/trans_virtio.c | 2 +- 14 files changed, 55 insertions(+), 30 deletions(-)
drivers/block/virtio_blk.c +1 -1
@@ -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); + vblk->vq = vdev->config->find_vq (vdev,0 , blk_done,"requests" );if (IS_ERR (vblk->vq)) { err =PTR_ERR (vblk->vq); goto out_free_vblk;
drivers/char/hw_random/virtio-rng.c +1 -1
@@ -94,7 +94,7 @@ 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); + vq = vdev->config->find_vq (vdev,0 , random_recv_done,"input" );if (IS_ERR (vq))return PTR_ERR (vq);
drivers/char/virtio_console.c +2 -2
@@ -202,13 +202,13 @@ static int __devinit virtcons_probe(struct virtio_device *dev)/* Find the input queue. */ /* 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); + in_vq = vdev->config->find_vq (vdev,0 , hvc_handle_input,"input" );if (IS_ERR (in_vq)) { err =PTR_ERR (in_vq); goto free; } - out_vq = vdev->config->find_vq (vdev,1 ,NULL ); + out_vq = vdev->config->find_vq (vdev,1 ,NULL ,"output" );if (IS_ERR (out_vq)) { err =PTR_ERR (out_vq); goto free_in_vq;
drivers/lguest/lguest_device.c +3 -2
@@ -228,7 +228,8 @@ extern void lguest_setup_irq(unsigned int irq);* function. */ static struct virtqueue *lg_find_vq (struct virtio_device *vdev,unsigned index, -void (*callback)(struct virtqueue *vq)) +void (*callback)(struct virtqueue *vq), +const char *name) {struct lguest_device *ldev =to_lgdev (vdev);struct lguest_vq_info *lvq;
@@ -263,7 +264,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,/* OK, tell virtio_ring.c to set up a virtqueue now we know its size * and we've got a pointer to its pages. */ vq =vring_new_virtqueue (lvq->config.num,LGUEST_VRING_ALIGN , - vdev, lvq->pages, lg_notify, callback); + vdev, lvq->pages, lg_notify, callback, name);if (!vq) { err = -ENOMEM ; goto unmap;
drivers/net/virtio_net.c +3 -3
@@ -906,20 +906,20 @@ static int virtnet_probe(struct virtio_device *vdev) vi->mergeable_rx_bufs = true;/* We expect two virtqueues, receive then send. */ - vi->rvq = vdev->config->find_vq (vdev,0 , skb_recv_done); + vi->rvq = vdev->config->find_vq (vdev,0 , skb_recv_done,"input" );if (IS_ERR (vi->rvq)) { err =PTR_ERR (vi->rvq); goto free; } - vi->svq = vdev->config->find_vq (vdev,1 , skb_xmit_done); + vi->svq = vdev->config->find_vq (vdev,1 , skb_xmit_done,"output" );if (IS_ERR (vi->svq)) { err =PTR_ERR (vi->svq); goto free_recv; }if (virtio_has_feature (vi->vdev,VIRTIO_NET_F_CTRL_VQ )) { - vi->cvq = vdev->config->find_vq (vdev,2 ,NULL ); + vi->cvq = vdev->config->find_vq (vdev,2 ,NULL ,"control" );if (IS_ERR (vi->cvq)) { err =PTR_ERR (vi->svq); goto free_send;
drivers/s390/kvm/kvm_virtio.c +4 -3
@@ -173,8 +173,9 @@ static void kvm_notify(struct virtqueue *vq)* this device and sets it up. */ static struct virtqueue *kvm_find_vq (struct virtio_device *vdev, -unsigned index, -void (*callback)(struct virtqueue *vq)) +unsigned index, +void (*callback)(struct virtqueue *vq), +const char *name) {struct kvm_device *kdev =to_kvmdev (vdev);struct kvm_vqconfig *config;
@@ -194,7 +195,7 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, vq =vring_new_virtqueue (config->num,KVM_S390_VIRTIO_RING_ALIGN , vdev, (void *) config->address, - kvm_notify, callback); + kvm_notify, callback, name);if (!vq) { err = -ENOMEM ; goto unmap;
drivers/virtio/virtio.c +2 -0
@@ -185,6 +185,8 @@ int register_virtio_device(struct virtio_device *dev)/* Acknowledge that we've seen the device. */ add_status (dev,VIRTIO_CONFIG_S_ACKNOWLEDGE ); +INIT_LIST_HEAD (&dev->vqs); +/* device_register() causes the bus infrastructure to look for a * matching driver. */ err =device_register (&dev->dev);
drivers/virtio/virtio_balloon.c +2 -2
@@ -218,13 +218,13 @@ 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); + vb->inflate_vq = vdev->config->find_vq (vdev,0 , balloon_ack,"inflate" );if (IS_ERR (vb->inflate_vq)) { err =PTR_ERR (vb->inflate_vq); goto out_free_vb; } - vb->deflate_vq = vdev->config->find_vq (vdev,1 , balloon_ack); + 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;
drivers/virtio/virtio_pci.c +3 -2
@@ -208,7 +208,8 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)/* the config->find_vq() implementation */ static struct virtqueue *vp_find_vq (struct virtio_device *vdev,unsigned index, -void (*callback)(struct virtqueue *vq)) +void (*callback)(struct virtqueue *vq), +const char *name) {struct virtio_pci_device *vp_dev =to_vp_device (vdev);struct virtio_pci_vq_info *info;
@@ -247,7 +248,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,/* create the vring */ vq =vring_new_virtqueue (info->num,VIRTIO_PCI_VRING_ALIGN , - vdev, info->queue, vp_notify, callback); + vdev, info->queue, vp_notify, callback, name);if (!vq) { err = -ENOMEM ; goto out_activate_queue;
drivers/virtio/virtio_ring.c +19 -6
@@ -23,21 +23,30 @@#ifdef DEBUG /* For development, we want to crash whenever the ring is screwed. */ -#define BAD_RING(_vq, fmt...) \ - do { dev_err(&(_vq)->vq.vdev->dev, fmt); BUG(); } while(0) +#define BAD_RING(_vq, fmt, args...) \ + do { \ + dev_err(&(_vq)->vq.vdev->dev, \ + "%s:"fmt, (_vq)->vq.name, ##args); \ + BUG(); \ + } while (0)/* Caller is supposed to guarantee no reentry. */ #define START_USE (_vq) \ do { \ if ((_vq)->in_use) \ - panic("in_use = %i\n", (_vq)->in_use); \ + panic("%s:in_use = %i\n", \ + (_vq)->vq.name, (_vq)->in_use); \ (_vq)->in_use = __LINE__; \ mb(); \ } while(0)#define END_USE (_vq) \ do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0)#else -#define BAD_RING(_vq, fmt...) \ - do { dev_err(&_vq->vq.vdev->dev, fmt); (_vq)->broken = true; } while(0) +#define BAD_RING(_vq, fmt, args...) \ + do { \ + dev_err(&_vq->vq.vdev->dev, \ + "%s:"fmt, (_vq)->vq.name, ##args); \ + (_vq)->broken = true; \ + } while(0)#define START_USE (vq)#define END_USE (vq)#endif
@@ -284,7 +293,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,struct virtio_device *vdev, void *pages, void (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *)) + void (*callback)(struct virtqueue *), + const char *name) { struct vring_virtqueue *vq;unsigned int i;
@@ -303,10 +313,12 @@ struct virtqueue *vring_new_virtqueue(unsigned int num, vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.vq_ops = &vring_vq_ops; + vq->vq.name = name; vq->notify = notify; vq->broken = false; vq->last_used_idx =0 ; vq->num_added =0 ; +list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false;#endif
@@ -327,6 +339,7 @@ EXPORT_SYMBOL_GPL(vring_new_virtqueue);void vring_del_virtqueue (struct virtqueue *vq) { +list_del (&vq->list);kfree (to_vvq (vq)); }EXPORT_SYMBOL_GPL (vring_del_virtqueue);
include/linux/virtio.h +8 -4
@@ -10,14 +10,17 @@/** * virtqueue - a queue to register buffers for sending or receiving. +* @list: the chain of virtqueues for this device * @callback: the function to call when buffers are consumed (can be NULL). +* @name: the name of this virtqueue (mainly for debugging) * @vdev: the virtio device this queue was created for. * @vq_ops: the operations for this virtqueue (see below). * @priv: a pointer for the virtqueue implementation to use. */ -struct virtqueue -{ +struct virtqueue { +struct list_head list;void (*callback)(struct virtqueue *vq); +const char *name;struct virtio_device *vdev;struct virtqueue_ops *vq_ops;void *priv;
@@ -76,15 +79,16 @@ struct virtqueue_ops {* @dev: underlying device. * @id: the device type identification (used to match it with a driver). * @config: the configuration ops for this device. +* @vqs: the list of virtqueues for this device. * @features: the features supported by both driver and device. * @priv: private pointer for the driver's use. */ -struct virtio_device -{ +struct virtio_device {int index;struct device dev;struct virtio_device_id id;struct virtio_config_ops *config; +struct list_head vqs;/* Note that this is a Linux set_bit-style bitmap. */ unsigned long features[1 ];void *priv;
include/linux/virtio_config.h +4 -2
@@ -55,7 +55,8 @@* @find_vq: find a virtqueue and instantiate it. * vdev: the virtio_device * index: the 0-based virtqueue number in case there's more than one. -* callback: the virqtueue callback +* callback: the virtqueue callback +* name: the virtqueue name (mainly for debugging) * Returns the new virtqueue or ERR_PTR() (eg. -ENOENT). * @del_vq: free a virtqueue found by find_vq(). * @get_features: get the array of feature bits for this device.
@@ -77,7 +78,8 @@ struct virtio_config_opsvoid (*reset)(struct virtio_device *vdev);struct virtqueue *(*find_vq)(struct virtio_device *vdev,unsigned index, -void (*callback)(struct virtqueue *)); +void (*callback)(struct virtqueue *), +const char *name);void (*del_vq)(struct virtqueue *vq);u32 (*get_features)(struct virtio_device *vdev);void (*finalize_features)(struct virtio_device *vdev);
include/linux/virtio_ring.h +2 -1
@@ -119,7 +119,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,struct virtio_device *vdev, void *pages, void (*notify)(struct virtqueue *vq), - void (*callback)(struct virtqueue *vq)); + void (*callback)(struct virtqueue *vq), + const char *name);void vring_del_virtqueue (struct virtqueue *vq);/* Filter out transport-specific feature bits. */ void vring_transport_features (struct virtio_device *vdev);
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); + chan->vq = vdev->config->find_vq (vdev,0 , 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 v6 1/4 virtio: add names to virtqueue struct, mapping from devices to queues.
In reply to a message not in the archiveFrom: Rusty Russell <[email protected]>
Add a linked list of all virtqueues for a virtio device: this helps for debugging and is also needed for upcoming interface change.
Also, add a "name" field for clearer debug messages.
---
including this Rusty's patch here for completeness.
drivers/block/virtio_blk.c | 2 +- drivers/char/hw_random/virtio-rng.c | 2 +- drivers/char/virtio_console.c | 4 ++-- drivers/lguest/lguest_device.c | 5 +++-- drivers/net/virtio_net.c | 6 +++--- drivers/s390/kvm/kvm_virtio.c | 7 ++++--- drivers/virtio/virtio.c | 2 ++ drivers/virtio/virtio_balloon.c | 4 ++-- drivers/virtio/virtio_pci.c | 5 +++-- drivers/virtio/virtio_ring.c | 25 +++++++++++++++++++------ include/linux/virtio.h | 12 ++++++++---- include/linux/virtio_config.h | 6 ++++-- include/linux/virtio_ring.h | 3 ++- net/9p/trans_virtio.c | 2 +- 14 files changed, 55 insertions(+), 30 deletions(-)
drivers/block/virtio_blk.c +1 -1
@@ -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); + vblk->vq = vdev->config->find_vq (vdev,0 , blk_done,"requests" );if (IS_ERR (vblk->vq)) { err =PTR_ERR (vblk->vq); goto out_free_vblk;
drivers/char/hw_random/virtio-rng.c +1 -1
@@ -94,7 +94,7 @@ 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); + vq = vdev->config->find_vq (vdev,0 , random_recv_done,"input" );if (IS_ERR (vq))return PTR_ERR (vq);
drivers/char/virtio_console.c +2 -2
@@ -202,13 +202,13 @@ static int __devinit virtcons_probe(struct virtio_device *dev)/* Find the input queue. */ /* 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); + in_vq = vdev->config->find_vq (vdev,0 , hvc_handle_input,"input" );if (IS_ERR (in_vq)) { err =PTR_ERR (in_vq); goto free; } - out_vq = vdev->config->find_vq (vdev,1 ,NULL ); + out_vq = vdev->config->find_vq (vdev,1 ,NULL ,"output" );if (IS_ERR (out_vq)) { err =PTR_ERR (out_vq); goto free_in_vq;
drivers/lguest/lguest_device.c +3 -2
@@ -228,7 +228,8 @@ extern void lguest_setup_irq(unsigned int irq);* function. */ static struct virtqueue *lg_find_vq (struct virtio_device *vdev,unsigned index, -void (*callback)(struct virtqueue *vq)) +void (*callback)(struct virtqueue *vq), +const char *name) {struct lguest_device *ldev =to_lgdev (vdev);struct lguest_vq_info *lvq;
@@ -263,7 +264,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,/* OK, tell virtio_ring.c to set up a virtqueue now we know its size * and we've got a pointer to its pages. */ vq =vring_new_virtqueue (lvq->config.num,LGUEST_VRING_ALIGN , - vdev, lvq->pages, lg_notify, callback); + vdev, lvq->pages, lg_notify, callback, name);if (!vq) { err = -ENOMEM ; goto unmap;
drivers/net/virtio_net.c +3 -3
@@ -906,20 +906,20 @@ static int virtnet_probe(struct virtio_device *vdev) vi->mergeable_rx_bufs = true;/* We expect two virtqueues, receive then send. */ - vi->rvq = vdev->config->find_vq (vdev,0 , skb_recv_done); + vi->rvq = vdev->config->find_vq (vdev,0 , skb_recv_done,"input" );if (IS_ERR (vi->rvq)) { err =PTR_ERR (vi->rvq); goto free; } - vi->svq = vdev->config->find_vq (vdev,1 , skb_xmit_done); + vi->svq = vdev->config->find_vq (vdev,1 , skb_xmit_done,"output" );if (IS_ERR (vi->svq)) { err =PTR_ERR (vi->svq); goto free_recv; }if (virtio_has_feature (vi->vdev,VIRTIO_NET_F_CTRL_VQ )) { - vi->cvq = vdev->config->find_vq (vdev,2 ,NULL ); + vi->cvq = vdev->config->find_vq (vdev,2 ,NULL ,"control" );if (IS_ERR (vi->cvq)) { err =PTR_ERR (vi->svq); goto free_send;
drivers/s390/kvm/kvm_virtio.c +4 -3
@@ -173,8 +173,9 @@ static void kvm_notify(struct virtqueue *vq)* this device and sets it up. */ static struct virtqueue *kvm_find_vq (struct virtio_device *vdev, -unsigned index, -void (*callback)(struct virtqueue *vq)) +unsigned index, +void (*callback)(struct virtqueue *vq), +const char *name) {struct kvm_device *kdev =to_kvmdev (vdev);struct kvm_vqconfig *config;
@@ -194,7 +195,7 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, vq =vring_new_virtqueue (config->num,KVM_S390_VIRTIO_RING_ALIGN , vdev, (void *) config->address, - kvm_notify, callback); + kvm_notify, callback, name);if (!vq) { err = -ENOMEM ; goto unmap;
drivers/virtio/virtio.c +2 -0
@@ -185,6 +185,8 @@ int register_virtio_device(struct virtio_device *dev)/* Acknowledge that we've seen the device. */ add_status (dev,VIRTIO_CONFIG_S_ACKNOWLEDGE ); +INIT_LIST_HEAD (&dev->vqs); +/* device_register() causes the bus infrastructure to look for a * matching driver. */ err =device_register (&dev->dev);
drivers/virtio/virtio_balloon.c +2 -2
@@ -218,13 +218,13 @@ 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); + vb->inflate_vq = vdev->config->find_vq (vdev,0 , balloon_ack,"inflate" );if (IS_ERR (vb->inflate_vq)) { err =PTR_ERR (vb->inflate_vq); goto out_free_vb; } - vb->deflate_vq = vdev->config->find_vq (vdev,1 , balloon_ack); + 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;
drivers/virtio/virtio_pci.c +3 -2
@@ -208,7 +208,8 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)/* the config->find_vq() implementation */ static struct virtqueue *vp_find_vq (struct virtio_device *vdev,unsigned index, -void (*callback)(struct virtqueue *vq)) +void (*callback)(struct virtqueue *vq), +const char *name) {struct virtio_pci_device *vp_dev =to_vp_device (vdev);struct virtio_pci_vq_info *info;
@@ -247,7 +248,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,/* create the vring */ vq =vring_new_virtqueue (info->num,VIRTIO_PCI_VRING_ALIGN , - vdev, info->queue, vp_notify, callback); + vdev, info->queue, vp_notify, callback, name);if (!vq) { err = -ENOMEM ; goto out_activate_queue;
drivers/virtio/virtio_ring.c +19 -6
@@ -23,21 +23,30 @@#ifdef DEBUG /* For development, we want to crash whenever the ring is screwed. */ -#define BAD_RING(_vq, fmt...) \ - do { dev_err(&(_vq)->vq.vdev->dev, fmt); BUG(); } while(0) +#define BAD_RING(_vq, fmt, args...) \ + do { \ + dev_err(&(_vq)->vq.vdev->dev, \ + "%s:"fmt, (_vq)->vq.name, ##args); \ + BUG(); \ + } while (0)/* Caller is supposed to guarantee no reentry. */ #define START_USE (_vq) \ do { \ if ((_vq)->in_use) \ - panic("in_use = %i\n", (_vq)->in_use); \ + panic("%s:in_use = %i\n", \ + (_vq)->vq.name, (_vq)->in_use); \ (_vq)->in_use = __LINE__; \ mb(); \ } while(0)#define END_USE (_vq) \ do { BUG_ON(!(_vq)->in_use); (_vq)->in_use = 0; mb(); } while(0)#else -#define BAD_RING(_vq, fmt...) \ - do { dev_err(&_vq->vq.vdev->dev, fmt); (_vq)->broken = true; } while(0) +#define BAD_RING(_vq, fmt, args...) \ + do { \ + dev_err(&_vq->vq.vdev->dev, \ + "%s:"fmt, (_vq)->vq.name, ##args); \ + (_vq)->broken = true; \ + } while(0)#define START_USE (vq)#define END_USE (vq)#endif
@@ -284,7 +293,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,struct virtio_device *vdev, void *pages, void (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *)) + void (*callback)(struct virtqueue *), + const char *name) { struct vring_virtqueue *vq;unsigned int i;
@@ -303,10 +313,12 @@ struct virtqueue *vring_new_virtqueue(unsigned int num, vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.vq_ops = &vring_vq_ops; + vq->vq.name = name; vq->notify = notify; vq->broken = false; vq->last_used_idx =0 ; vq->num_added =0 ; +list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG vq->in_use = false;#endif
@@ -327,6 +339,7 @@ EXPORT_SYMBOL_GPL(vring_new_virtqueue);void vring_del_virtqueue (struct virtqueue *vq) { +list_del (&vq->list);kfree (to_vvq (vq)); }EXPORT_SYMBOL_GPL (vring_del_virtqueue);
include/linux/virtio.h +8 -4
@@ -10,14 +10,17 @@/** * virtqueue - a queue to register buffers for sending or receiving. +* @list: the chain of virtqueues for this device * @callback: the function to call when buffers are consumed (can be NULL). +* @name: the name of this virtqueue (mainly for debugging) * @vdev: the virtio device this queue was created for. * @vq_ops: the operations for this virtqueue (see below). * @priv: a pointer for the virtqueue implementation to use. */ -struct virtqueue -{ +struct virtqueue { +struct list_head list;void (*callback)(struct virtqueue *vq); +const char *name;struct virtio_device *vdev;struct virtqueue_ops *vq_ops;void *priv;
@@ -76,15 +79,16 @@ struct virtqueue_ops {* @dev: underlying device. * @id: the device type identification (used to match it with a driver). * @config: the configuration ops for this device. +* @vqs: the list of virtqueues for this device. * @features: the features supported by both driver and device. * @priv: private pointer for the driver's use. */ -struct virtio_device -{ +struct virtio_device {int index;struct device dev;struct virtio_device_id id;struct virtio_config_ops *config; +struct list_head vqs;/* Note that this is a Linux set_bit-style bitmap. */ unsigned long features[1 ];void *priv;
include/linux/virtio_config.h +4 -2
@@ -55,7 +55,8 @@* @find_vq: find a virtqueue and instantiate it. * vdev: the virtio_device * index: the 0-based virtqueue number in case there's more than one. -* callback: the virqtueue callback +* callback: the virtqueue callback +* name: the virtqueue name (mainly for debugging) * Returns the new virtqueue or ERR_PTR() (eg. -ENOENT). * @del_vq: free a virtqueue found by find_vq(). * @get_features: get the array of feature bits for this device.
@@ -77,7 +78,8 @@ struct virtio_config_opsvoid (*reset)(struct virtio_device *vdev);struct virtqueue *(*find_vq)(struct virtio_device *vdev,unsigned index, -void (*callback)(struct virtqueue *)); +void (*callback)(struct virtqueue *), +const char *name);void (*del_vq)(struct virtqueue *vq);u32 (*get_features)(struct virtio_device *vdev);void (*finalize_features)(struct virtio_device *vdev);
include/linux/virtio_ring.h +2 -1
@@ -119,7 +119,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,struct virtio_device *vdev, void *pages, void (*notify)(struct virtqueue *vq), - void (*callback)(struct virtqueue *vq)); + void (*callback)(struct virtqueue *vq), + const char *name);void vring_del_virtqueue (struct virtqueue *vq);/* Filter out transport-specific feature bits. */ void vring_transport_features (struct virtio_device *vdev);
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); + chan->vq = vdev->config->find_vq (vdev,0 , 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 v6 2/4 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 | 47 ++++++++++++++++++++++++++-------- net/9p/trans_virtio.c | 2 +- 10 files changed, 181 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 kvm_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 +36 -11
80 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 v6 2/4 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 | 47 ++++++++++++++++++++++++++-------- net/9p/trans_virtio.c | 2 +- 10 files changed, 181 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 kvm_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 +36 -11
80 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
Re: [PATCHv6 2/4] virtio: find_vqs/del_vqs virtio operations
And here's the fixup patch I applied:
drivers/lguest/lguest_device.c +4 -4
@@ -318,11 +318,11 @@ static void lg_del_vqs(struct virtio_devstruct virtqueue *vq, *n;list_for_each_entry_safe (vq, n, &vdev->vqs, list) -kvm_del_vq (vq); +lg_del_vq (vq); }static int lg_find_vqs (struct virtio_device *vdev,unsigned nvqs, - struct virtqueue *vqs[] +struct virtqueue *vqs[],vq_callback_t *callbacks[],const char *names[]) {
@@ -331,7 +331,7 @@ static int lg_find_vqs(struct virtio_dev/* We must have this many virtqueues. */ if (nvqs > ldev->desc->num_vq) -return ERR_PTR (-ENOENT ); +return -ENOENT ;for (i =0 ; i < nvqs; ++i) { vqs[i] =lg_find_vq (vdev, i, callbacks[i], names[i]);
@@ -341,7 +341,7 @@ static int lg_find_vqs(struct virtio_devreturn 0 ; error: -vp_del_vqs (vdev); +lg_del_vqs (vdev);return PTR_ERR (vqs[i]); }
net/9p/trans_virtio.c +2 -2
@@ -260,7 +260,7 @@ static int p9_virtio_probe(struct virtioreturn 0 ; out_free_vq: - vdev->config->del_vq (chan->vq); + vdev->config->del_vqs (vdev); fail:mutex_lock (&virtio_9p_lock); chan_index--;
@@ -331,7 +331,7 @@ static void p9_virtio_remove(struct virtBUG_ON (chan->inuse);if (chan->initialized) { - vdev->config->del_vq (chan->vq); + vdev->config->del_vqs (vdev); chan->initialized = false; } }
Re: [PATCHv6 2/4] virtio: find_vqs/del_vqs virtio operations
In reply to Michael S. TsirkinAnd here's the fixup patch I applied:
drivers/lguest/lguest_device.c +4 -4
@@ -318,11 +318,11 @@ static void lg_del_vqs(struct virtio_devstruct virtqueue *vq, *n;list_for_each_entry_safe (vq, n, &vdev->vqs, list) -kvm_del_vq (vq); +lg_del_vq (vq); }static int lg_find_vqs (struct virtio_device *vdev,unsigned nvqs, - struct virtqueue *vqs[] +struct virtqueue *vqs[],vq_callback_t *callbacks[],const char *names[]) {
@@ -331,7 +331,7 @@ static int lg_find_vqs(struct virtio_dev/* We must have this many virtqueues. */ if (nvqs > ldev->desc->num_vq) -return ERR_PTR (-ENOENT ); +return -ENOENT ;for (i =0 ; i < nvqs; ++i) { vqs[i] =lg_find_vq (vdev, i, callbacks[i], names[i]);
@@ -341,7 +341,7 @@ static int lg_find_vqs(struct virtio_devreturn 0 ; error: -vp_del_vqs (vdev); +lg_del_vqs (vdev);return PTR_ERR (vqs[i]); }
net/9p/trans_virtio.c +2 -2
@@ -260,7 +260,7 @@ static int p9_virtio_probe(struct virtioreturn 0 ; out_free_vq: - vdev->config->del_vq (chan->vq); + vdev->config->del_vqs (vdev); fail:mutex_lock (&virtio_9p_lock); chan_index--;
@@ -331,7 +331,7 @@ static void p9_virtio_remove(struct virtBUG_ON (chan->inuse);if (chan->initialized) { - vdev->config->del_vq (chan->vq); + vdev->config->del_vqs (vdev); chan->initialized = false; } }
Michael S. Tsirkin
maintainerPATCH v6 3/4 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 v6 3/4 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 v6 4/4 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 v6 4/4 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: [PATCHv6 4/4] virtio_pci: optional MSI-X support
Quoted 105 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.drivers/virtio/virtio_pci.c +95
-0+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; +} +@@ -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 ; +
drivers/virtio/virtio_pci.c static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, · line 439
Quoted 10 lines
drivers/virtio/virtio_pci.c +9
-0+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; + } + } +
I'm not sure I understand how the vq -> msi mapping works. Do we actually support an arbitrary mapping, or just either linear or n:1?
I don't mind the driver being limited, but the device interface should be flexible. We'll want to deal with limited vector availability soon.
Signature · 1 line
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
Re: [PATCHv6 4/4] virtio_pci: optional MSI-X support
In reply to Michael S. TsirkinQuoted 105 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.drivers/virtio/virtio_pci.c +95
-0+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; +} +@@ -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 ; +
drivers/virtio/virtio_pci.c static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index, · line 439
Quoted 10 lines
drivers/virtio/virtio_pci.c +9
-0+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; + } + } +
I'm not sure I understand how the vq -> msi mapping works. Do we actually support an arbitrary mapping, or just either linear or n:1?
I don't mind the driver being limited, but the device interface should be flexible. We'll want to deal with limited vector availability soon.
Signature · 1 line
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
Michael S. Tsirkin
maintainerRe: [PATCHv6 4/4] virtio_pci: optional MSI-X support
Quoted 10 lines · Avi Kivity
Quoted 5 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.I'm not sure I understand how the vq -> msi mapping works. Do we actually support an arbitrary mapping, or just either linear or n:1?
Arbitrary mapping.
Quoted 2 lines
I don't mind the driver being limited, but the device interface should be flexible. We'll want to deal with limited vector availability soon.
I agree.
The code in qemu lets you specify, for each queue, which MSIX vector you want to use, or a special value if you don't want signalling. You also specify which MSIX vector you want to use for config change notifications, or a special value if you want to e.g. poll.
I think that's as flexible as it gets.
The API within guest is much simpler, but it does not need to be stable.
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRe: [PATCHv6 4/4] virtio_pci: optional MSI-X support
In reply to Avi KivityQuoted 10 lines · Avi Kivity
Quoted 5 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.I'm not sure I understand how the vq -> msi mapping works. Do we actually support an arbitrary mapping, or just either linear or n:1?
Arbitrary mapping.
Quoted 2 lines
I don't mind the driver being limited, but the device interface should be flexible. We'll want to deal with limited vector availability soon.
I agree.
The code in qemu lets you specify, for each queue, which MSIX vector you want to use, or a special value if you don't want signalling. You also specify which MSIX vector you want to use for config change notifications, or a special value if you want to e.g. poll.
I think that's as flexible as it gets.
The API within guest is much simpler, but it does not need to be stable.
Signature · 1 line
MST