Skip to contentSkip to conversation

virtio: add request_vqs/free_vqs operations

PATCH In review Michael S. Tsirkin 2 participants 20 messages Apr 27, 2009 – May 4, 2009 1 message not in the archive

Subsystems (11)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

Michael S. Tsirkin

author maintainer
#1 lore raw

PATCH 1/8 virtio: add request_vqs/free_vqs operations

This adds 2 new optional virtio operations: request_vqs/free_vqs. They will be used for MSI support, because MSI needs to know the total number of vectors upfront.

---
 include/linux/virtio_config.h |   35 +++++++++++++++++++++++++++++++++++
 1 files changed, 35 insertions(+), 0 deletions(-)
include/linux/virtio_config.h +35 -0
index bf8ec28..e935670 100644
@@ -49,6 +49,15 @@
  * @set_status: write the status byte
  *	vdev: the virtio_device
  *	status: the new status byte
+ * @request_vqs: request the specified number of virtqueues
+ *	vdev: the virtio_device
+ *	max_vqs: the max number of virtqueues we want
+ *      If supplied, must call before any virtqueues are instantiated.
+ *      To modify the max number of virtqueues after request_vqs has been
+ *      called, call free_vqs and then request_vqs with a new value.
+ * @free_vqs: cleanup resources allocated by request_vqs
+ *	vdev: the virtio_device
+ *      If supplied, must call after all virtqueues have been deleted.
  * @reset: reset the device
  *	vdev: the virtio device
  *	After this, status and feature negotiation must be done again
@@ -75,6 +84,8 @@ struct virtio_config_ops
 	u8 (*get_status)(struct virtio_device *vdev);
 	void (*set_status)(struct virtio_device *vdev, u8 status);
 	void (*reset)(struct virtio_device *vdev);
+	int (*request_vqs)(struct virtio_device *vdev, unsigned max_vqs);
+	void (*free_vqs)(struct virtio_device *vdev);
 	struct virtqueue *(*find_vq)(struct virtio_device *vdev,
 				     unsigned index,
 				     void (*callback)(struct virtqueue *));
@@ -126,5 +137,29 @@ static inline int virtio_config_buf(struct virtio_device *vdev,
 	vdev->config->get(vdev, offset, buf, len);
 	return 0;
 }
+
+/**
+ * virtio_request_vqs: request the specified number of virtqueues
+ * @vdev: the virtio_device
+ * @max_vqs: the max number of virtqueues we want
+ *
+ * For details, see documentation for request_vqs above. */
+static inline int virtio_request_vqs(struct virtio_device *vdev,
+				     unsigned max_vqs)
+{
+	return vdev->config->request_vqs ?
+		vdev->config->request_vqs(vdev, max_vqs) : 0;
+}
+
+/**
+ * free_vqs: cleanup resources allocated by virtio_request_vqs
+ * @vdev: the virtio_device
+ *
+ * For details, see documentation for free_vqs above. */
+static inline void virtio_free_vqs(struct virtio_device *vdev)
+{
+	if (vdev->config->free_vqs)
+		vdev->config->free_vqs(vdev);
+}
 #endif /* __KERNEL__ */
 #endif /* _LINUX_VIRTIO_CONFIG_H */
Signature · 1 line
1.6.0.6
author maintainer
#2 lore raw

PATCH 1/8 virtio: add request_vqs/free_vqs operations

In reply to a message not in the archive

This adds 2 new optional virtio operations: request_vqs/free_vqs. They will be used for MSI support, because MSI needs to know the total number of vectors upfront.

---
 include/linux/virtio_config.h |   35 +++++++++++++++++++++++++++++++++++
 1 files changed, 35 insertions(+), 0 deletions(-)
include/linux/virtio_config.h +35 -0
index bf8ec28..e935670 100644
@@ -49,6 +49,15 @@
  * @set_status: write the status byte
  *	vdev: the virtio_device
  *	status: the new status byte
+ * @request_vqs: request the specified number of virtqueues
+ *	vdev: the virtio_device
+ *	max_vqs: the max number of virtqueues we want
+ *      If supplied, must call before any virtqueues are instantiated.
+ *      To modify the max number of virtqueues after request_vqs has been
+ *      called, call free_vqs and then request_vqs with a new value.
+ * @free_vqs: cleanup resources allocated by request_vqs
+ *	vdev: the virtio_device
+ *      If supplied, must call after all virtqueues have been deleted.
  * @reset: reset the device
  *	vdev: the virtio device
  *	After this, status and feature negotiation must be done again
