[PATCH RFC 21/21] qemu: Implement support for configuring iothread to virtqueue mapping for disks

Peter Krempa pkrempa at redhat.com
Thu Mar 2 14:38:03 UTC 2023


Add validation and formatting of the commandline.

Note that this is based on Stefan's RFC series which only adds the qemu
interface:

https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg04090.html
---
 src/qemu/qemu_command.c                       |  45 +++++++
 src/qemu/qemu_validate.c                      | 117 ++++++++++++++++--
 .../iothreads-disk.x86_64-latest.args         |   4 +-
 3 files changed, 153 insertions(+), 13 deletions(-)

diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index cc476addce..d9bb984101 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1746,6 +1746,45 @@ qemuBuildDriveStr(virDomainDiskDef *disk)
 }


+static virJSONValue *
+qemuBuildDiskDeviceIothreadMappingProps(GSList *iothreads)
+{
+    g_autoptr(virJSONValue) ret = virJSONValueNewArray();
+    GSList *n;
+
+    for (n = iothreads; n; n = n->next) {
+        virDomainDiskIothreadDef *ioth = n->data;
+        g_autoptr(virJSONValue) props = NULL;
+        g_autoptr(virJSONValue) queues = NULL;
+        g_autofree char *alias = g_strdup_printf("iothread%u", ioth->id);
+        size_t i;
+
+        if (ioth->nqueues > 0) {
+            queues = virJSONValueNewArray();
+
+            for (i = 0; i < ioth->nqueues; i++) {
+                g_autoptr(virJSONValue) vq = virJSONValueNewNumberUint(ioth->queues[i]);
+
+                if (virJSONValueArrayAppend(queues, &vq))
+                    return NULL;
+            }
+        }
+
+        if (virJSONValueObjectAdd(&props,
+                                  "s:iothread", alias,
+                                  "A:vqs", &queues,
+                                  NULL) < 0)
+            return NULL;
+
+
+        if (virJSONValueArrayAppend(ret, &props))
+            return NULL;
+    }
+
+    return g_steal_pointer(&ret);
+}
+
+
 virJSONValue *
 qemuBuildDiskDeviceProps(const virDomainDef *def,
                          virDomainDiskDef *disk,
@@ -1804,11 +1843,16 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,

     case VIR_DOMAIN_DISK_BUS_VIRTIO: {
         virTristateSwitch scsi = VIR_TRISTATE_SWITCH_ABSENT;
+        g_autoptr(virJSONValue) iothreadMapping = NULL;
         g_autofree char *iothread = NULL;

         if (disk->iothread > 0)
             iothread = g_strdup_printf("iothread%u", disk->iothread);

+        if (disk->iothreads &&
+            !(iothreadMapping = qemuBuildDiskDeviceIothreadMappingProps(disk->iothreads)))
+            return NULL;
+
         if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_VHOST_USER &&
             virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SCSI)) {
             /* if sg_io is true but the scsi option isn't supported,
@@ -1832,6 +1876,7 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,
                                   "T:scsi", scsi,
                                   "p:num-queues", disk->queues,
                                   "p:queue-size", disk->queue_size,
+                                  "A:iothread-vq-mapping", &iothreadMapping,
                                   NULL) < 0)
             return NULL;
     }
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index c877aa73d4..79096a2fcf 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -2822,12 +2822,24 @@ qemuValidateDomainDeviceDefDiskSerial(const char *value)
 }


-static bool
+static int
 qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def,
-                                         const virDomainDiskDef *disk)
+                                         const virDomainDiskDef *disk,
+                                         virQEMUCaps *qemuCaps)
 {
+    if (disk->iothread == 0 && !disk->iothreads)
+        return 0;
+
     switch ((virDomainDiskBus)disk->bus) {
     case VIR_DOMAIN_DISK_BUS_VIRTIO:
+        if (disk->iothreads) {
+            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_IOTHREAD_MAPPING)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("IOThread mapping for disk '%s' is not available with this QEMU binary"),
+                               disk->dst);
+                return -1;
+            }
+        }
         break;

     case VIR_DOMAIN_DISK_BUS_IDE:
@@ -2843,18 +2855,101 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def,
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                        _("IOThreads not available for bus %s target %s"),
                        virDomainDiskBusTypeToString(disk->bus), disk->dst);
-        return false;
+        return -1;
     }

-    /* Can we find the disk iothread in the iothreadid list? */
-    if (!virDomainIOThreadIDFind(def, disk->iothread)) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("Disk iothread '%u' not defined in iothreadid"),
-                       disk->iothread);
-        return false;
+    if (disk->iothreads) {
+        virDomainDiskIothreadDef *first_ioth = disk->iothreads->data;
+        g_autoptr(virBitmap) queueMap = NULL;
+        g_autoptr(GHashTable) iothreads = virHashNew(NULL);
+        ssize_t unused;
+        GSList *n;
+
+        if (first_ioth->queues) {
+            if (disk->queues == 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               _("disk 'queue' count must be configured for explicit iothread to queue mapping"));
+                return -1;
+            }
+
+            queueMap = virBitmapNew(disk->queues);
+        }
+
+        /* we are validating that:
+         * - there are no duplicate iothreads
+         * - there are only valid iothreads
+         * - if queue mapping is provided
+         *    - queue is in range
+         *    - it must be provided for all assigned iothreads
+         *    - it must be provided for all queues
+         *    - queue must be assigned only once
+         */
+        for (n = disk->iothreads; n; n = n->next) {
+            virDomainDiskIothreadDef *ioth = n->data;
+            g_autofree char *alias = g_strdup_printf("iothread%u", ioth->id);
+            size_t i;
+
+            if (g_hash_table_contains(iothreads, alias)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("Duplicate mapping for iothread '%u'"), ioth->id);
+                return -1;
+            }
+
+            g_hash_table_insert(iothreads, g_steal_pointer(&alias), NULL);
+
+            if (!virDomainIOThreadIDFind(def, ioth->id)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("Disk iothread '%u' not defined in iothreadid"),
+                               ioth->id);
+                return -1;
+            }
+
+            if (!!queueMap != !!ioth->queues) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               _("iothread to queue mapping must be provided for all iothreads or for none"));
+                return -1;
+            }
+
+            for (i = 0; i < ioth->nqueues; i++) {
+                bool hasMapping;
+
+                if (virBitmapGetBit(queueMap, ioth->queues[i], &hasMapping) < 0) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("disk iothread queue '%u' mapping out of range"),
+                                   ioth->queues[i]);
+                    return -1;
+                }
+
+                if (hasMapping) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("disk iothread queue '%u' is already assigned"),
+                                   ioth->queues[i]);
+                    return -1;
+                }
+
+                ignore_value(virBitmapSetBit(queueMap, ioth->queues[i]));
+
+            }
+        }
+
+        if (queueMap) {
+            if ((unused = virBitmapNextClearBit(queueMap, -1)) >= 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("missing iothread mapping for queue '%zd'"),
+                               unused);
+                return -1;
+            }
+        }
+    } else {
+        if (!virDomainIOThreadIDFind(def, disk->iothread)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("Disk iothread '%u' not defined in iothreadid"),
+                           disk->iothread);
+            return -1;
+        }
     }

