[PATCH RFC 00/13] qemu: Add support for iothread to virtqueue mapping for 'virtio-scsi'

The first part of the series refactors the existing code for reuse and then uses the new helpers to implement the feature. Note that this series is in RFC state as the qemu patches are still being discussed. Thus also the capability bump is not final. Also note that we should discuss the libvirt interface perhaps as it turns out that 'virtio-scsi' has two internal queues that need to be mapped as well. For now I've solved this administratively by instructing users to also add mapping for queue '0' and '1' which are the special ones in case of virtio-scsi. qemu-patches: https://mail.gnu.org/archive/html/qemu-devel/2025-02/msg02810.html Peter Krempa (13): conf: Rename 'virDomainDiskIothreadDef' to 'virDomainIothreadMappingDef' conf: domain: Extract code for parsing and formatting iotrhead mapping definition hypervisor: domain: Extract code for checking iothread usage qemu: command: Rename 'qemuBuildDiskDeviceIothreadMappingProps' to 'qemuBuildIothreadMappingProps' qemu: validate: Extract iothread mapping validation code qemuValidateCheckSCSIControllerIOThreads: Return '0' and '-1' instead of bools conf: schemas: Rename 'diskDriverIothreads' to 'iothreadMapping' conf: Validate that iohtreads are used only with 'virtio-scsi' controllers qemucapabilitiestest: Update 'caps_10.0.0_x86_64' to XXXXXX qemu: capabilities: Introduce QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING conf: Add support for iothread to queue mapping config for 'virtio-scsi' qemu: Implement support for iothread <-> virtqueue mapping for 'virtio-scsi' controllers qemuxmlconftest: Add 'iothreads-virtio-scsi-mapping' case docs/formatdomain.rst | 33 +++ src/conf/domain_conf.c | 157 +++++++----- src/conf/domain_conf.h | 11 +- src/conf/domain_validate.c | 19 ++ src/conf/schemas/domaincommon.rng | 7 +- src/hypervisor/domain_driver.c | 34 +-- src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 12 +- src/qemu/qemu_domain.c | 4 +- src/qemu/qemu_validate.c | 234 ++++++++++-------- .../caps_10.0.0_x86_64.replies | 12 +- .../caps_10.0.0_x86_64.xml | 3 +- ...r-virtio-serial-iothread.x86_64-latest.err | 1 + .../controller-virtio-serial-iothread.xml | 27 ++ ...ads-virtio-scsi-mapping.x86_64-latest.args | 39 +++ ...eads-virtio-scsi-mapping.x86_64-latest.xml | 54 ++++ .../iothreads-virtio-scsi-mapping.xml | 46 ++++ tests/qemuxmlconftest.c | 3 + 19 files changed, 506 insertions(+), 193 deletions(-) create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.xml -- 2.48.1