@@ -75,6 +84,8 @@ struct virtio_config_ops
 	u8 (*get_status)(struct virtio_device *vdev);
 	void (*set_status)(struct virtio_device *vdev, u8 status);
 	void (*reset)(struct virtio_device *vdev);
+	int (*request_vqs)(struct virtio_device *vdev, unsigned max_vqs);
+	void (*free_vqs)(struct virtio_device *vdev);
 	struct virtqueue *(*find_vq)(struct virtio_device *vdev,
 				     unsigned index,
 				     void (*callback)(struct virtqueue *));
@@ -126,5 +137,29 @@ static inline int virtio_config_buf(struct virtio_device *vdev,
 	vdev->config->get(vdev, offset, buf, len);
 	return 0;
 }
+
+/**
+ * virtio_request_vqs: request the specified number of virtqueues
+ * @vdev: the virtio_device
+ * @max_vqs: the max number of virtqueues we want
+ *
+ * For details, see documentation for request_vqs above. */
+static inline int virtio_request_vqs(struct virtio_device *vdev,
+				     unsigned max_vqs)
+{
+	return vdev->config->request_vqs ?
+		vdev->config->request_vqs(vdev, max_vqs) : 0;
+}
+
+/**
+ * free_vqs: cleanup resources allocated by virtio_request_vqs
+ * @vdev: the virtio_device
+ *
+ * For details, see documentation for free_vqs above. */
+static inline void virtio_free_vqs(struct virtio_device *vdev)
+{
+	if (vdev->config->free_vqs)
+		vdev->config->free_vqs(vdev);
+}
 #endif /* __KERNEL__ */
 #endif /* _LINUX_VIRTIO_CONFIG_H */
Signature · 1 line
1.6.0.6
#3 lore raw
Quoted 5 lines · Michael S. Tsirkin

This adds 2 new optional virtio operations: request_vqs/free_vqs. They will be used for MSI support, because MSI needs to know the total number of vectors upfront.

Hi Michael,

Thanks for this work! But this interface is horrible. Either probe for the
number of vqs in virtio_pci, or change find_vq to

	int (*find_vqs)(struct virtio_device *, unsigned max,
						     struct virtqueue *vqs[]);

Thanks,
Rusty.

#4 lore raw
In reply to Michael S. Tsirkin
Quoted 5 lines · Michael S. Tsirkin

This adds 2 new optional virtio operations: request_vqs/free_vqs. They will be used for MSI support, because MSI needs to know the total number of vectors upfront.

Hi Michael,

Thanks for this work! But this interface is horrible. Either probe for the
number of vqs in virtio_pci, or change find_vq to

	int (*find_vqs)(struct virtio_device *, unsigned max,
						     struct virtqueue *vqs[]);

Thanks,
Rusty.

author maintainer
#5 lore raw
Quoted 8 lines · Rusty Russell
Quoted 5 lines · Michael S. Tsirkin

This adds 2 new optional virtio operations: request_vqs/free_vqs. They will be used for MSI support, because MSI needs to know the total number of vectors upfront.

Hi Michael,

Hi Rusty,

Quoted 5 lines

Thanks for this work! But this interface is horrible. Either probe for the
number of vqs in virtio_pci, or change find_vq to

	int (*find_vqs)(struct virtio_device *, unsigned max,
						     struct virtqueue *vqs[]);

I'm happier with the later option: it's easy for a host to expose support for a very large number of vqs, and I don't want them to waste resources if guest does not use them.

Thanks for the feedback!

Quoted 2 lines

Thanks,
Rusty.

Signature · 1 line
MST
author maintainer
#6 lore raw
In reply to Rusty Russell
Quoted 8 lines · Rusty Russell
Quoted 5 lines · Michael S. Tsirkin

This adds 2 new optional virtio operations: request_vqs/free_vqs. They will be used for MSI support, because MSI needs to know the total number of vectors upfront.

Hi Michael,

Hi Rusty,

Quoted 5 lines

Thanks for this work! But this interface is horrible. Either probe for the
number of vqs in virtio_pci, or change find_vq to

	int (*find_vqs)(struct virtio_device *, unsigned max,
						     struct virtqueue *vqs[]);

I'm happier with the later option: it's easy for a host to expose support for a very large number of vqs, and I don't want them to waste resources if guest does not use them.

Thanks for the feedback!

Quoted 2 lines

