[ipxe-devel] [PATCH 4/4] [virtio] Remove queue size limit in legacy virtio

Ladi Prosek lprosek at redhat.com
Fri Dec 16 14:30:05 UTC 2016


Virtio 0.9 implementation was limited to the maximum virtqueue size of
MAX_QUEUE_NUM and the virtio-net driver would fail to initialize on hosts
exceeding this limit.

This commit lifts the restriction by allocating the queue memory based on
the actual queue size instead of using a fixed maximum. Note that virtio
1.0 still uses the MAX_QUEUE_NUM constant to cap the size (unfortunately
this functionality is not available in virtio 0.9).

Signed-off-by: Ladi Prosek <lprosek at redhat.com>
---
 src/drivers/bus/virtio-pci.c   | 48 ++++++++++++++++++++++++++++++++++--------
 src/drivers/net/virtio-net.c   |  1 +
 src/include/ipxe/virtio-pci.h  |  2 ++
 src/include/ipxe/virtio-ring.h |  6 ++----
 4 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c
index 30c13b9..a59b27a 100644
--- a/src/drivers/bus/virtio-pci.c
+++ b/src/drivers/bus/virtio-pci.c
@@ -21,11 +21,37 @@
 #include "ipxe/virtio-pci.h"
 #include "ipxe/virtio-ring.h"
 
+static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num)
+{
+    size_t queue_size = PAGE_MASK + vring_size(num);
+    size_t vdata_size = num * sizeof(void *);
+
+    vq->queue = zalloc(queue_size + vdata_size);
+    if (!vq->queue) {
+        return -ENOMEM;
+    }
+
+    /* vdata immediately follows the ring */
+    vq->vdata = (void **)(vq->queue + queue_size);
+
+    return 0;
+}
+
+void vp_free_vq(struct vring_virtqueue *vq)
+{
+    if (vq->queue) {
+        free(vq->queue);
+        vq->queue = NULL;
+        vq->vdata = NULL;
+    }
+}
+
 int vp_find_vq(unsigned int ioaddr, int queue_index,
                struct vring_virtqueue *vq)
 {
    struct vring * vr = &vq->vring;
    u16 num;
+   int rc;
 
    /* select the queue */
 
@@ -39,11 +65,6 @@ int vp_find_vq(unsigned int ioaddr, int queue_index,
            return -1;
    }
 
-   if (num > MAX_QUEUE_NUM) {
-           DBG("VIRTIO-PCI ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM);
-           return -1;
-   }
-
    /* check if the queue is already active */
 
    if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
@@ -54,8 +75,12 @@ int vp_find_vq(unsigned int ioaddr, int queue_index,
    vq->queue_index = queue_index;
 
    /* initialize the queue */
-
-   vring_init(vr, num, (unsigned char*)&vq->queue);
+   rc = vp_alloc_vq(vq, num);
+   if (rc) {
+           DBG("VIRTIO-PCI ERROR: failed to allocate queue memory\n");
+           return rc;
+   }
+   vring_init(vr, num, vq->queue);
 
    /* activate the queue
     *
@@ -351,7 +376,7 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev,
             return -ENOENT;
 
         if (size & (size - 1)) {
-            DBG("VIRTIO-PCI %p: bad queue size %d", vdev, size);
+            DBG("VIRTIO-PCI %p: bad queue size %d\n", vdev, size);
             return -EINVAL;
         }
 
@@ -368,7 +393,12 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev,
         /* get offset of notification word for this vq */
         off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off));
 
-        vring_init(&vq->vring, size, (unsigned char *)vq->queue);
+        err = vp_alloc_vq(vq, size);
+        if (err) {
+            DBG("VIRTIO-PCI %p: failed to allocate queue memory\n", vdev);
+            return err;
+        }
+        vring_init(&vq->vring, size, vq->queue);
 
         /* activate the queue */
         vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size));
diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c
index de3333d..eaf6ed3 100644
--- a/src/drivers/net/virtio-net.c
+++ b/src/drivers/net/virtio-net.c
@@ -185,6 +185,7 @@ static void virtnet_free_virtqueues ( struct net_device *netdev ) {
 
 	for ( i = 0; i < QUEUE_NB; i++ ) {
 		virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification );
+		vp_free_vq ( &virtnet->virtqueue[i] );
 	}
 
 	free ( virtnet->virtqueue );
diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h
index f3c9b17..f3074f1 100644
--- a/src/include/ipxe/virtio-pci.h
+++ b/src/include/ipxe/virtio-pci.h
@@ -196,9 +196,11 @@ static inline void vp_del_vq(unsigned int ioaddr, int queue_index)
 
 struct vring_virtqueue;
 
+void vp_free_vq(struct vring_virtqueue *vq);
 int vp_find_vq(unsigned int ioaddr, int queue_index,
                struct vring_virtqueue *vq);
 
+
 /* Virtio 1.0 I/O routines abstract away the three possible HW access
  * mechanisms - memory, port I/O, and PCI cfg space access. Also built-in
  * are endianness conversions - to LE on write and from LE on read. */
diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h
index d2ded30..e608e62 100644
--- a/src/include/ipxe/virtio-ring.h
+++ b/src/include/ipxe/virtio-ring.h
@@ -71,14 +71,12 @@ struct vring {
          + PAGE_MASK) & ~PAGE_MASK) + \
          (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num))
 
-typedef unsigned char virtio_queue_t[PAGE_MASK + vring_size(MAX_QUEUE_NUM)];
-
 struct vring_virtqueue {
-   virtio_queue_t queue;
+   unsigned char *queue;
    struct vring vring;
    u16 free_head;
    u16 last_used_idx;
-   void *vdata[MAX_QUEUE_NUM];
+   void **vdata;
    /* PCI */
    int queue_index;
    struct virtio_pci_region notification;
-- 
2.7.4




More information about the ipxe-devel mailing list