diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 9698b96ce9..298ad46a45 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -3338,7 +3338,28 @@ paravirtualized driver is specified via the ``disk`` element.
assigned to the same IOThread and are numbered from 1 to the domain
iothreads value. Available for a disk device ``target`` configured to use
"virtio" ``bus`` and "pci" or "ccw" ``address`` types. :since:`Since 1.2.8
- (QEMU 2.1)`
+ (QEMU 2.1)` *Note:* ``iothread`` is mutually exclusive with ``iothreads``.
+ - The optional ``iothreads`` sub-element allows specifying multiple IOThreads
+ via the ``iothread`` sub-element with attribute ``id`` the disk 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. :since:`Since 10.0.0 (QEMU 9.0, virtio disks only)`.
+ Example::
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- The optional ``queues`` attribute specifies the number of virt queues for
virtio-blk ( :since:`Since 3.9.0` ) or vhost-user-blk
( :since `Since 7.1.0` )
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 8d945a9416..be57a1981e 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2330,6 +2330,17 @@ virDomainDefGetVcpusTopology(const virDomainDef *def,
}
+void
+virDomainDiskIothreadDefFree(virDomainDiskIothreadDef *def)
+{
+ if (!def)
+ return;
+
+ g_free(def->queues);
+ g_free(def);
+}
+
+
static virDomainDiskDef *
virDomainDiskDefNewSource(virDomainXMLOption *xmlopt,
virStorageSource **src)
@@ -2378,6 +2389,7 @@ virDomainDiskDefFree(virDomainDiskDef *def)
g_free(def->virtio);
virDomainDeviceInfoClear(&def->info);
virObjectUnref(def->privateData);
+ g_slist_free_full(def->iothreads, (GDestroyNotify) virDomainDiskIothreadDefFree);
g_free(def);
}
@@ -7766,6 +7778,8 @@ static int
virDomainDiskDefDriverParseXML(virDomainDiskDef *def,
xmlNodePtr cur)
{
+ xmlNodePtr iothreadsNode;
+
def->driverName = virXMLPropString(cur, "name");
if (virXMLPropEnum(cur, "cache", virDomainDiskCacheTypeFromString,
@@ -7812,6 +7826,44 @@ virDomainDiskDefDriverParseXML(virDomainDiskDef *def,
if (virXMLPropUInt(cur, "iothread", 10, VIR_XML_PROP_NONZERO, &def->iothread) < 0)
return -1;
+ if ((iothreadsNode = virXMLNodeGetSubelement(cur, "iothreads"))) {
+ g_autoslist(virDomainDiskIothreadDef) 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(virDomainDiskIothreadDef) iothdef = g_new0(virDomainDiskIothreadDef, 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 (virXMLPropEnum(cur, "detect_zeroes",
virDomainDiskDetectZeroesTypeFromString,
VIR_XML_PROP_NONZERO, &def->detect_zeroes) < 0)
@@ -22705,6 +22757,30 @@ 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) {
+ virDomainDiskIothreadDef *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, "\n", iothDef->queues[q]);
+ }
+
+ virXMLFormatElement(&iothreadsChildBuf, "iothread", &iothreadAttrBuf, &iothreadChildBuf);
+ }
+
+ virXMLFormatElement(&childBuf, "iothreads", NULL, &iothreadsChildBuf);
+ }
+
virXMLFormatElement(buf, "driver", &attrBuf, &childBuf);
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index a7a7f770c1..d176bda5f8 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -503,6 +503,19 @@ typedef enum {
VIR_ENUM_DECL(virDomainSnapshotLocation);
+struct _virDomainDiskIothreadDef {
+ unsigned int id;
+
+ /* optional list of virtqueues the iothread should handle */
+ unsigned int *queues;
+ size_t nqueues;
+};
+
+typedef struct _virDomainDiskIothreadDef virDomainDiskIothreadDef;
+void virDomainDiskIothreadDefFree(virDomainDiskIothreadDef *def);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainDiskIothreadDef, virDomainDiskIothreadDefFree);
+
+
/* Stores the virtual disk configuration */
struct _virDomainDiskDef {
virStorageSource *src; /* non-NULL. XXX Allow NULL for empty cdrom? */
@@ -557,6 +570,7 @@ struct _virDomainDiskDef {
virDomainDeviceSGIO sgio;
virDomainDiskDiscard discard;
unsigned int iothread; /* unused = 0, > 0 specific thread # */
+ GSList *iothreads; /* List of virDomainDiskIothreadsDef */
virDomainDiskDetectZeroes detect_zeroes;
virTristateSwitch discard_no_unref;
char *domain_name; /* backend domain name */
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
index c72108886e..d485ec4fb1 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -934,6 +934,14 @@ virDomainDiskDefValidate(const virDomainDef *def,
}
}
+ /* configuring both and it's sub-element
+ * isn't supported */
+ if (disk->iothread && disk->iothreads) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("disk 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 c1c267d303..a34427c330 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -2536,9 +2536,26 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2590,17 +2607,23 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+