Thanks,
Rusty.

Signature · 1 line
MST
author maintainer
#7 lore raw

PATCH 2/8 virtio_blk: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_blk.
These will be required for MSI support.

---
 drivers/block/virtio_blk.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)
drivers/block/virtio_blk.c +8 -1
index 5d34764..523eddc 100644
@@ -224,10 +224,14 @@ static int virtblk_probe(struct virtio_device *vdev)
 	sg_init_table(vblk->sg, vblk->sg_elems);
 
 	/* We expect one virtqueue, for output. */
+	err = virtio_request_vqs(vdev, 1);
+	if (err)
+		goto out_free_vblk;
+
 	vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
 	if (IS_ERR(vblk->vq)) {
 		err = PTR_ERR(vblk->vq);
-		goto out_free_vblk;
+		goto out_free_vqs;
 	}
 
 	vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
@@ -324,6 +328,8 @@ out_mempool:
 	mempool_destroy(vblk->pool);
 out_free_vq:
 	vdev->config->del_vq(vblk->vq);
+out_free_vqs:
+	virtio_free_vqs(vdev);
 out_free_vblk:
 	kfree(vblk);
 out:
@@ -345,6 +351,7 @@ static void virtblk_remove(struct virtio_device *vdev)
 	put_disk(vblk->disk);
 	mempool_destroy(vblk->pool);
 	vdev->config->del_vq(vblk->vq);
+	virtio_free_vqs(vdev);
 	kfree(vblk);
 }
 
Signature · 1 line
1.6.0.6
author maintainer
#8 lore raw

PATCH 2/8 virtio_blk: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_blk.
These will be required for MSI support.

---
 drivers/block/virtio_blk.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)
drivers/block/virtio_blk.c +8 -1
index 5d34764..523eddc 100644
@@ -224,10 +224,14 @@ static int virtblk_probe(struct virtio_device *vdev)
 	sg_init_table(vblk->sg, vblk->sg_elems);
 
 	/* We expect one virtqueue, for output. */
+	err = virtio_request_vqs(vdev, 1);
+	if (err)
+		goto out_free_vblk;
+
 	vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
 	if (IS_ERR(vblk->vq)) {
 		err = PTR_ERR(vblk->vq);
-		goto out_free_vblk;
+		goto out_free_vqs;
 	}
 
 	vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
@@ -324,6 +328,8 @@ out_mempool:
 	mempool_destroy(vblk->pool);
 out_free_vq:
 	vdev->config->del_vq(vblk->vq);
+out_free_vqs:
+	virtio_free_vqs(vdev);
 out_free_vblk:
 	kfree(vblk);
 out:
@@ -345,6 +351,7 @@ static void virtblk_remove(struct virtio_device *vdev)
 	put_disk(vblk->disk);
 	mempool_destroy(vblk->pool);
 	vdev->config->del_vq(vblk->vq);
+	virtio_free_vqs(vdev);
 	kfree(vblk);
 }
 
Signature · 1 line
1.6.0.6
author maintainer
#9 lore raw

PATCH 3/8 virtio-rng: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio-rng.
These will be required for MSI support.

---
 drivers/char/hw_random/virtio-rng.c |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)
drivers/char/hw_random/virtio-rng.c +9 -1
index d0e563e..d43a6cd 100644
@@ -94,13 +94,20 @@ static int virtrng_probe(struct virtio_device *vdev)
 	int err;
 
 	/* We expect a single virtqueue. */
+	err = virtio_request_vqs(vdev, 1);
+	if (err)
+		return err;
+
 	vq = vdev->config->find_vq(vdev, 0, random_recv_done);
-	if (IS_ERR(vq))
+	if (IS_ERR(vq)) {
+		virtio_free_vqs(vdev);
 		return PTR_ERR(vq);
+	}
 
 	err = hwrng_register(&virtio_hwrng);
 	if (err) {
 		vdev->config->del_vq(vq);
+		virtio_free_vqs(vdev);
 		return err;
 	}
 
@@ -113,6 +120,7 @@ static void virtrng_remove(struct virtio_device *vdev)
 	vdev->config->reset(vdev);
 	hwrng_unregister(&virtio_hwrng);
 	vdev->config->del_vq(vq);
+	virtio_free_vqs(vdev);
 }
 
 static struct virtio_device_id id_table[] = {
Signature · 1 line
1.6.0.6
author maintainer
#10 lore raw

PATCH 3/8 virtio-rng: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio-rng.
These will be required for MSI support.

---
 drivers/char/hw_random/virtio-rng.c |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)