-    return true;
+    return 0;
 }


@@ -3147,7 +3242,7 @@ qemuValidateDomainDeviceDefDiskFrontend(const virDomainDiskDef *disk,
         qemuValidateDomainDeviceDefDiskSerial(disk->serial) < 0)
         return -1;

-    if (disk->iothread && !qemuValidateDomainDeviceDefDiskIOThreads(def, disk))
+    if (qemuValidateDomainDeviceDefDiskIOThreads(def, disk, qemuCaps))
         return -1;

     return 0;
diff --git a/tests/qemuxml2argvdata/iothreads-disk.x86_64-latest.args b/tests/qemuxml2argvdata/iothreads-disk.x86_64-latest.args
index d1953327a7..05729acde5 100644
--- a/tests/qemuxml2argvdata/iothreads-disk.x86_64-latest.args
+++ b/tests/qemuxml2argvdata/iothreads-disk.x86_64-latest.args
@@ -37,10 +37,10 @@ XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
 -device '{"driver":"virtio-blk-pci","iothread":"iothread1","bus":"pci.0","addr":"0x4","drive":"libvirt-3-format","id":"virtio-disk1","bootindex":1}' \
 -blockdev '{"driver":"file","filename":"/var/lib/libvirt/images/iothrtest2.img","node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap"}' \
 -blockdev '{"node-name":"libvirt-2-format","read-only":false,"driver":"raw","file":"libvirt-2-storage"}' \
--device '{"driver":"virtio-blk-pci","num-queues":4,"bus":"pci.0","addr":"0x2","drive":"libvirt-2-format","id":"virtio-disk2"}' \
+-device '{"driver":"virtio-blk-pci","num-queues":4,"iothread-vq-mapping":[{"iothread":"iothread2","vqs":[1,3]},{"iothread":"iothread3","vqs":[0,2]}],"bus":"pci.0","addr":"0x2","drive":"libvirt-2-format","id":"virtio-disk2"}' \
 -blockdev '{"driver":"file","filename":"/var/lib/libvirt/images/iothrtest3.img","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
 -blockdev '{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}' \
--device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x3","drive":"libvirt-1-format","id":"virtio-disk3"}' \
+-device '{"driver":"virtio-blk-pci","iothread-vq-mapping":[{"iothread":"iothread4"},{"iothread":"iothread1"}],"bus":"pci.0","addr":"0x3","drive":"libvirt-1-format","id":"virtio-disk3"}' \
 -audiodev '{"id":"audio1","driver":"none"}' \
 -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
 -msg timestamp=on
-- 
2.39.2



More information about the libvir-list mailing list