The iothread mapping will be also possible for 'virtio-scsi' controllers so rename the corresponding structs to a generic name. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/domain_conf.c | 10 +++++----- src/conf/domain_conf.h | 10 +++++----- src/hypervisor/domain_driver.c | 2 +- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_domain.c | 4 ++-- src/qemu/qemu_validate.c | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 87f87bbe56..22b9dad9ee 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2368,7 +2368,7 @@ virDomainDefGetVcpusTopology(const virDomainDef *def, void -virDomainDiskIothreadDefFree(virDomainDiskIothreadDef *def) +virDomainIothreadMappingDefFree(virDomainIothreadMappingDef *def) { if (!def) return; @@ -2426,7 +2426,7 @@ virDomainDiskDefFree(virDomainDiskDef *def) g_free(def->virtio); virDomainDeviceInfoClear(&def->info); virObjectUnref(def->privateData); - g_slist_free_full(def->iothreads, (GDestroyNotify) virDomainDiskIothreadDefFree); + g_slist_free_full(def->iothreads, (GDestroyNotify) virDomainIothreadMappingDefFree); g_free(def); } @@ -7991,7 +7991,7 @@ virDomainDiskDefDriverParseXML(virDomainDiskDef *def, return -1; if ((iothreadsNode = virXMLNodeGetSubelement(cur, "iothreads"))) { - g_autoslist(virDomainDiskIothreadDef) ioth = NULL; + g_autoslist(virDomainIothreadMappingDef) ioth = NULL; g_autoptr(GPtrArray) iothreadNodes = NULL; if ((iothreadNodes = virXMLNodeGetSubelementList(iothreadsNode, "iothread"))) { @@ -7999,7 +7999,7 @@ virDomainDiskDefDriverParseXML(virDomainDiskDef *def, for (i = 0; i < iothreadNodes->len; i++) { xmlNodePtr iothNode = g_ptr_array_index(iothreadNodes, i); - g_autoptr(virDomainDiskIothreadDef) iothdef = g_new0(virDomainDiskIothreadDef, 1); + g_autoptr(virDomainIothreadMappingDef) iothdef = g_new0(virDomainIothreadMappingDef, 1); g_autoptr(GPtrArray) queueNodes = NULL; if (virXMLPropUInt(iothNode, "id", 10, VIR_XML_PROP_REQUIRED, @@ -23240,7 +23240,7 @@ virDomainDiskDefFormatDriver(virBuffer *buf, GSList *n; for (n = disk->iothreads; n; n = n->next) { - virDomainDiskIothreadDef *iothDef = n->data; + virDomainIothreadMappingDef *iothDef = n->data; g_auto(virBuffer) iothreadAttrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) iothreadChildBuf = VIR_BUFFER_INIT_CHILD(&iothreadsChildBuf); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e51c74b6d1..10dd5497ac 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -507,7 +507,7 @@ typedef enum { VIR_ENUM_DECL(virDomainSnapshotLocation); -struct _virDomainDiskIothreadDef { +struct _virDomainIothreadMappingDef { unsigned int id; /* optional list of virtqueues the iothread should handle */ @@ -515,9 +515,9 @@ struct _virDomainDiskIothreadDef { size_t nqueues; }; -typedef struct _virDomainDiskIothreadDef virDomainDiskIothreadDef; -void virDomainDiskIothreadDefFree(virDomainDiskIothreadDef *def); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainDiskIothreadDef, virDomainDiskIothreadDefFree); +typedef struct _virDomainIothreadMappingDef virDomainIothreadMappingDef; +void virDomainIothreadMappingDefFree(virDomainIothreadMappingDef *def); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainIothreadMappingDef, virDomainIothreadMappingDefFree); /* Stores the virtual disk configuration */ @@ -574,7 +574,7 @@ struct _virDomainDiskDef { virDomainDeviceSGIO sgio; virDomainDiskDiscard discard; unsigned int iothread; /* unused = 0, > 0 specific thread # */ - GSList *iothreads; /* List of virDomainDiskIothreadsDef */ + GSList *iothreads; /* List of virDomainIothreadMappingDef */ virDomainDiskDetectZeroes detect_zeroes; virTristateSwitch discard_no_unref; char *domain_name; /* backend domain name */ diff --git a/src/hypervisor/domain_driver.c b/src/hypervisor/domain_driver.c index bea84cd09d..9fbfec9f04 100644 --- a/src/hypervisor/domain_driver.c +++ b/src/hypervisor/domain_driver.c @@ -562,7 +562,7 @@ virDomainDriverDelIOThreadCheck(virDomainDef *def, bool inuse = false; for (n = def->disks[i]->iothreads; n; n = n->next) { - virDomainDiskIothreadDef *iothread = n->data; + virDomainIothreadMappingDef *iothread = n->data; if (iothread->id == iothread_id) { inuse = true; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 7370711918..35a71b73a7 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1580,7 +1580,7 @@ qemuBuildDiskDeviceIothreadMappingProps(GSList *iothreads) GSList *n; for (n = iothreads; n; n = n->next) { - virDomainDiskIothreadDef *ioth = n->data; + virDomainIothreadMappingDef *ioth = n->data; g_autoptr(virJSONValue) props = NULL; g_autoptr(virJSONValue) queues = NULL; g_autofree char *alias = g_strdup_printf("iothread%u", ioth->id); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index cf05dca55a..7ede1a5a6e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -6646,8 +6646,8 @@ qemuDomainDiskChangeSupportedIothreads(virDomainDiskDef *disk, GSList *new = disk->iothreads; while (true) { - virDomainDiskIothreadDef *old_def; - virDomainDiskIothreadDef *new_def; + virDomainIothreadMappingDef *old_def; + virDomainIothreadMappingDef *new_def; size_t i; /* match - both empty or both at the end */ diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 76f2eafe49..fb4c9bf552 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2801,7 +2801,7 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def, } if (disk->iothreads) { - virDomainDiskIothreadDef *first_ioth = disk->iothreads->data; + virDomainIothreadMappingDef *first_ioth = disk->iothreads->data; g_autoptr(virBitmap) queueMap = NULL; g_autoptr(GHashTable) iothreads = virHashNew(NULL); ssize_t unused; @@ -2827,7 +2827,7 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def, * - queue must be assigned only once */ for (n = disk->iothreads; n; n = n->next) { - virDomainDiskIothreadDef *ioth = n->data; + virDomainIothreadMappingDef *ioth = n->data; g_autofree char *alias = g_strdup_printf("iothread%u", ioth->id); size_t i; -- 2.48.1

The code will be also needed for 'virtio-scsi' controller definitions. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/domain_conf.c | 143 +++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 62 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 22b9dad9ee..1a52cda62d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7939,11 +7939,56 @@ virDomainDiskDefGeometryParse(virDomainDiskDef *def, static int -virDomainDiskDefDriverParseXML(virDomainDiskDef *def, - xmlNodePtr cur) +virDomainIothreadMappingDefParse(xmlNodePtr driverNode, + GSList **iothreads) { xmlNodePtr iothreadsNode; + g_autoslist(virDomainIothreadMappingDef) ioth = NULL; + g_autoptr(GPtrArray) iothreadNodes = NULL; + size_t i; + + if (!(iothreadsNode = virXMLNodeGetSubelement(driverNode, "iothreads"))) + return 0; + + if (!(iothreadNodes = virXMLNodeGetSubelementList(iothreadsNode, "iothread"))) + return 0; + + for (i = 0; i < iothreadNodes->len; i++) { + xmlNodePtr iothNode = g_ptr_array_index(iothreadNodes, i); + g_autoptr(virDomainIothreadMappingDef) iothdef = g_new0(virDomainIothreadMappingDef, 1); + g_autoptr(GPtrArray) queueNodes = NULL; + + if (virXMLPropUInt(iothNode, "id", 10, VIR_XML_PROP_REQUIRED, + &iothdef->id) < 0) + return -1; + + if ((queueNodes = virXMLNodeGetSubelementList(iothNode, "queue"))) { + size_t q; + + iothdef->queues = g_new0(unsigned int, queueNodes->len); + iothdef->nqueues = queueNodes->len; + + for (q = 0; q < queueNodes->len; q++) { + xmlNodePtr queueNode = g_ptr_array_index(queueNodes, q); + + if (virXMLPropUInt(queueNode, "id", 10, VIR_XML_PROP_REQUIRED, + &(iothdef->queues[q])) < 0) + return -1; + } + } + ioth = g_slist_prepend(ioth, g_steal_pointer(&iothdef)); + } + + *iothreads = g_slist_reverse(g_steal_pointer(&ioth)); + return 0; +} + + +static int +virDomainDiskDefDriverParseXML(virDomainDiskDef *def, + xmlNodePtr cur) +{ def->driverName = virXMLPropString(cur, "name"); if (virXMLPropEnum(cur, "cache", virDomainDiskCacheTypeFromString, @@ -7990,43 +8035,8 @@ virDomainDiskDefDriverParseXML(virDomainDiskDef *def, if (virXMLPropUInt(cur, "iothread", 10, VIR_XML_PROP_NONZERO, &def->iothread) < 0) return -1; - if ((iothreadsNode = virXMLNodeGetSubelement(cur, "iothreads"))) { - g_autoslist(virDomainIothreadMappingDef) ioth = NULL; - g_autoptr(GPtrArray) iothreadNodes = NULL; - - if ((iothreadNodes = virXMLNodeGetSubelementList(iothreadsNode, "iothread"))) { - size_t i; - - for (i = 0; i < iothreadNodes->len; i++) { - xmlNodePtr iothNode = g_ptr_array_index(iothreadNodes, i); - g_autoptr(virDomainIothreadMappingDef) iothdef = g_new0(virDomainIothreadMappingDef, 1); - g_autoptr(GPtrArray) queueNodes = NULL; - - if (virXMLPropUInt(iothNode, "id", 10, VIR_XML_PROP_REQUIRED, - &iothdef->id) < 0) - return -1; - - if ((queueNodes = virXMLNodeGetSubelementList(iothNode, "queue"))) { - size_t q; - - iothdef->queues = g_new0(unsigned int, queueNodes->len); - iothdef->nqueues = queueNodes->len; - - for (q = 0; q < queueNodes->len; q++) { - xmlNodePtr queueNode = g_ptr_array_index(queueNodes, q); - - if (virXMLPropUInt(queueNode, "id", 10, VIR_XML_PROP_REQUIRED, - &(iothdef->queues[q])) < 0) - return -1; - } - } - - ioth = g_slist_prepend(ioth, g_steal_pointer(&iothdef)); - } - - def->iothreads = g_slist_reverse(g_steal_pointer(&ioth)); - } - } + if (virDomainIothreadMappingDefParse(cur, &def->iothreads) < 0) + return -1; if (virXMLPropEnum(cur, "detect_zeroes", virDomainDiskDetectZeroesTypeFromString, @@ -23161,6 +23171,37 @@ virDomainDiskDefFormatIotune(virBuffer *buf, #undef FORMAT_IOTUNE +static void +virDomainIothreadMappingDefFormat(virBuffer *buf, + GSList *iothreads) +{ + g_auto(virBuffer) iothreadsChildBuf = VIR_BUFFER_INIT_CHILD(buf); + GSList *n; + + if (!iothreads) + return; + + for (n = iothreads; n; n = n->next) { + virDomainIothreadMappingDef *iothDef = n->data; + g_auto(virBuffer) iothreadAttrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) iothreadChildBuf = VIR_BUFFER_INIT_CHILD(&iothreadsChildBuf); + + virBufferAsprintf(&iothreadAttrBuf, " id='%u'", iothDef->id); + + if (iothDef->queues) { + size_t q; + + for (q = 0; q < iothDef->nqueues; q++) + virBufferAsprintf(&iothreadChildBuf, "<queue id='%u'/>\n", iothDef->queues[q]); + } + + virXMLFormatElement(&iothreadsChildBuf, "iothread", &iothreadAttrBuf, &iothreadChildBuf); + } + + virXMLFormatElement(buf, "iothreads", NULL, &iothreadsChildBuf); +} + + static void virDomainDiskDefFormatDriver(virBuffer *buf, virDomainDiskDef *disk) @@ -23235,29 +23276,7 @@ virDomainDiskDefFormatDriver(virBuffer *buf, virXMLFormatElement(&childBuf, "metadata_cache", NULL, &metadataCacheChildBuf); } - if (disk->iothreads) { - g_auto(virBuffer) iothreadsChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf); - GSList *n; - - for (n = disk->iothreads; n; n = n->next) { - virDomainIothreadMappingDef *iothDef = n->data; - g_auto(virBuffer) iothreadAttrBuf = VIR_BUFFER_INITIALIZER; - g_auto(virBuffer) iothreadChildBuf = VIR_BUFFER_INIT_CHILD(&iothreadsChildBuf); - - virBufferAsprintf(&iothreadAttrBuf, " id='%u'", iothDef->id); - - if (iothDef->queues) { - size_t q; - - for (q = 0; q < iothDef->nqueues; q++) - virBufferAsprintf(&iothreadChildBuf, "<queue id='%u'/>\n", iothDef->queues[q]); - } - - virXMLFormatElement(&iothreadsChildBuf, "iothread", &iothreadAttrBuf, &iothreadChildBuf); - } - - virXMLFormatElement(&childBuf, "iothreads", NULL, &iothreadsChildBuf); - } + virDomainIothreadMappingDefFormat(&childBuf, disk->iothreads); virXMLFormatElement(buf, "driver", &attrBuf, &childBuf); } -- 2.48.1

s/trhead/thread/ in the commit summary On a Friday in 2025, Peter Krempa wrote:
The code will be also needed for 'virtio-scsi' controller definitions.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/domain_conf.c | 143 +++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 62 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The code will be also needed for 'virtio-scsi' controller definitions. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/hypervisor/domain_driver.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/hypervisor/domain_driver.c b/src/hypervisor/domain_driver.c index 9fbfec9f04..b7499a376f 100644 --- a/src/hypervisor/domain_driver.c +++ b/src/hypervisor/domain_driver.c @@ -537,6 +537,23 @@ virDomainDriverAddIOThreadCheck(virDomainDef *def, return 0; } + +static bool +virDomainIothreadMappingDefHasIothread(GSList *iothreads, + unsigned int iothread_id) +{ + GSList *n; + + for (n = iothreads; n; n = n->next) { + virDomainIothreadMappingDef *iothread = n->data; + + if (iothread->id == iothread_id) + return true; + } + + return false; +} + /** * virDomainDriverDelIOThreadCheck: * @def: domain definition @@ -558,19 +575,7 @@ virDomainDriverDelIOThreadCheck(virDomainDef *def, } for (i = 0; i < def->ndisks; i++) { - GSList *n; - bool inuse = false; - - for (n = def->disks[i]->iothreads; n; n = n->next) { - virDomainIothreadMappingDef *iothread = n->data; - - if (iothread->id == iothread_id) { - inuse = true; - break; - } - } - - if (inuse || + if (virDomainIothreadMappingDefHasIothread(def->disks[i]->iothreads, iothread_id) || def->disks[i]->iothread == iothread_id) { virReportError(VIR_ERR_INVALID_ARG, _("cannot remove IOThread %1$u since it is being used by disk '%2$s'"), -- 2.48.1

Prepare for reuse of the code for 'virtio-scsi' controller. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_command.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 35a71b73a7..b76fec27c5 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1574,7 +1574,7 @@ qemuBuildDriveStr(virDomainDiskDef *disk) static virJSONValue * -qemuBuildDiskDeviceIothreadMappingProps(GSList *iothreads) +qemuBuildIothreadMappingProps(GSList *iothreads) { g_autoptr(virJSONValue) ret = virJSONValueNewArray(); GSList *n; @@ -1687,7 +1687,7 @@ qemuBuildDiskDeviceProps(const virDomainDef *def, iothread = g_strdup_printf("iothread%u", disk->iothread); if (disk->iothreads && - !(iothreadMapping = qemuBuildDiskDeviceIothreadMappingProps(disk->iothreads))) + !(iothreadMapping = qemuBuildIothreadMappingProps(disk->iothreads))) return NULL; if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_VHOST_USER && -- 2.48.1

Extract the code to 'qemuDomainValidateIothreadMapping'. It will be reused to validate the mapping for 'virtio-scsi' iothreads. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_validate.c | 192 +++++++++++++++++++++------------------ 1 file changed, 104 insertions(+), 88 deletions(-) diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index fb4c9bf552..8ac3e65e9d 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2764,6 +2764,102 @@ qemuValidateDomainDeviceDefDiskSerial(const char *value) } +static int +qemuDomainValidateIothreadMapping(const virDomainDef *def, + GSList *iothreads, + size_t queues) +{ + virDomainIothreadMappingDef *first_ioth; + g_autoptr(virBitmap) queueMap = NULL; + g_autoptr(GHashTable) iothreadMap = virHashNew(NULL); + ssize_t unused; + GSList *n; + + if (!iothreads) + return 0; + + first_ioth = iothreads->data; + + if (first_ioth->queues) { + if (queues == 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'queue' count must be configured for explicit iothread to queue mapping")); + return -1; + } + + queueMap = virBitmapNew(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 = iothreads; n; n = n->next) { + virDomainIothreadMappingDef *ioth = n->data; + g_autofree char *alias = g_strdup_printf("iothread%u", ioth->id); + size_t i; + + if (g_hash_table_contains(iothreadMap, alias)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("duplicate mapping for iothread '%1$u'"), ioth->id); + return -1; + } + + g_hash_table_insert(iothreadMap, g_steal_pointer(&alias), NULL); + + if (!virDomainIOThreadIDFind(def, ioth->id)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("iothread '%1$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, + _("iothread queue '%1$u' mapping out of range"), + ioth->queues[i]); + return -1; + } + + if (hasMapping) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("iothread queue '%1$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 '%1$zd'"), + unused); + return -1; + } + } + + return 0; +} + + static int qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def, const virDomainDiskDef *disk, @@ -2800,95 +2896,15 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def, return -1; } - if (disk->iothreads) { - virDomainIothreadMappingDef *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) { - virDomainIothreadMappingDef *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 '%1$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 '%1$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 '%1$u' mapping out of range"), - ioth->queues[i]); - return -1; - } - - if (hasMapping) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk iothread queue '%1$u' is already assigned"), - ioth->queues[i]); - return -1; - } - - ignore_value(virBitmapSetBit(queueMap, ioth->queues[i])); - - } - } + if (qemuDomainValidateIothreadMapping(def, disk->iothreads, disk->queues) < 0) + return -1; - if (queueMap) { - if ((unused = virBitmapNextClearBit(queueMap, -1)) >= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("missing iothread mapping for queue '%1$zd'"), - unused); - return -1; - } - } - } else { - if (!virDomainIOThreadIDFind(def, disk->iothread)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Disk iothread '%1$u' not defined in iothreadid"), - disk->iothread); - return -1; - } + if (disk->iothread != 0 && + !virDomainIOThreadIDFind(def, disk->iothread)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Disk iothread '%1$u' not defined in iothreadid"), + disk->iothread); + return -1; } return 0; -- 2.48.1

The function reports libvirt errors so stick with the usual '0' and '-1' return values. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_validate.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 8ac3e65e9d..2185e31a27 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -3599,19 +3599,19 @@ qemuValidateDomainDeviceDefControllerIDE(const virDomainControllerDef *controlle * Returns true if either supported or there are no iothreads for controller; * otherwise, returns false if configuration is not quite right. */ -static bool +static int qemuValidateCheckSCSIControllerIOThreads(const virDomainControllerDef *controller, const virDomainDef *def) { if (!controller->iothread) - return true; + return 0; if (controller->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && controller->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && controller->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("virtio-scsi IOThreads only available for virtio pci and virtio ccw controllers")); - return false; + return -1; } /* Can we find the controller iothread in the iothreadid list? */ @@ -3619,10 +3619,10 @@ qemuValidateCheckSCSIControllerIOThreads(const virDomainControllerDef *controlle virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("controller iothread '%1$u' not defined in iothreadid"), controller->iothread); - return false; + return -1; } - return true; + return 0; } @@ -3634,7 +3634,7 @@ qemuValidateDomainDeviceDefControllerSCSI(const virDomainControllerDef *controll case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL: - if (!qemuValidateCheckSCSIControllerIOThreads(controller, def)) + if (qemuValidateCheckSCSIControllerIOThreads(controller, def) < 0) return -1; break; -- 2.48.1

The schema definition will be reused when adding iotrhead<->virtuqueue mapping for 'virtio-scsi'. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/schemas/domaincommon.rng | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 96cedb85e8..755a43e893 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -2606,7 +2606,7 @@ </element> </define> - <define name="diskDriverIothreads"> + <define name="iothreadMapping"> <element name="iothreads"> <oneOrMore> <element name="iothread"> @@ -2687,7 +2687,7 @@ </element> </optional> <optional> - <ref name="diskDriverIothreads"/> + <ref name="iothreadMapping"/> </optional> </interleave> </element> -- 2.48.1

On a Friday in 2025, Peter Krempa wrote:
The schema definition will be reused when adding iotrhead<->virtuqueue
iothread<->virtqueue
mapping for 'virtio-scsi'.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/schemas/domaincommon.rng | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The documentation states: ``iothread`` Supported for controller type ``scsi`` using model ``virtio-scsi`` for ``address`` types ``pci`` and ``ccw`` :since:`since 1.3.5 (QEMU 2.4)`. The The code itself didn't validate if iothread is specified for any other controller type. Add test case showing the issue on one example. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/domain_validate.c | 11 ++++++++ ...r-virtio-serial-iothread.x86_64-latest.err | 1 + .../controller-virtio-serial-iothread.xml | 27 +++++++++++++++++++ tests/qemuxmlconftest.c | 2 ++ 4 files changed, 41 insertions(+) create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index eb5e764c02..88e5f37651 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1260,6 +1260,17 @@ virDomainControllerDefValidate(const virDomainControllerDef *controller) } } + if (controller->iothread != 0) { + if (controller->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI || + !(controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI || + controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL || + controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("iothreads are supported only by 'virtio-scsi' controllers")); + return -1; + } + } + return 0; } diff --git a/tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err b/tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err new file mode 100644 index 0000000000..ac4d8b2370 --- /dev/null +++ b/tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err @@ -0,0 +1 @@ +XML error: 'iothread' attribute only supported for virtio scsi controllers diff --git a/tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml b/tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml new file mode 100644 index 0000000000..0b523093c7 --- /dev/null +++ b/tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml @@ -0,0 +1,27 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' cpuset='1-4,8-20,525'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='virtio-serial' index='0'> + <driver iothread='3'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </controller> + <console type='pty'> + <target type='virtio'/> + </console> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index 1f0068864a..ce48b91ac2 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -2822,6 +2822,8 @@ mymain(void) DO_TEST_CAPS_LATEST("virtio-options-rng-packed"); DO_TEST_CAPS_LATEST("virtio-options-video-packed"); + DO_TEST_CAPS_LATEST_PARSE_ERROR("controller-virtio-serial-iothread"); + DO_TEST_CAPS_LATEST("fd-memory-numa-topology"); DO_TEST_CAPS_LATEST("fd-memory-numa-topology2"); DO_TEST_CAPS_LATEST("fd-memory-numa-topology3"); -- 2.48.1

In the commit summary: *iothreads On a Friday in 2025, Peter Krempa wrote:
The documentation states:
``iothread`` Supported for controller type ``scsi`` using model ``virtio-scsi`` for ``address`` types ``pci`` and ``ccw`` :since:`since 1.3.5 (QEMU 2.4)`. The
The code itself didn't validate if iothread is specified for any other controller type.
Add test case showing the issue on one example.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/conf/domain_validate.c | 11 ++++++++ ...r-virtio-serial-iothread.x86_64-latest.err | 1 + .../controller-virtio-serial-iothread.xml | 27 +++++++++++++++++++ tests/qemuxmlconftest.c | 2 ++ 4 files changed, 41 insertions(+) create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Notable changes: - 'virtio-scsi' supports 'iothread-vq-mapping' - 'nbd-server-start' command supports 'handshake-max-seconds' argument --- .../qemucapabilitiesdata/caps_10.0.0_x86_64.replies | 12 +++++++++++- tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.replies b/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.replies index a08546ae46..5a59e08ff1 100644 --- a/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.replies +++ b/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.replies @@ -20,7 +20,7 @@ "minor": 2, "major": 9 }, - "package": "v9.2.0-1636-gffaf7f0376" + "package": "v9.2.0-1748-g9dac9d741e" }, "id": "libvirt-2" } @@ -4229,6 +4229,11 @@ "name": "addr", "type": "362" }, + { + "name": "handshake-max-seconds", + "default": null, + "type": "int" + }, { "name": "tls-creds", "default": null, @@ -28025,6 +28030,11 @@ "description": "on/off", "type": "bool" }, + { + "name": "iothread-vq-mapping", + "description": "IOThread virtqueue mapping list [{\"iothread\":\"<id>\", \"vqs\":[1,2,3,...]},...]", + "type": "IOThreadVirtQueueMappingList" + }, { "default-value": 4294967295, "name": "num_queues", diff --git a/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml index b1cf477bdc..22bb605f3f 100644 --- a/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml @@ -214,7 +214,7 @@ <flag name='blockdev-set-active'/> <version>9002050</version> <microcodeVersion>43100285</microcodeVersion> - <package>v9.2.0-1636-gffaf7f0376</package> + <package>v9.2.0-1748-g9dac9d741e</package> <arch>x86_64</arch> <hostCPU type='kvm' model='base' migratability='yes'> <property name='avx-ne-convert' type='boolean' value='false'/> -- 2.48.1

The 'virtio-scsi' controller now supports iothread<->virtqueue mapping configuration. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml | 1 + 3 files changed, 4 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 23b466c36e..19511e2333 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -728,6 +728,7 @@ VIR_ENUM_IMPL(virQEMUCaps, "machine.virt.aia", /* QEMU_CAPS_MACHINE_VIRT_AIA */ "virtio-mem-ccw", /* QEMU_CAPS_DEVICE_VIRTIO_MEM_CCW */ "blockdev-set-active", /* QEMU_CAPS_BLOCKDEV_SET_ACTIVE */ + "virtio-scsi.iothread-mapping", /* QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING */ ); @@ -1478,6 +1479,7 @@ static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsSpaprPCIHostBrid static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVirtioSCSI[] = { { "acpi-index", QEMU_CAPS_ACPI_INDEX, NULL }, + { "iothread-vq-mapping", QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING, NULL }, }; static struct virQEMUCapsDevicePropsFlags virQEMUCapsDevicePropsVfioPCI[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index ee71331a09..feca9bd185 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -707,6 +707,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_MACHINE_VIRT_AIA, /* -machine virt,aia=(none|aplic|aplic-imsic), RISC-V only */ QEMU_CAPS_DEVICE_VIRTIO_MEM_CCW, /* -device virtio-mem-ccw */ QEMU_CAPS_BLOCKDEV_SET_ACTIVE, /* blockdev-set-active QMP command supported */ + QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING, /* virtio-scsi supports per-virtqueue iothread mapping */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml index 22bb605f3f..b10de7c6d6 100644 --- a/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_10.0.0_x86_64.xml @@ -212,6 +212,7 @@ <flag name='netdev-stream-reconnect-miliseconds'/> <flag name='migrate-incoming.exit-on-error'/> <flag name='blockdev-set-active'/> + <flag name='virtio-scsi.iothread-mapping'/> <version>9002050</version> <microcodeVersion>43100285</microcodeVersion> <package>v9.2.0-1748-g9dac9d741e</package> -- 2.48.1

Upcoming qemu release will support configuring mapping iothreads to virtio queues for 'virtio-scsi' controllers in order to improve performance. Reuse the infrastructure we have from the same configuration for 'virti-blk' to implement the conf support for this feature. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- docs/formatdomain.rst | 33 +++++++++++++++++++++++++++++++ src/conf/domain_conf.c | 10 +++++++++- src/conf/domain_conf.h | 1 + src/conf/domain_validate.c | 10 +++++++++- src/conf/schemas/domaincommon.rng | 3 +++ src/hypervisor/domain_driver.c | 3 ++- 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 83aeaa32c2..802c875808 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -4123,6 +4123,39 @@ An optional sub-element ``driver`` can specify the driver specific options: If a specific IOThread is desired for a specific SCSI ``disk``, then multiple controllers must be defined each having a specific ``iothread`` value. The ``iothread`` value must be within the range 1 to the domain iothreads value. +``iothreads`` + Supported for ``virtio-scsi`` controllers using ``address`` types ``pci`` and + ``ccw``. :since:`since 11.1.0 (QEMU 10.0).` Mutually exclusive with ``iothread``. + + The optional ``iothreads`` sub-element allows specifying multiple IOThreads + via the ``iothread`` sub-element with attribute ``id`` the virtio-scsi + controller will use for I/O operations. Optionally the ``iothread`` element + can have multiple ``queue`` subelements specifying that given iothread + should be used to handle given queues. + + Note that the ``virtio-scsi`` device has two extra internal queues ``ctrl`` + and ``event`` with indices ``0`` and ``1`` besides queues configured via + the ``queues`` attribute. They need to be covered by manual mapping if + provided. + + Example:: + + <driver name='qemu' queues='3'> + <iothreads> + <iothread id='2'> + <queue id='1'/> + </iothread> + <iothread id='3'> + <queue id='0'/> + <queue id='2'/> + </iothread> + <iothread id='4'> + <queue id='3'/> + <queue id='4'/> + </iothread> + </iothreads> + </driver> + virtio options For virtio controllers, `Virtio-related options`_ can also be set. ( :since:`Since 3.5.0` ) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1a52cda62d..755f4cb537 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2557,6 +2557,8 @@ void virDomainControllerDefFree(virDomainControllerDef *def) if (!def) return; + g_slist_free_full(def->iothreads, (GDestroyNotify) virDomainIothreadMappingDefFree); + virDomainDeviceInfoClear(&def->info); g_free(def->virtio); @@ -8618,6 +8620,9 @@ virDomainControllerDefParseXML(virDomainXMLOption *xmlopt, &def->iothread) < 0) return NULL; + if (virDomainIothreadMappingDefParse(driver, &def->iothreads) < 0) + return NULL; + if (virDomainVirtioOptionsParseXML(driver, &def->virtio) < 0) return NULL; } @@ -23514,6 +23519,7 @@ virDomainControllerDriverFormat(virBuffer *buf, virDomainControllerDef *def) { g_auto(virBuffer) driverBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) driverChildBuf = VIR_BUFFER_INIT_CHILD(buf); if (def->queues) virBufferAsprintf(&driverBuf, " queues='%u'", def->queues); @@ -23532,9 +23538,11 @@ virDomainControllerDriverFormat(virBuffer *buf, if (def->iothread) virBufferAsprintf(&driverBuf, " iothread='%u'", def->iothread); + virDomainIothreadMappingDefFormat(&driverChildBuf, def->iothreads); + virDomainVirtioOptionsFormat(&driverBuf, def->virtio); - virXMLFormatElement(buf, "driver", &driverBuf, NULL); + virXMLFormatElement(buf, "driver", &driverBuf, &driverChildBuf); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 10dd5497ac..8e75e3e04b 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -764,6 +764,7 @@ struct _virDomainControllerDef { unsigned int max_sectors; virTristateSwitch ioeventfd; unsigned int iothread; /* unused = 0, > 0 specific thread # */ + GSList *iothreads; /* List of virDomainIothreadMappingDef */ union { virDomainVirtioSerialOpts vioserial; virDomainPCIControllerOpts pciopts; diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 88e5f37651..967600581a 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -1260,7 +1260,7 @@ virDomainControllerDefValidate(const virDomainControllerDef *controller) } } - if (controller->iothread != 0) { + if (controller->iothread != 0 || controller->iothreads) { if (controller->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI || !(controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI || controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL || @@ -1269,6 +1269,14 @@ virDomainControllerDefValidate(const virDomainControllerDef *controller) _("iothreads are supported only by 'virtio-scsi' controllers")); return -1; } + + /* configuring both <driver iothread='n'> and it's <iothreads> sub-element + * isn't supported */ + if (controller->iothread && controller->iothreads) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("controller driver 'iothread' attribute can't be used together with 'iothreads' subelement")); + return -1; + } } return 0; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 755a43e893..a8705b34ee 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -3070,6 +3070,9 @@ <optional> <ref name="driverIOThread"/> </optional> + <optional> + <ref name="iothreadMapping"/> + </optional> <ref name="virtioOptions"/> </element> </optional> diff --git a/src/hypervisor/domain_driver.c b/src/hypervisor/domain_driver.c index b7499a376f..29ba358477 100644 --- a/src/hypervisor/domain_driver.c +++ b/src/hypervisor/domain_driver.c @@ -585,7 +585,8 @@ virDomainDriverDelIOThreadCheck(virDomainDef *def, } for (i = 0; i < def->ncontrollers; i++) { - if (def->controllers[i]->iothread == iothread_id) { + if (virDomainIothreadMappingDefHasIothread(def->controllers[i]->iothreads, iothread_id) || + def->controllers[i]->iothread == iothread_id) { virReportError(VIR_ERR_INVALID_ARG, _("cannot remove IOThread '%1$u' since it is being used by controller"), iothread_id); -- 2.48.1

Similarly to 'virtio-blk' users can map multiple iothreads and pin them appropriately for 'virtio-scsi' controllers to ensure the best performance. Implement the validation and command line generation based on the helpers we have for 'virtio-blk'. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_command.c | 6 ++++++ src/qemu/qemu_validate.c | 32 +++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index b76fec27c5..ea98dee912 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2510,6 +2510,7 @@ qemuBuildControllerSCSIDevProps(virDomainControllerDef *def, virQEMUCaps *qemuCaps) { g_autoptr(virJSONValue) props = NULL; + g_autoptr(virJSONValue) iothreadsMapping = NULL; g_autofree char *iothread = NULL; const char *driver = NULL; @@ -2521,11 +2522,16 @@ qemuBuildControllerSCSIDevProps(virDomainControllerDef *def, qemuCaps))) return NULL; + if (def->iothreads && + !(iothreadsMapping = qemuBuildIothreadMappingProps(def->iothreads))) + return NULL; + if (def->iothread > 0) iothread = g_strdup_printf("iothread%u", def->iothread); if (virJSONValueObjectAdd(&props, "S:iothread", iothread, + "A:iothread-vq-mapping", &iothreadsMapping, "s:id", def->info.alias, "p:num_queues", def->queues, "p:cmd_per_lun", def->cmd_per_lun, diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 2185e31a27..617cf69f1e 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -3601,9 +3601,10 @@ qemuValidateDomainDeviceDefControllerIDE(const virDomainControllerDef *controlle */ static int qemuValidateCheckSCSIControllerIOThreads(const virDomainControllerDef *controller, - const virDomainDef *def) + const virDomainDef *def, + virQEMUCaps *qemuCaps) { - if (!controller->iothread) + if (controller->iothread == 0 && !controller->iothreads) return 0; if (controller->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && @@ -3614,8 +3615,22 @@ qemuValidateCheckSCSIControllerIOThreads(const virDomainControllerDef *controlle return -1; } - /* Can we find the controller iothread in the iothreadid list? */ - if (!virDomainIOThreadIDFind(def, controller->iothread)) { + if (controller->iothreads) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("IOThread mapping for virtio-scsi controllers is not available with this QEMU binary")); + return -1; + } + + /* The 'virtio-scsi' device has two extra internal queues 'ctrl' and + * 'event' with indices '0' and '1' which need to be covered by mapping */ + if (qemuDomainValidateIothreadMapping(def, controller->iothreads, + controller->queues + 2) < 0) + return -1; + } + + if (controller->iothread > 0 && + !virDomainIOThreadIDFind(def, controller->iothread)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("controller iothread '%1$u' not defined in iothreadid"), controller->iothread); @@ -3628,13 +3643,15 @@ qemuValidateCheckSCSIControllerIOThreads(const virDomainControllerDef *controlle static int qemuValidateDomainDeviceDefControllerSCSI(const virDomainControllerDef *controller, - const virDomainDef *def) + const virDomainDef *def, + virQEMUCaps *qemuCaps) { switch ((virDomainControllerModelSCSI) controller->model) { case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL: - if (qemuValidateCheckSCSIControllerIOThreads(controller, def) < 0) + if (qemuValidateCheckSCSIControllerIOThreads(controller, def, + qemuCaps) < 0) return -1; break; @@ -4308,7 +4325,8 @@ qemuValidateDomainDeviceDefController(const virDomainControllerDef *controller, break; case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - ret = qemuValidateDomainDeviceDefControllerSCSI(controller, def); + ret = qemuValidateDomainDeviceDefControllerSCSI(controller, def, + qemuCaps); break; case VIR_DOMAIN_CONTROLLER_TYPE_PCI: -- 2.48.1

Test the XML and commandline for iothread<->virtqueue mapping for 'virtio-scsi' controllers. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- ...ads-virtio-scsi-mapping.x86_64-latest.args | 39 ++++++++++++++ ...eads-virtio-scsi-mapping.x86_64-latest.xml | 54 +++++++++++++++++++ .../iothreads-virtio-scsi-mapping.xml | 46 ++++++++++++++++ tests/qemuxmlconftest.c | 1 + 4 files changed, 140 insertions(+) create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.xml diff --git a/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.args b/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.args new file mode 100644 index 0000000000..d1a6d85336 --- /dev/null +++ b/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.args @@ -0,0 +1,39 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \ +-accel tcg \ +-cpu qemu64 \ +-m size=219136k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ +-overcommit mem-lock=off \ +-smp 2,sockets=2,cores=1,threads=1 \ +-object '{"qom-type":"iothread","id":"iothread1"}' \ +-object '{"qom-type":"iothread","id":"iothread2"}' \ +-object '{"qom-type":"iothread","id":"iothread3"}' \ +-object '{"qom-type":"iothread","id":"iothread4"}' \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot strict=on \ +-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \ +-device '{"driver":"virtio-scsi-pci","iothread-vq-mapping":[{"iothread":"iothread2","vqs":[1,3]},{"iothread":"iothread3","vqs":[0,2]}],"id":"scsi0","num_queues":2,"bus":"pci.0","addr":"0xb"}' \ +-blockdev '{"driver":"file","filename":"/var/lib/libvirt/images/iothrtest2.img","node-name":"libvirt-1-storage","read-only":false}' \ +-device '{"driver":"scsi-hd","bus":"scsi0.0","channel":0,"scsi-id":0,"lun":3,"device_id":"drive-scsi0-0-0-3","drive":"libvirt-1-storage","id":"scsi0-0-0-3","bootindex":1}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.xml b/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.xml new file mode 100644 index 0000000000..99752ef5e7 --- /dev/null +++ b/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.xml @@ -0,0 +1,54 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>2</vcpu> + <iothreads>4</iothreads> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='/var/lib/libvirt/images/iothrtest2.img'/> + <target dev='sdc' bus='scsi'/> + <address type='drive' controller='0' bus='0' target='0' unit='3'/> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <controller type='scsi' index='0' model='virtio-scsi'> + <driver queues='2'> + <iothreads> + <iothread id='2'> + <queue id='1'/> + <queue id='3'/> + </iothread> + <iothread id='3'> + <queue id='0'/> + <queue id='2'/> + </iothread> + </iothreads> + </driver> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/> + </controller> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.xml b/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.xml new file mode 100644 index 0000000000..336ba0a3d1 --- /dev/null +++ b/tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.xml @@ -0,0 +1,46 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>2</vcpu> + <iothreads>4</iothreads> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='/var/lib/libvirt/images/iothrtest2.img'/> + <target dev='sdc' bus='scsi'/> + <address type='drive' controller='0' bus='0' target='0' unit='3'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <controller type='scsi' index='0' model='virtio-scsi'> + <driver queues='2'> + <iothreads> + <iothread id='2'> + <queue id='1'/> + <queue id='3'/> + </iothread> + <iothread id='3'> + <queue id='0'/> + <queue id='2'/> + </iothread> + </iothreads> + </driver> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/> + </controller> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index ce48b91ac2..f24272b0ef 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -2189,6 +2189,7 @@ mymain(void) DO_TEST_CAPS_LATEST("iothreads-disk"); DO_TEST_CAPS_VER("iothreads-virtio-scsi-pci", "5.2.0"); DO_TEST_CAPS_LATEST("iothreads-virtio-scsi-pci"); + DO_TEST_CAPS_LATEST("iothreads-virtio-scsi-mapping"); DO_TEST_CAPS_ARCH_LATEST("iothreads-virtio-scsi-ccw", "s390x"); DO_TEST_CAPS_LATEST("cpu-topology1"); -- 2.48.1

On Fri, Feb 14, 2025 at 05:29:34PM +0100, Peter Krempa wrote:
The first part of the series refactors the existing code for reuse and then uses the new helpers to implement the feature.
Note that this series is in RFC state as the qemu patches are still being discussed. Thus also the capability bump is not final.
Also note that we should discuss the libvirt interface perhaps as it turns out that 'virtio-scsi' has two internal queues that need to be mapped as well.
For now I've solved this administratively by instructing users to also add mapping for queue '0' and '1' which are the special ones in case of virtio-scsi.
Thanks for tackling this upcoming QEMU feature! I thought about the pros/cons of exposing all virtqueues in iothread-vq-mapping= whereas just the command virtqueues are exposed by num_queues=. Although it's confusing and inconvenient that the implicit ctrl and event virtqueues need to be covered by iothread-vq-mapping= but are not counted in num_queues=, I'd rather not introduce magic to hide this detail. If users do need to control these virtqueues explicitly then the magic will get in the way. Does anyone have a different opinion? Stefan
qemu-patches: https://mail.gnu.org/archive/html/qemu-devel/2025-02/msg02810.html
Peter Krempa (13): conf: Rename 'virDomainDiskIothreadDef' to 'virDomainIothreadMappingDef' conf: domain: Extract code for parsing and formatting iotrhead mapping definition hypervisor: domain: Extract code for checking iothread usage qemu: command: Rename 'qemuBuildDiskDeviceIothreadMappingProps' to 'qemuBuildIothreadMappingProps' qemu: validate: Extract iothread mapping validation code qemuValidateCheckSCSIControllerIOThreads: Return '0' and '-1' instead of bools conf: schemas: Rename 'diskDriverIothreads' to 'iothreadMapping' conf: Validate that iohtreads are used only with 'virtio-scsi' controllers qemucapabilitiestest: Update 'caps_10.0.0_x86_64' to XXXXXX qemu: capabilities: Introduce QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING conf: Add support for iothread to queue mapping config for 'virtio-scsi' qemu: Implement support for iothread <-> virtqueue mapping for 'virtio-scsi' controllers qemuxmlconftest: Add 'iothreads-virtio-scsi-mapping' case
docs/formatdomain.rst | 33 +++ src/conf/domain_conf.c | 157 +++++++----- src/conf/domain_conf.h | 11 +- src/conf/domain_validate.c | 19 ++ src/conf/schemas/domaincommon.rng | 7 +- src/hypervisor/domain_driver.c | 34 +-- src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 12 +- src/qemu/qemu_domain.c | 4 +- src/qemu/qemu_validate.c | 234 ++++++++++-------- .../caps_10.0.0_x86_64.replies | 12 +- .../caps_10.0.0_x86_64.xml | 3 +- ...r-virtio-serial-iothread.x86_64-latest.err | 1 + .../controller-virtio-serial-iothread.xml | 27 ++ ...ads-virtio-scsi-mapping.x86_64-latest.args | 39 +++ ...eads-virtio-scsi-mapping.x86_64-latest.xml | 54 ++++ .../iothreads-virtio-scsi-mapping.xml | 46 ++++ tests/qemuxmlconftest.c | 3 + 19 files changed, 506 insertions(+), 193 deletions(-) create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.xml
-- 2.48.1

On Tue, Feb 18, 2025 at 13:53:31 +0800, Stefan Hajnoczi wrote:
On Fri, Feb 14, 2025 at 05:29:34PM +0100, Peter Krempa wrote:
The first part of the series refactors the existing code for reuse and then uses the new helpers to implement the feature.
Note that this series is in RFC state as the qemu patches are still being discussed. Thus also the capability bump is not final.
Also note that we should discuss the libvirt interface perhaps as it turns out that 'virtio-scsi' has two internal queues that need to be mapped as well.
For now I've solved this administratively by instructing users to also add mapping for queue '0' and '1' which are the special ones in case of virtio-scsi.
Thanks for tackling this upcoming QEMU feature! I thought about the pros/cons of exposing all virtqueues in iothread-vq-mapping= whereas just the command virtqueues are exposed by num_queues=. Although it's confusing and inconvenient that the implicit ctrl and event virtqueues need to be covered by iothread-vq-mapping= but are not counted in num_queues=, I'd rather not introduce magic to hide this detail. If users do need to control these virtqueues explicitly then the magic will get in the way.
I thought about it a bit as well and I think we should: 1) Document the round robin assignment a bit more prominently, so that users don't feel compelled to do a full mapping. Currently the example spells out the full maping configuration, so the first example should be the simpler one. 2) Modify the qemu code so that the mapping XML will explicitly name the 'ctrl/event' queues. Leave the numbered ones for the explicit ones configured via queues. That way it'll be obvious what's happening. <driver name='qemu' queues='2'> <iothreads> <iothread id='2'> <queue id='event'/> <queue id='ctrl'/> </iothread> <iothread id='3'> <queue id='0'/> </iothread> <iothread id='4'> <queue id='1'/> </iothread> </iothreads> </driver> Libvirt will then map the above config to mapping for queues 0,1,2,3 for use with qemu. That way there will be no ambiguity and weirdness when comparing the config between virtio-blk and virtio-scsi.
Does anyone have a different opinion?
I agree. We should hint the users to use the simpler configs and use the full mapping only if necessary. When using the full mapping the above XML will IMO make it obvious what's happening. In qemu no change will be needed although the role of queues 0 and 1 should be formalized in docs so that it doesn't change later on at least without notifying libvirt so that we adapt if needed.

On Tue, Feb 18, 2025 at 12:15:46PM +0100, Peter Krempa wrote:
On Tue, Feb 18, 2025 at 13:53:31 +0800, Stefan Hajnoczi wrote:
On Fri, Feb 14, 2025 at 05:29:34PM +0100, Peter Krempa wrote:
The first part of the series refactors the existing code for reuse and then uses the new helpers to implement the feature.
Note that this series is in RFC state as the qemu patches are still being discussed. Thus also the capability bump is not final.
Also note that we should discuss the libvirt interface perhaps as it turns out that 'virtio-scsi' has two internal queues that need to be mapped as well.
For now I've solved this administratively by instructing users to also add mapping for queue '0' and '1' which are the special ones in case of virtio-scsi.
Thanks for tackling this upcoming QEMU feature! I thought about the pros/cons of exposing all virtqueues in iothread-vq-mapping= whereas just the command virtqueues are exposed by num_queues=. Although it's confusing and inconvenient that the implicit ctrl and event virtqueues need to be covered by iothread-vq-mapping= but are not counted in num_queues=, I'd rather not introduce magic to hide this detail. If users do need to control these virtqueues explicitly then the magic will get in the way.
I thought about it a bit as well and I think we should:
1) Document the round robin assignment a bit more prominently, so that users don't feel compelled to do a full mapping. Currently the example spells out the full maping configuration, so the first example should be the simpler one.
Good idea.
2) Modify the qemu code so that the mapping XML will explicitly name the 'ctrl/event' queues. Leave the numbered ones for the explicit ones configured via queues. That way it'll be obvious what's happening.
<driver name='qemu' queues='2'> <iothreads> <iothread id='2'> <queue id='event'/> <queue id='ctrl'/> </iothread> <iothread id='3'> <queue id='0'/> </iothread> <iothread id='4'> <queue id='1'/> </iothread> </iothreads> </driver>
Libvirt will then map the above config to mapping for queues 0,1,2,3 for use with qemu. That way there will be no ambiguity and weirdness when comparing the config between virtio-blk and virtio-scsi.
Nice!
Does anyone have a different opinion?
I agree. We should hint the users to use the simpler configs and use the full mapping only if necessary. When using the full mapping the above XML will IMO make it obvious what's happening.
In qemu no change will be needed although the role of queues 0 and 1 should be formalized in docs so that it doesn't change later on at least without notifying libvirt so that we adapt if needed.
The roles of the queues are defined in the VIRTIO specification. ctrl and event are always 0 and 1. The only way they could change is if a new feature bit is introduced, but this is very unlikely. https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#... Stefan

On a Friday in 2025, Peter Krempa wrote:
The first part of the series refactors the existing code for reuse and then uses the new helpers to implement the feature.
Note that this series is in RFC state as the qemu patches are still being discussed. Thus also the capability bump is not final.
Also note that we should discuss the libvirt interface perhaps as it turns out that 'virtio-scsi' has two internal queues that need to be mapped as well.
For now I've solved this administratively by instructing users to also add mapping for queue '0' and '1' which are the special ones in case of virtio-scsi.
qemu-patches: https://mail.gnu.org/archive/html/qemu-devel/2025-02/msg02810.html
Peter Krempa (13): conf: Rename 'virDomainDiskIothreadDef' to 'virDomainIothreadMappingDef' conf: domain: Extract code for parsing and formatting iotrhead mapping definition hypervisor: domain: Extract code for checking iothread usage qemu: command: Rename 'qemuBuildDiskDeviceIothreadMappingProps' to 'qemuBuildIothreadMappingProps' qemu: validate: Extract iothread mapping validation code qemuValidateCheckSCSIControllerIOThreads: Return '0' and '-1' instead of bools conf: schemas: Rename 'diskDriverIothreads' to 'iothreadMapping' conf: Validate that iohtreads are used only with 'virtio-scsi' controllers
To the above patches Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
qemucapabilitiestest: Update 'caps_10.0.0_x86_64' to XXXXXX qemu: capabilities: Introduce QEMU_CAPS_VIRTIO_SCSI_IOTHREAD_MAPPING conf: Add support for iothread to queue mapping config for 'virtio-scsi' qemu: Implement support for iothread <-> virtqueue mapping for 'virtio-scsi' controllers qemuxmlconftest: Add 'iothreads-virtio-scsi-mapping' case
docs/formatdomain.rst | 33 +++ src/conf/domain_conf.c | 157 +++++++----- src/conf/domain_conf.h | 11 +- src/conf/domain_validate.c | 19 ++ src/conf/schemas/domaincommon.rng | 7 +- src/hypervisor/domain_driver.c | 34 +-- src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 12 +- src/qemu/qemu_domain.c | 4 +- src/qemu/qemu_validate.c | 234 ++++++++++-------- .../caps_10.0.0_x86_64.replies | 12 +- .../caps_10.0.0_x86_64.xml | 3 +- ...r-virtio-serial-iothread.x86_64-latest.err | 1 + .../controller-virtio-serial-iothread.xml | 27 ++ ...ads-virtio-scsi-mapping.x86_64-latest.args | 39 +++ ...eads-virtio-scsi-mapping.x86_64-latest.xml | 54 ++++ .../iothreads-virtio-scsi-mapping.xml | 46 ++++ tests/qemuxmlconftest.c | 3 + 19 files changed, 506 insertions(+), 193 deletions(-) create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/controller-virtio-serial-iothread.xml create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/iothreads-virtio-scsi-mapping.xml
-- 2.48.1
participants (3)
-
Ján Tomko
-
Peter Krempa
-
Stefan Hajnoczi