drivers/char/hw_random/virtio-rng.c +9 -1
index d0e563e..d43a6cd 100644
@@ -94,13 +94,20 @@ static int virtrng_probe(struct virtio_device *vdev)
 	int err;
 
 	/* We expect a single virtqueue. */
+	err = virtio_request_vqs(vdev, 1);
+	if (err)
+		return err;
+
 	vq = vdev->config->find_vq(vdev, 0, random_recv_done);
-	if (IS_ERR(vq))
+	if (IS_ERR(vq)) {
+		virtio_free_vqs(vdev);
 		return PTR_ERR(vq);
+	}
 
 	err = hwrng_register(&virtio_hwrng);
 	if (err) {
 		vdev->config->del_vq(vq);
+		virtio_free_vqs(vdev);
 		return err;
 	}
 
@@ -113,6 +120,7 @@ static void virtrng_remove(struct virtio_device *vdev)
 	vdev->config->reset(vdev);
 	hwrng_unregister(&virtio_hwrng);
 	vdev->config->del_vq(vq);
+	virtio_free_vqs(vdev);
 }
 
 static struct virtio_device_id id_table[] = {
Signature · 1 line
1.6.0.6
author maintainer
#11 lore raw

PATCH 4/8 virtio_console: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_console.
These will be required for MSI support.

---
 drivers/char/virtio_console.c |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)
drivers/char/virtio_console.c +7 -1
index ff6f5a4..78c503f 100644
@@ -199,13 +199,17 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
 		goto fail;
 	}
 
+	err = virtio_request_vqs(vdev, 2);
+	if (err)
+		goto free;
+
 	/* 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);
 	if (IS_ERR(in_vq)) {
 		err = PTR_ERR(in_vq);
-		goto free;
+		goto free_vqs;
 	}
 
 	out_vq = vdev->config->find_vq(vdev, 1, NULL);
@@ -244,6 +248,8 @@ free_out_vq:
 	vdev->config->del_vq(out_vq);
 free_in_vq:
 	vdev->config->del_vq(in_vq);
+free_vqs:
+	virtio_free_vqs(vdev);
 free:
 	kfree(inbuf);
 fail:
Signature · 1 line
1.6.0.6
author maintainer
#12 lore raw

PATCH 4/8 virtio_console: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_console.
These will be required for MSI support.

---
 drivers/char/virtio_console.c |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)
drivers/char/virtio_console.c +7 -1
index ff6f5a4..78c503f 100644
@@ -199,13 +199,17 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
 		goto fail;
 	}
 
+	err = virtio_request_vqs(vdev, 2);
+	if (err)
+		goto free;
+
 	/* 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);
 	if (IS_ERR(in_vq)) {
 		err = PTR_ERR(in_vq);
-		goto free;
+		goto free_vqs;
 	}
 
 	out_vq = vdev->config->find_vq(vdev, 1, NULL);
@@ -244,6 +248,8 @@ free_out_vq:
 	vdev->config->del_vq(out_vq);
 free_in_vq:
 	vdev->config->del_vq(in_vq);
+free_vqs:
+	virtio_free_vqs(vdev);
 free:
 	kfree(inbuf);
 fail:
Signature · 1 line
1.6.0.6
author maintainer
#13 lore raw

PATCH 5/8 virtio_net: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_net.
These will be required for MSI support.

---
 drivers/net/virtio_net.c |   16 +++++++++++++---
 1 files changed, 13 insertions(+), 3 deletions(-)
drivers/net/virtio_net.c +13 -3
index 9c82a39..fbe8a70 100644
@@ -841,6 +841,7 @@ static int virtnet_probe(struct virtio_device *vdev)
 	int err;
 	struct net_device *dev;
 	struct virtnet_info *vi;
+	bool control_vq;
 
 	/* Allocate ourselves a network device with room for our info */
 	dev = alloc_etherdev(sizeof(struct virtnet_info));
@@ -901,11 +902,17 @@ static int virtnet_probe(struct virtio_device *vdev)
 	if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
 		vi->mergeable_rx_bufs = true;
 
-	/* We expect two virtqueues, receive then send. */
+	/* We expect either two or three virtqueues: receive, send and
+	 * optionally control. */
+	control_vq = virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ);
+	err = virtio_request_vqs(vdev, control_vq ? 3 : 2);
+	if (err)
+		goto free;
+
 	vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
 	if (IS_ERR(vi->rvq)) {
 		err = PTR_ERR(vi->rvq);
-		goto free;
+		goto free_vqs;
 	}
 
 	vi->svq = vdev->config->find_vq(vdev, 1, skb_xmit_done);
