diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index e921749692..518f7c555f 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -520,6 +520,18 @@
<iothreads>4</iothreads>
...
</domain>
+
+
+<domain>
+ ...
+ <iothreadids>
+ <iothread id="2"/>
+ <iothread id="4"/>
+ <iothread id="6"/>
+ <iothread id="8"/>
+ </iothreadids>
+ ...
+</domain>
@@ -530,7 +542,25 @@
virtio-blk-pci and virtio-blk-ccw target storage devices. There
should be only 1 or 2 IOThreads per host CPU. There may be more
than one supported device assigned to each IOThread.
+ Since 1.2.8
+ iothreadids
+ -
+ The optional
iothreadids
element provides the capability
+ to specifically define the IOThread ID's for the domain. By default,
+ IOThread ID's are sequentially numbered starting from 1 through the
+ number of iothreads
defined for the domain. The
+ id
attribute is used to define the IOThread ID. The
+ id
attribute must be a positive integer greater than 0.
+ If there are less iothreadids
defined than
+ iothreads
defined for the domain, then libvirt will
+ sequentially fill iothreadids
starting at 1 avoiding
+ any predefined id
. If there are more
+ iothreadids
defined than iothreads
+ defined for the domain, then the iothreads
value
+ will be adjusted accordingly.
+ Since 1.2.15
+
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 19461f5c54..7072954ac9 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -538,6 +538,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 03710cba85..1051936439 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2102,6 +2102,32 @@ virDomainPinDefCopy(virDomainPinDefPtr *src, int npin)
return NULL;
}
+
+void
+virDomainIOThreadIDDefFree(virDomainIOThreadIDDefPtr def)
+{
+ if (!def)
+ return;
+ VIR_FREE(def);
+}
+
+
+static void
+virDomainIOThreadIDDefArrayFree(virDomainIOThreadIDDefPtr *def,
+ int nids)
+{
+ size_t i;
+
+ if (!def)
+ return;
+
+ for (i = 0; i < nids; i++)
+ virDomainIOThreadIDDefFree(def[i]);
+
+ VIR_FREE(def);
+}
+
+
void
virDomainPinDefFree(virDomainPinDefPtr def)
{
@@ -2298,6 +2324,8 @@ void virDomainDefFree(virDomainDefPtr def)
virCPUDefFree(def->cpu);
+ virDomainIOThreadIDDefArrayFree(def->iothreadids, def->niothreadids);
+
virDomainPinDefArrayFree(def->cputune.vcpupin, def->cputune.nvcpupin);
virDomainPinDefFree(def->cputune.emulatorpin);
@@ -13170,6 +13198,54 @@ virDomainIdmapDefParseXML(xmlXPathContextPtr ctxt,
return idmap;
}
+/* Parse the XML definition for an IOThread ID
+ *
+ * Format is :
+ *
+ * 4
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+static virDomainIOThreadIDDefPtr
+virDomainIOThreadIDDefParseXML(xmlNodePtr node,
+ xmlXPathContextPtr ctxt)
+{
+ virDomainIOThreadIDDefPtr iothrid;
+ xmlNodePtr oldnode = ctxt->node;
+ char *tmp = NULL;
+
+ if (VIR_ALLOC(iothrid) < 0)
+ return NULL;
+
+ ctxt->node = node;
+
+ if (!(tmp = virXPathString("string(./@id)", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Missing 'id' attribute in element"));
+ goto error;
+ }
+ if (virStrToLong_uip(tmp, NULL, 10, &iothrid->iothread_id) < 0 ||
+ iothrid->iothread_id == 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("invalid iothread 'id' value '%s'"), tmp);
+ goto error;
+ }
+
+ cleanup:
+ VIR_FREE(tmp);
+ ctxt->node = oldnode;
+ return iothrid;
+
+ error:
+ virDomainIOThreadIDDefFree(iothrid);
+ goto cleanup;
+}
+
+
/* Parse the XML definition for a vcpupin
*
* vcpupin has the form of
@@ -13976,6 +14052,49 @@ virDomainDefParseXML(xmlDocPtr xml,
}
VIR_FREE(tmp);
+ /* Extract any iothread id's defined */
+ if ((n = virXPathNodeSet("./iothreadids/iothread", ctxt, &nodes)) < 0)
+ goto error;
+
+ if (n > def->iothreads)
+ def->iothreads = n;
+
+ if (n && VIR_ALLOC_N(def->iothreadids, n) < 0)
+ goto error;
+
+ for (i = 0; i < n; i++) {
+ virDomainIOThreadIDDefPtr iothrid = NULL;
+ if (!(iothrid = virDomainIOThreadIDDefParseXML(nodes[i], ctxt)))
+ goto error;
+
+ if (virDomainIOThreadIDFind(def, iothrid->iothread_id)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("duplicate iothread id '%u' found"),
+ iothrid->iothread_id);
+ virDomainIOThreadIDDefFree(iothrid);
+ goto error;
+ }
+ def->iothreadids[def->niothreadids++] = iothrid;
+ }
+ VIR_FREE(nodes);
+
+ /* If no iothreadid's or not fully populated, let's finish the job
+ * here rather than in PostParseCallback
+ */
+ if (def->iothreads && def->iothreads != def->niothreadids) {
+ unsigned int iothread_id = 1;
+ while (def->niothreadids != def->iothreads) {
+ if (!virDomainIOThreadIDFind(def, iothread_id)) {
+ virDomainIOThreadIDDefPtr iothrid;
+
+ if (!(iothrid = virDomainIOThreadIDAdd(def, iothread_id)))
+ goto error;
+ iothrid->autofill = true;
+ }
+ iothread_id++;
+ }
+ }
+
/* Extract cpu tunables. */
if ((n = virXPathULong("string(./cputune/shares[1])", ctxt,
&def->cputune.shares)) < -1) {
@@ -17260,6 +17379,67 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def)
return 0;
}
+virDomainIOThreadIDDefPtr
+virDomainIOThreadIDFind(virDomainDefPtr def,
+ unsigned int iothread_id)
+{
+ size_t i;
+
+ if (!def->iothreadids || !def->niothreadids)
+ return NULL;
+
+ for (i = 0; i < def->niothreadids; i++) {
+ if (iothread_id == def->iothreadids[i]->iothread_id)
+ return def->iothreadids[i];
+ }
+
+ return NULL;
+}
+
+virDomainIOThreadIDDefPtr
+virDomainIOThreadIDAdd(virDomainDefPtr def,
+ unsigned int iothread_id)
+{
+ virDomainIOThreadIDDefPtr iothrid = NULL;
+
+ if (virDomainIOThreadIDFind(def, iothread_id)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot duplicate iothread_id '%u' in iothreadids"),
+ iothread_id);
+ return NULL;
+ }
+
+ if (VIR_ALLOC(iothrid) < 0)
+ goto error;
+
+ iothrid->iothread_id = iothread_id;
+
+ if (VIR_APPEND_ELEMENT_COPY(def->iothreadids, def->niothreadids,
+ iothrid) < 0)
+ goto error;
+
+ return iothrid;
+
+ error:
+ virDomainIOThreadIDDefFree(iothrid);
+ return NULL;
+}
+
+void
+virDomainIOThreadIDDel(virDomainDefPtr def,
+ unsigned int iothread_id)
+{
+ int n;
+
+ for (n = 0; n < def->niothreadids; n++) {
+ if (def->iothreadids[n]->iothread_id == iothread_id) {
+ virDomainIOThreadIDDefFree(def->iothreadids[n]);
+ VIR_DELETE_ELEMENT(def->iothreadids, n, def->niothreadids);
+ return;
+ }
+ }
+}
+
/* Check if vcpupin with same id already exists. */
bool
virDomainPinIsDuplicate(virDomainPinDefPtr *def,
@@ -20602,8 +20782,27 @@ virDomainDefFormatInternal(virDomainDefPtr def,
virBufferAsprintf(buf, " current='%u'", def->vcpus);
virBufferAsprintf(buf, ">%u\n", def->maxvcpus);
- if (def->iothreads > 0)
- virBufferAsprintf(buf, "%u\n", def->iothreads);
+ if (def->iothreads > 0) {
+ virBufferAsprintf(buf, "%u\n",
+ def->iothreads);
+ /* Only print out iothreadids if we read at least one */
+ for (i = 0; i < def->niothreadids; i++) {
+ if (!def->iothreadids[i]->autofill)
+ break;
+ }
+ if (i < def->niothreadids) {
+ virBufferAddLit(buf, "\n");
+ virBufferAdjustIndent(buf, 2);
+ for (i = 0; i < def->niothreadids; i++) {
+ if (def->iothreadids[i]->autofill)
+ continue;
+ virBufferAsprintf(buf, "\n",
+ def->iothreadids[i]->iothread_id);
+ }
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "\n");
+ }
+ }
if (def->cputune.sharesSpecified ||
(def->cputune.nvcpupin && !virDomainIsAllVcpupinInherited(def)) ||
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 99550528a2..28a003e87e 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2054,6 +2054,16 @@ struct _virDomainHugePage {
unsigned long long size; /* hugepage size in KiB */
};
+typedef struct _virDomainIOThreadIDDef virDomainIOThreadIDDef;
+typedef virDomainIOThreadIDDef *virDomainIOThreadIDDefPtr;
+
+struct _virDomainIOThreadIDDef {
+ bool autofill;
+ unsigned int iothread_id;
+};
+
+void virDomainIOThreadIDDefFree(virDomainIOThreadIDDefPtr def);
+
typedef struct _virDomainCputune virDomainCputune;
typedef virDomainCputune *virDomainCputunePtr;
@@ -2145,6 +2155,8 @@ struct _virDomainDef {
virBitmapPtr cpumask;
unsigned int iothreads;
+ size_t niothreadids;
+ virDomainIOThreadIDDefPtr *iothreadids;
virDomainCputune cputune;
@@ -2604,6 +2616,12 @@ bool virDomainDefCheckABIStability(virDomainDefPtr src,
int virDomainDefAddImplicitControllers(virDomainDefPtr def);
+virDomainIOThreadIDDefPtr virDomainIOThreadIDFind(virDomainDefPtr def,
+ unsigned int iothread_id);
+virDomainIOThreadIDDefPtr virDomainIOThreadIDAdd(virDomainDefPtr def,
+ unsigned int iothread_id);
+void virDomainIOThreadIDDel(virDomainDefPtr def, unsigned int iothread_id);
+
unsigned int virDomainDefFormatConvertXMLFlags(unsigned int flags);
char *virDomainDefFormat(virDomainDefPtr def,
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e6555f1a92..1c345fcdab 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -324,6 +324,10 @@ virDomainHubTypeToString;
virDomainHypervTypeFromString;
virDomainHypervTypeToString;
virDomainInputDefFree;
+virDomainIOThreadIDAdd;
+virDomainIOThreadIDDefFree;
+virDomainIOThreadIDDel;
+virDomainIOThreadIDFind;
virDomainLeaseDefFree;
virDomainLeaseIndex;
virDomainLeaseInsert;