@@ -914,7 +921,7 @@ static int virtnet_probe(struct virtio_device *vdev)
 		goto free_recv;
 	}
 
-	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
+	if (control_vq) {
 		vi->cvq = vdev->config->find_vq(vdev, 2, NULL);
 		if (IS_ERR(vi->cvq)) {
 			err = PTR_ERR(vi->svq);
@@ -965,6 +972,8 @@ free_send:
 	vdev->config->del_vq(vi->svq);
 free_recv:
 	vdev->config->del_vq(vi->rvq);
+free_vqs:
+	virtio_free_vqs(vi->vdev);
 free:
 	free_netdev(dev);
 	return err;
@@ -994,6 +1003,7 @@ static void virtnet_remove(struct virtio_device *vdev)
 	vdev->config->del_vq(vi->rvq);
 	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
 		vdev->config->del_vq(vi->cvq);
+	virtio_free_vqs(vi->vdev);
 	unregister_netdev(vi->dev);
 
 	while (vi->pages)
Signature · 1 line
1.6.0.6
author maintainer
#14 lore raw

PATCH 5/8 virtio_net: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_net.
These will be required for MSI support.

---
 drivers/net/virtio_net.c |   16 +++++++++++++---
 1 files changed, 13 insertions(+), 3 deletions(-)
drivers/net/virtio_net.c +13 -3
index 9c82a39..fbe8a70 100644
@@ -841,6 +841,7 @@ static int virtnet_probe(struct virtio_device *vdev)
 	int err;
 	struct net_device *dev;
 	struct virtnet_info *vi;
+	bool control_vq;
 
 	/* Allocate ourselves a network device with room for our info */
 	dev = alloc_etherdev(sizeof(struct virtnet_info));
@@ -901,11 +902,17 @@ static int virtnet_probe(struct virtio_device *vdev)
 	if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
 		vi->mergeable_rx_bufs = true;
 
-	/* We expect two virtqueues, receive then send. */
+	/* We expect either two or three virtqueues: receive, send and
+	 * optionally control. */
+	control_vq = virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ);
+	err = virtio_request_vqs(vdev, control_vq ? 3 : 2);
+	if (err)
+		goto free;
+
 	vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
 	if (IS_ERR(vi->rvq)) {
 		err = PTR_ERR(vi->rvq);
-		goto free;
+		goto free_vqs;
 	}
 
 	vi->svq = vdev->config->find_vq(vdev, 1, skb_xmit_done);
@@ -914,7 +921,7 @@ static int virtnet_probe(struct virtio_device *vdev)
 		goto free_recv;
 	}
 
-	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
+	if (control_vq) {
 		vi->cvq = vdev->config->find_vq(vdev, 2, NULL);
 		if (IS_ERR(vi->cvq)) {
 			err = PTR_ERR(vi->svq);
@@ -965,6 +972,8 @@ free_send:
 	vdev->config->del_vq(vi->svq);
 free_recv:
 	vdev->config->del_vq(vi->rvq);
+free_vqs:
+	virtio_free_vqs(vi->vdev);
 free:
 	free_netdev(dev);
 	return err;
@@ -994,6 +1003,7 @@ static void virtnet_remove(struct virtio_device *vdev)
 	vdev->config->del_vq(vi->rvq);
 	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
 		vdev->config->del_vq(vi->cvq);
+	virtio_free_vqs(vi->vdev);
 	unregister_netdev(vi->dev);
 
 	while (vi->pages)
Signature · 1 line
1.6.0.6
author maintainer
#15 lore raw

PATCH 6/8 virtio_balloon: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_balloon.
These will be required for MSI support.

---
 drivers/virtio/virtio_balloon.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)
drivers/virtio/virtio_balloon.c +8 -1
index 5926826..77eac2b 100644
@@ -217,10 +217,14 @@ static int virtballoon_probe(struct virtio_device *vdev)
 	vb->vdev = vdev;
 
 	/* We expect two virtqueues. */
+	err = virtio_request_vqs(vdev, 2);
+	if (err)
+		goto out_free_vb;
+
 	vb->inflate_vq = vdev->config->find_vq(vdev, 0, balloon_ack);
 	if (IS_ERR(vb->inflate_vq)) {
 		err = PTR_ERR(vb->inflate_vq);
-		goto out_free_vb;
+		goto out_free_vqs;
 	}
 
 	vb->deflate_vq = vdev->config->find_vq(vdev, 1, balloon_ack);
@@ -244,6 +248,8 @@ out_del_deflate_vq:
 	vdev->config->del_vq(vb->deflate_vq);
 out_del_inflate_vq:
 	vdev->config->del_vq(vb->inflate_vq);
+out_free_vqs:
+	virtio_free_vqs(vdev);
 out_free_vb:
 	kfree(vb);
 out:
@@ -265,6 +271,7 @@ static void virtballoon_remove(struct virtio_device *vdev)
 
 	vdev->config->del_vq(vb->deflate_vq);
 	vdev->config->del_vq(vb->inflate_vq);
+	virtio_free_vqs(vdev);
 	kfree(vb);
 }
 
Signature · 1 line
1.6.0.6
author maintainer
#16 lore raw

PATCH 6/8 virtio_balloon: add request_vqs/free_vqs calls

In reply to a message not in the archive

Add request_vqs/free_vqs calls to virtio_balloon.
These will be required for MSI support.

---
 drivers/virtio/virtio_balloon.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)
drivers/virtio/virtio_balloon.c +8 -1
index 5926826..77eac2b 100644
@@ -217,10 +217,14 @@ static int virtballoon_probe(struct virtio_device *vdev)
 	vb->vdev = vdev;
 
 	/* We expect two virtqueues. */
+	err = virtio_request_vqs(vdev, 2);
+	if (err)
+		goto out_free_vb;
+
 	vb->inflate_vq = vdev->config->find_vq(vdev, 0, balloon_ack);
 	if (IS_ERR(vb->inflate_vq)) {
 		err = PTR_ERR(vb->inflate_vq);
-		goto out_free_vb;
+		goto out_free_vqs;
 	}
 
 	vb->deflate_vq = vdev->config->find_vq(vdev, 1, balloon_ack);
@@ -244,6 +248,8 @@ out_del_deflate_vq:
 	vdev->config->del_vq(vb->deflate_vq);
 out_del_inflate_vq:
 	vdev->config->del_vq(vb->inflate_vq);
+out_free_vqs:
+	virtio_free_vqs(vdev);
 out_free_vb:
 	kfree(vb);
 out:
@@ -265,6 +271,7 @@ static void virtballoon_remove(struct virtio_device *vdev)
 
 	vdev->config->del_vq(vb->deflate_vq);
 	vdev->config->del_vq(vb->inflate_vq);
+	virtio_free_vqs(vdev);
 	kfree(vb);
 }
 
Signature · 1 line
1.6.0.6
author maintainer
#17 lore raw

PATCH 7/8 virtio_pci: split up vp_interrupt

In reply to a message not in the archive

This 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 |   45 +++++++++++++++++++++++++++++++++---------
 1 files changed, 35 insertions(+), 10 deletions(-)
drivers/virtio/virtio_pci.c +35 -10
index 330aacb..151538c 100644
@@ -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,14 +215,11 @@ 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 (isr & VIRTIO_PCI_ISR_CONFIG)
+		vp_config_changed(irq, opaque);
 
-		if (drv && drv->config_changed)
-			drv->config_changed(&vp_dev->vdev);
-	}
+	return vp_vring_interrupt(irq, opaque);
+}
 
 	spin_lock_irqsave(&vp_dev->lock, flags);
 	list_for_each_entry(info, &vp_dev->virtqueues, node) {
Signature · 1 line
1.6.0.6
author maintainer
#18 lore raw

PATCH 7/8 virtio_pci: split up vp_interrupt

In reply to a message not in the archive

This 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 |   45 +++++++++++++++++++++++++++++++++---------
 1 files changed, 35 insertions(+), 10 deletions(-)
drivers/virtio/virtio_pci.c +35 -10
index 330aacb..151538c 100644
@@ -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,14 +215,11 @@ 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 (isr & VIRTIO_PCI_ISR_CONFIG)
+		vp_config_changed(irq, opaque);
 
-		if (drv && drv->config_changed)
-			drv->config_changed(&vp_dev->vdev);
-	}
+	return vp_vring_interrupt(irq, opaque);
+}
 
 	spin_lock_irqsave(&vp_dev->lock, flags);
 	list_for_each_entry(info, &vp_dev->virtqueues, node) {
Signature · 1 line
1.6.0.6
author maintainer
#19 lore raw

PATCH 8/8 virtio_pci: optional MSI-X support

In reply to a message not in the archive

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 |  147 ++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 132 insertions(+), 15 deletions(-)
drivers/virtio/virtio_pci.c +132 -15
index 151538c..20bdc8c 100644
@@ -42,8 +42,33 @@ struct virtio_pci_device
 	/* a list of queues so we can dispatch IRQs */
 	spinlock_t lock;
 	struct list_head virtqueues;
+
+	/* MSI-X support */
+	struct msix_entry *msix_entries;
+	/* Name strings for interrupts. This size should be enough,
+	 * and I'm too lazy to allocate each name separately. */
+	char (*msix_names)[256];
+	/* Number of vectors configured at startup (excludes per-virtqueue
+	 * vectors if any) */
+	unsigned msix_preset_vectors;
+	/* Number of per-virtqueue vectors if any. */
+	unsigned msix_per_vq_vectors;
+};
+
+/* Constants for MSI-X */
+/* Use first vector for configuration changes, second and the rest for
+ * virtqueues Thus, we need at least 2 vectors for MSI. */
+enum {
+	VP_MSIX_CONFIG_VECTOR = 0,
+	VP_MSIX_VQ_VECTOR = 1,
+	VP_MSIX_MIN_VECTORS = 2
 };
 
+static inline int vq_vector(int index)
+{
+	return index + VP_MSIX_VQ_VECTOR;
+}
+
 struct virtio_pci_vq_info
 {
 	/* the actual virtqueue */
@@ -221,14 +246,92 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
 	return vp_vring_interrupt(irq, opaque);
 }
 
-	spin_lock_irqsave(&vp_dev->lock, flags);
-	list_for_each_entry(info, &vp_dev->virtqueues, node) {
-		if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
-			ret = IRQ_HANDLED;
+/* the config->free_vqs() implementation */
+static void vp_free_vqs(struct virtio_device *vdev) {
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	int i;
+
+	for (i = 0; i < vp_dev->msix_preset_vectors; ++i)
+		free_irq(vp_dev->msix_entries[i].vector, vp_dev);
+
+	if (!vp_dev->msix_preset_vectors)
+		free_irq(vp_dev->pci_dev->irq, vp_dev);
+}
+
+/* the config->request_vqs() implementation */
+static int vp_request_vqs(struct virtio_device *vdev, unsigned max_vqs) {
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	const char *name = dev_name(&vp_dev->vdev.dev);
+	unsigned i, vectors;
+	int err = -ENOMEM;
+
+	/* We need at most one vector per queue and one for config changes */
+	vectors = vq_vector(max_vqs);
+	vp_dev->msix_entries = kmalloc(vectors * sizeof *vp_dev->msix_entries,
+				       GFP_KERNEL);
+	if (!vp_dev->msix_entries)
+		goto error_entries;
+	vp_dev->msix_names = kmalloc(vectors * sizeof *vp_dev->msix_names,
+				     GFP_KERNEL);
+	if (!vp_dev->msix_names)
+		goto error_names;
+
+	snprintf(vp_dev->msix_names[VP_MSIX_CONFIG_VECTOR],
+		 sizeof *vp_dev->msix_names, "%s-config", name);
+	for (i = 0; i < max_vqs; ++i)
+		snprintf(vp_dev->msix_names[vq_vector(i)],
+			 sizeof *vp_dev->msix_names, "%s-vq-%d", name, i);
+
+	vp_dev->msix_preset_vectors = 1;
+	vp_dev->msix_per_vq_vectors = max_vqs;
+	for (;;) {
+		err = pci_enable_msix(vp_dev->pci_dev, vp_dev->msix_entries,
+				      vectors);
+		/* Error out if not enough vectors */
+		if (err > 0 && err < VP_MSIX_MIN_VECTORS)
+			err = -EBUSY;
+		if (err <= 0)
+			break;
+		/* Not enough vectors for all queues. Retry, disabling
+		 * per-queue interrupts */
+		vectors = VP_MSIX_MIN_VECTORS;
+		vp_dev->msix_preset_vectors = VP_MSIX_MIN_VECTORS;
+		vp_dev->msix_per_vq_vectors = 0;
+		snprintf(vp_dev->msix_names[VP_MSIX_VQ_VECTOR],
+			 sizeof *vp_dev->msix_names, "%s-vq", name);
 	}
-	spin_unlock_irqrestore(&vp_dev->lock, flags);
 
-	return ret;
+	if (err) {
+		/* Can't allocate enough MSI-X vectors, use regular interrupt */
+		vp_dev->msix_preset_vectors = 0;
+		vp_dev->msix_per_vq_vectors = 0;
+		/* Register a handler for the queue with the PCI device's
+		 * interrupt */
+		err = request_irq(vp_dev->pci_dev->irq, vp_interrupt,
+				  IRQF_SHARED, name, vp_dev);
+		if (err)
+			goto error_irq;
+	}
+	for (i = 0; i < vp_dev->msix_preset_vectors; ++i) {
+		err = request_irq(vp_dev->msix_entries[i].vector,
+				  i == VP_MSIX_CONFIG_VECTOR ?
+				  vp_config_changed : vp_vring_interrupt,
+				  0, vp_dev->msix_names[i], vp_dev);
+		if (err) {
+			/* Set msix_preset_vectors so that only vectors we
+			 * already allocated will be freed by vp_free_vqs. */
+			vp_dev->msix_preset_vectors = i;
+			goto error_irq;
+		}
+	}
+	return 0;
+error_irq:
+	vp_free_vqs(vdev);
+	kfree(vp_dev->msix_names);
+error_names:
+	kfree(vp_dev->msix_entries);
+error_entries:
+	return err;
 }
 
 /* the config->find_vq() implementation */
@@ -242,6 +345,10 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
 	u16 num;
 	int err;
 
+	/* If using IRQ vector per vq, make sure we have enough vectors */
+	if (vp_dev->msix_per_vq_vectors && vp_dev->msix_per_vq_vectors <= index)
+		return ERR_PTR(-ENOENT);
+
 	/* Select the queue we're interested in */
 	iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
 
@@ -285,8 +392,19 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
 	list_add(&info->node, &vp_dev->virtqueues);
 	spin_unlock_irqrestore(&vp_dev->lock, flags);
 
+	/* allocate per-vq irq if neccessary */
+	if (vp_dev->msix_per_vq_vectors) {
+		err = request_irq(vp_dev->msix_entries[vq_vector(index)].vector,
+				  vring_interrupt, 0,
+				  vp_dev->msix_names[vq_vector(index)], vq);
+		if (err)
+			goto out_request_irq;
+	}
+
 	return vq;
 
+out_request_irq:
+	vring_del_virtqueue(vq);
 out_activate_queue:
 	iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 	free_pages_exact(info->queue, size);
@@ -302,6 +420,11 @@ static void vp_del_vq(struct virtqueue *vq)
 	struct virtio_pci_vq_info *info = vq->priv;
 	unsigned long flags, size;
 
+	if (vp_dev->msix_per_vq_vectors) {
+		int vector = vq_vector(info->queue_index);
+		free_irq(vp_dev->msix_entries[vector].vector, vq);
+	}
+
 	spin_lock_irqsave(&vp_dev->lock, flags);
 	list_del(&info->node);
 	spin_unlock_irqrestore(&vp_dev->lock, flags);
@@ -323,6 +446,8 @@ static struct virtio_config_ops virtio_pci_config_ops = {
 	.get_status	= vp_get_status,
 	.set_status	= vp_set_status,
 	.reset		= vp_reset,
+	.request_vqs	= vp_request_vqs,
+	.free_vqs	= vp_free_vqs,
 	.find_vq	= vp_find_vq,
 	.del_vq		= vp_del_vq,
 	.get_features	= vp_get_features,
@@ -394,21 +519,13 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
 	vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
 	vp_dev->vdev.id.device = pci_dev->subsystem_device;
 
-	/* register a handler for the queue with the PCI device's interrupt */
-	err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
-			  dev_name(&vp_dev->vdev.dev), vp_dev);
-	if (err)
-		goto out_set_drvdata;
-
 	/* finally register the virtio device */
 	err = register_virtio_device(&vp_dev->vdev);
 	if (err)
-		goto out_req_irq;
+		goto out_set_drvdata;
 
 	return 0;
 
-out_req_irq:
-	free_irq(pci_dev->irq, vp_dev);
 out_set_drvdata:
 	pci_set_drvdata(pci_dev, NULL);
 	pci_iounmap(pci_dev, vp_dev->ioaddr);
Signature · 1 line
1.6.0.6
author maintainer
#20 lore raw

PATCH 8/8 virtio_pci: optional MSI-X support

In reply to a message not in the archive

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