Rather than use the default numbering scheme of 1..number of iothreads
defined for the domain, use the iothreadid's list for the iothread_id and
possibly augmenting the alias using the iothreadsid name.
This also requires adjusting the iothreadpids structure to include room for
the iothread_id and name (if found in the alias, thus iothreadids entry).
The iothreadpids will keep track of the live system, while iothreadids will
be used for the configuration.
Now that the iothreadpids list keeps track of the iothread_id's, these
can be used in place of the many places where a for loop would "know"
that the ID was "+ 1" from the array element.
The new tests ensure usage of the <iothreadid> values for an exact number
of iothreads, the usage of a smaller number of <iothreadid> values than
iothreads that exist (and usage of the default numbering scheme), and the
usage of the optional name value to provide the alias for the IOThread.
Signed-off-by: John Ferlan <jferlan(a)redhat.com>
---
src/qemu/qemu_cgroup.c | 13 ++-
src/qemu/qemu_command.c | 104 ++++++++++++++++++---
src/qemu/qemu_command.h | 4 +
src/qemu/qemu_domain.c | 40 +++++++-
src/qemu/qemu_domain.h | 2 +
src/qemu/qemu_driver.c | 35 ++++---
src/qemu/qemu_process.c | 29 +++++-
.../qemuxml2argv-iothreads-ids-partial.args | 10 ++
.../qemuxml2argv-iothreads-ids-partial.xml | 33 +++++++
.../qemuxml2argv-iothreads-ids.args | 8 ++
.../qemuxml2argv-iothreads-ids.xml | 33 +++++++
.../qemuxml2argv-iothreads-name.args | 17 ++++
.../qemuxml2argv-iothreads-name.xml | 44 +++++++++
tests/qemuxml2argvtest.c | 4 +
tests/qemuxml2xmltest.c | 3 +
15 files changed, 339 insertions(+), 40 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.xml
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c
index da07346..d74302a 100644
--- a/src/qemu/qemu_cgroup.c
+++ b/src/qemu/qemu_cgroup.c
@@ -803,7 +803,8 @@ qemuRestoreCgroupState(virDomainObjPtr vm)
}
for (i = 0; i < priv->niothreadpids; i++) {
- if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_IOTHREAD, i + 1,
+ if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_IOTHREAD,
+ priv->iothreadpids[i]->iothread_id,
false, &cgroup_temp) < 0 ||
virCgroupSetCpusetMemoryMigrate(cgroup_temp, true) < 0 ||
virCgroupGetCpusetMems(cgroup_temp, &nodeset) < 0 ||
@@ -1188,10 +1189,8 @@ qemuSetupCgroupForIOThreads(virDomainObjPtr vm)
goto cleanup;
for (i = 0; i < priv->niothreadpids; i++) {
- /* IOThreads are numbered 1..n, although the array is 0..n-1,
- * so we will account for that here
- */
- if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_IOTHREAD, i + 1,
+ if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_IOTHREAD,
+ priv->iothreadpids[i]->iothread_id,
true, &cgroup_iothread) < 0)
goto cleanup;
@@ -1222,8 +1221,8 @@ qemuSetupCgroupForIOThreads(virDomainObjPtr vm)
/* specific cpu mask */
for (j = 0; j < def->cputune.niothreadspin; j++) {
- /* IOThreads are numbered/named 1..n */
- if (def->cputune.iothreadspin[j]->id == i + 1) {
+ if (def->cputune.iothreadspin[j]->id ==
+ priv->iothreadpids[i]->iothread_id) {
cpumask = def->cputune.iothreadspin[j]->cpumask;
break;
}
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index e7e0937..68c85e2 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -678,6 +678,57 @@ qemuOpenVhostNet(virDomainDefPtr def,
}
int
+qemuDomainParseIOThreadAlias(char *alias,
+ unsigned int *iothread_id,
+ char **name)
+{
+ unsigned int idval;
+ char *idname = NULL;
+
+ /* IOThread's alias is either "iothread#" or
"name_iothread#", where
+ * name is a user definable prefix for the alias and the # is the
+ * iothreadids iothread_id provided in XML or generated during
+ * post parse processing
+ */
+ if (STRPREFIX(alias, "iothread")) {
+ if (virStrToLong_ui(alias + strlen("iothread"),
+ NULL, 10, &idval) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to find iothread id for '%s'"),
+ alias);
+ return -1;
+ }
+ /* Default - no need to do anything with name */
+ } else {
+ char *spot = strstr(alias, "_iothread");
+
+ if (!spot) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Malformed IOThreads alias '%s'"),
+ alias);
+ return -1;
+ }
+
+ /* Pick off the user defined name from the front */
+ if (VIR_STRNDUP(idname, alias, spot - alias) < 0)
+ return -1;
+
+ if (virStrToLong_ui(alias + strlen(idname) + strlen("_iothread"),
+ NULL, 10, &idval) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to find iothread id for '%s'"),
+ alias);
+ VIR_FREE(idname);
+ return -1;
+ }
+ }
+
+ *iothread_id = idval;
+ *name = idname;
+ return 0;
+}
+
+int
qemuNetworkPrepareDevices(virDomainDefPtr def)
{
int ret = -1;
@@ -3985,11 +4036,11 @@ qemuCheckIOThreads(virDomainDefPtr def,
return false;
}
- /* Value larger than iothreads available? */
- if (disk->iothread > def->iothreads) {
+ /* Can we find the disk iothread in the iothreadid list? */
+ if (!virDomainIOThreadIDFind(def, disk->iothread)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("Disk iothread '%u' invalid only %u
IOThreads"),
- disk->iothread, def->iothreads);
+ _("Disk iothread '%u' not defined"),
+ disk->iothread);
return false;
}
@@ -4005,6 +4056,8 @@ qemuBuildDriveDevStr(virDomainDefPtr def,
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
+ virDomainIOThreadIDDefPtr iothrid = NULL;
+ char *disk_iothr_alias = NULL;
int controllerModel;
if (virDomainDiskDefDstDuplicates(def))
@@ -4194,10 +4247,27 @@ qemuBuildDriveDevStr(virDomainDefPtr def,
}
break;
case VIR_DOMAIN_DISK_BUS_VIRTIO:
+ if (disk->iothread) {
+ if (!(iothrid = virDomainIOThreadIDFind(def, disk->iothread))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("cannot find iothread '%u' in
iothreadids"),
+ disk->iothread);
+ goto error;
+ }
+ if (iothrid->name) {
+ if (virAsprintf(&disk_iothr_alias, "%s_iothread%u",
+ iothrid->name, iothrid->iothread_id) < 0)
+ goto error;
+ } else {
+ if (virAsprintf(&disk_iothr_alias, "iothread%u",
+ iothrid->iothread_id) < 0)
+ goto error;
+ }
+ }
if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
virBufferAddLit(&opt, "virtio-blk-ccw");
- if (disk->iothread)
- virBufferAsprintf(&opt, ",iothread=iothread%u",
disk->iothread);
+ if (disk_iothr_alias)
+ virBufferAsprintf(&opt, ",iothread=%s", disk_iothr_alias);
} else if (disk->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) {
virBufferAddLit(&opt, "virtio-blk-s390");
@@ -4206,9 +4276,11 @@ qemuBuildDriveDevStr(virDomainDefPtr def,
virBufferAddLit(&opt, "virtio-blk-device");
} else {
virBufferAddLit(&opt, "virtio-blk-pci");
- if (disk->iothread)
- virBufferAsprintf(&opt, ",iothread=iothread%u",
disk->iothread);
+ if (disk_iothr_alias)
+ virBufferAsprintf(&opt, ",iothread=%s", disk_iothr_alias);
}
+ VIR_FREE(disk_iothr_alias);
+
qemuBuildIoEventFdStr(&opt, disk->ioeventfd, qemuCaps);
if (disk->event_idx &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_EVENT_IDX)) {
@@ -8794,14 +8866,20 @@ qemuBuildCommandLine(virConnectPtr conn,
if (def->iothreads > 0 &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) {
- /* Create named iothread objects starting with 1. These may be used
+ /* Create iothread objects using the defined iothreadids list
+ * and the defined id and name from the list. These may be used
* by a disk definition which will associate to an iothread by
- * supplying a value of 1 up to the number of iothreads available
- * (since 0 would indicate to not use the feature).
+ * supplying a value of an id from the list
*/
- for (i = 1; i <= def->iothreads; i++) {
+ for (i = 0; i < def->niothreadids; i++) {
virCommandAddArg(cmd, "-object");
- virCommandAddArgFormat(cmd, "iothread,id=iothread%zu", i);
+ if (def->iothreadids[i]->name)
+ virCommandAddArgFormat(cmd, "iothread,id=%s_iothread%u",
+ def->iothreadids[i]->name,
+ def->iothreadids[i]->iothread_id);
+ else
+ virCommandAddArgFormat(cmd, "iothread,id=iothread%u",
+ def->iothreadids[i]->iothread_id);
}
}
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index a29db41..70be7ec 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -238,6 +238,10 @@ int qemuOpenVhostNet(virDomainDefPtr def,
int *vhostfd,
size_t *vhostfdSize);
+int qemuDomainParseIOThreadAlias(char *alias,
+ unsigned int *iothread_id,
+ char **name);
+
int qemuNetworkPrepareDevices(virDomainDefPtr def);
/*
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 2b4a519..7244c2c 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -442,8 +442,10 @@ qemuDomainObjPrivateFree(void *data)
virDomainChrSourceDefFree(priv->monConfig);
qemuDomainObjFreeJob(priv);
VIR_FREE(priv->vcpupids);
- for (i = 0; i < priv->niothreadpids; i++)
+ for (i = 0; i < priv->niothreadpids; i++) {
+ VIR_FREE(priv->iothreadpids[i]->name);
VIR_FREE(priv->iothreadpids[i]);
+ }
VIR_FREE(priv->iothreadpids);
VIR_FREE(priv->lockState);
VIR_FREE(priv->origname);
@@ -510,8 +512,16 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data)
virBufferAddLit(buf, "<iothreads>\n");
virBufferAdjustIndent(buf, 2);
for (i = 0; i < priv->niothreadpids; i++) {
- virBufferAsprintf(buf, "<iothread pid='%d'/>\n",
- priv->iothreadpids[i]->thread_id);
+ if (priv->iothreadpids[i]->name)
+ virBufferAsprintf(buf,
+ "<iothread pid='%d'
iothreadid='%u' name='%s'/>\n",
+ priv->iothreadpids[i]->thread_id,
+ priv->iothreadpids[i]->iothread_id,
+ priv->iothreadpids[i]->name);
+ else
+ virBufferAsprintf(buf, "<iothread pid='%d'
iothreadid='%u'/>\n",
+ priv->iothreadpids[i]->thread_id,
+ priv->iothreadpids[i]->iothread_id);
}
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</iothreads>\n");
@@ -648,6 +658,7 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data)
for (i = 0; i < n; i++) {
qemuDomainIOThreadInfoPtr info = NULL;
char *pidstr = virXMLPropString(nodes[i], "pid");
+ char *idstr;
if (!pidstr || VIR_ALLOC(info) < 0) {
VIR_FREE(pidstr);
@@ -661,6 +672,29 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data)
goto error;
}
VIR_FREE(pidstr);
+
+ /* Prior to 1.2.15 - iothread_id's were generated by starting
+ * at 1 and incrementing to the number of iothread's. So follow
+ * that here for any running domain that has iothreads configured
+ * so that the domain doesn't disappear. As of 1.2.15, the write
+ * of the running domain will include the iothreadid, so it
+ * should be found. The 'name' is optional and also is only
+ * available as of 1.2.15 - the only way it'll be present is if
+ * idstr is present and something was defined; otherwise, it'll
+ * be NULL which indicates usage of only the default algorithm.
+ */
+ idstr = virXMLPropString(nodes[i], "iothreadid");
+ if (!idstr) {
+ info->iothread_id = i + 1;
+ } else {
+ if (virStrToLong_ui(idstr, NULL, 10,
+ &info->iothread_id) < 0) {
+ VIR_FREE(idstr);
+ goto error;
+ }
+ info->name = virXMLPropString(nodes[i], "name");
+ }
+ VIR_FREE(idstr);
}
VIR_FREE(nodes);
}
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 2f75c70..515d73b 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -140,6 +140,8 @@ typedef struct _qemuDomainIOThreadInfo qemuDomainIOThreadInfo;
typedef qemuDomainIOThreadInfo *qemuDomainIOThreadInfoPtr;
struct _qemuDomainIOThreadInfo {
int thread_id;
+ unsigned int iothread_id;
+ char *name;
};
typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 1c575d0..d99f886 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5827,14 +5827,20 @@ qemuDomainGetIOThreadsLive(virQEMUDriverPtr driver,
goto endjob;
for (i = 0; i < niothreads; i++) {
+ unsigned int iothread_id;
+ char *name = NULL;
virBitmapPtr map = NULL;
- if (VIR_ALLOC(info_ret[i]) < 0)
+ if (qemuDomainParseIOThreadAlias(iothreads[i]->name,
+ &iothread_id, &name) < 0)
goto endjob;
- if (virStrToLong_ui(iothreads[i]->name + strlen("iothread"), NULL,
10,
- &info_ret[i]->iothread_id) < 0)
+ if (VIR_ALLOC(info_ret[i]) < 0) {
+ VIR_FREE(name);
goto endjob;
+ }
+ info_ret[i]->iothread_id = iothread_id;
+ VIR_FREE(name);
if (virProcessGetAffinity(iothreads[i]->thread_id, &map, hostcpus) <
0)
goto endjob;
@@ -5889,19 +5895,19 @@ qemuDomainGetIOThreadsConfig(virDomainDefPtr targetDef,
if (VIR_ALLOC_N(info_ret, targetDef->iothreads) < 0)
goto cleanup;
- for (i = 0; i < targetDef->iothreads; i++) {
+ for (i = 0; i < targetDef->niothreadids; i++) {
virDomainPinDefPtr pininfo;
if (VIR_ALLOC(info_ret[i]) < 0)
goto cleanup;
- /* IOThreads being counting at 1 */
- info_ret[i]->iothread_id = i + 1;
+ /* IOThread ID's are taken from the iothreadids list */
+ info_ret[i]->iothread_id = targetDef->iothreadids[i]->iothread_id;
/* Initialize the cpumap */
pininfo = virDomainPinFind(targetDef->cputune.iothreadspin,
targetDef->cputune.niothreadspin,
- i + 1);
+ targetDef->iothreadids[i]->iothread_id);
if (!pininfo) {
if (targetDef->cpumask) {
cpumask = targetDef->cpumask;
@@ -5988,6 +5994,7 @@ qemuDomainPinIOThread(virDomainPtr dom,
unsigned int flags)
{
int ret = -1;
+ size_t i;
virQEMUDriverPtr driver = dom->conn->privateData;
virQEMUDriverConfigPtr cfg = NULL;
virDomainObjPtr vm;
@@ -6051,10 +6058,13 @@ qemuDomainPinIOThread(virDomainPtr dom,
goto endjob;
}
- if (iothread_id > priv->niothreadpids) {
+ for (i = 0; i < priv->niothreadpids; i++) {
+ if (iothread_id == priv->iothreadpids[i]->iothread_id)
+ break;
+ }
+ if (i == priv->niothreadpids) {
virReportError(VIR_ERR_INVALID_ARG,
- _("iothread value out of range %d > %zu"),
- iothread_id, priv->niothreadpids);
+ _("iothread value %d not found"), iothread_id);
goto endjob;
}
@@ -6092,7 +6102,7 @@ qemuDomainPinIOThread(virDomainPtr dom,
goto endjob;
}
} else {
- if (virProcessSetAffinity(priv->iothreadpids[iothread_id -
1]->thread_id,
+ if (virProcessSetAffinity(priv->iothreadpids[i]->thread_id,
pcpumap) < 0) {
virReportError(VIR_ERR_SYSTEM_ERROR,
_("failed to set cpu affinity for IOThread
%d"),
@@ -10039,7 +10049,8 @@ qemuDomainSetNumaParamsLive(virDomainObjPtr vm,
}
for (i = 0; i < priv->niothreadpids; i++) {
- if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_IOTHREAD, i + 1,
+ if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_IOTHREAD,
+ priv->iothreadpids[i]->iothread_id,
false, &cgroup_temp) < 0 ||
virCgroupSetCpusetMems(cgroup_temp, nodeset_str) < 0)
goto cleanup;
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 939b8f5..a17ce70 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -2271,8 +2271,24 @@ qemuProcessDetectIOThreadPIDs(virQEMUDriverPtr driver,
goto cleanup;
priv->niothreadpids = niothreads;
- for (i = 0; i < priv->niothreadpids; i++)
- priv->iothreadpids[i]->thread_id = iothreads[i]->thread_id;
+ for (i = 0; i < priv->niothreadpids; i++) {
+ unsigned int iothread_id;
+ char *name = NULL;
+ qemuDomainIOThreadInfoPtr info;
+
+ if (qemuDomainParseIOThreadAlias(iothreads[i]->name,
+ &iothread_id, &name) < 0)
+ goto cleanup;
+
+ if (VIR_ALLOC(info) < 0) {
+ VIR_FREE(name);
+ goto cleanup;
+ }
+ priv->iothreadpids[i] = info;
+ info->thread_id = iothreads[i]->thread_id;
+ info->iothread_id = iothread_id;
+ info->name = name;
+ }
ret = 0;
@@ -2462,11 +2478,11 @@ qemuProcessSetIOThreadsAffinity(virDomainObjPtr vm)
return -1;
}
- for (i = 0; i < def->iothreads; i++) {
+ for (i = 0; i < priv->niothreadpids; i++) {
/* set affinity only for existing vcpus */
if (!(pininfo = virDomainPinFind(def->cputune.iothreadspin,
def->cputune.niothreadspin,
- i + 1)))
+ priv->iothreadpids[i]->iothread_id)))
continue;
if (virProcessSetAffinity(priv->iothreadpids[i]->thread_id,
@@ -2521,7 +2537,8 @@ qemuProcessSetSchedulers(virDomainObjPtr vm)
}
for (i = 0; i < priv->niothreadpids; i++) {
- if (qemuProcessSetSchedParams(i + 1, priv->iothreadpids[i]->thread_id,
+ if (qemuProcessSetSchedParams(priv->iothreadpids[i]->iothread_id,
+ priv->iothreadpids[i]->thread_id,
vm->def->cputune.niothreadsched,
vm->def->cputune.iothreadsched) < 0)
return -1;
@@ -5306,6 +5323,8 @@ void qemuProcessStop(virQEMUDriverPtr driver,
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
VIR_FREE(priv->vcpupids);
priv->nvcpupids = 0;
+ for (i = 0; i < priv->niothreadpids; i++)
+ VIR_FREE(priv->iothreadpids[i]);
VIR_FREE(priv->iothreadpids);
priv->niothreadpids = 0;
virObjectUnref(priv->qemuCaps);
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.args
b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.args
new file mode 100644
index 0000000..444cd17
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.args
@@ -0,0 +1,10 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu -S -M \
+pc -m 214 -smp 2 \
+-object iothread,id=iothread5 \
+-object iothread,id=iothread6 \
+-object iothread,id=iothread1 \
+-object iothread,id=iothread2 \
+-nographic -monitor \
+unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb -hda \
+/dev/HostVG/QEMUGuest1 -net none -serial none -parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.xml
b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.xml
new file mode 100644
index 0000000..c631677
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids-partial.xml
@@ -0,0 +1,33 @@
+<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>
+ <iothreadids>
+ <iothread id='5'/>
+ <iothread id='6'/>
+ </iothreadids>
+ <os>
+ <type arch='i686' 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</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <memballoon model='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.args
b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.args
new file mode 100644
index 0000000..68998f6
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.args
@@ -0,0 +1,8 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu -S -M \
+pc -m 214 -smp 2 \
+-object iothread,id=iothread2 \
+-object iothread,id=iothread4 \
+-nographic -monitor \
+unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb -hda \
+/dev/HostVG/QEMUGuest1 -net none -serial none -parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.xml
b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.xml
new file mode 100644
index 0000000..d70e74b
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-ids.xml
@@ -0,0 +1,33 @@
+<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>2</iothreads>
+ <iothreadids>
+ <iothread id='2'/>
+ <iothread id='4'/>
+ </iothreadids>
+ <os>
+ <type arch='i686' 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</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <memballoon model='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.args
b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.args
new file mode 100644
index 0000000..824b7ac
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.args
@@ -0,0 +1,17 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu -S -M \
+pc -m 214 -smp 2 \
+-object iothread,id=test1disk_iothread1 \
+-object iothread,id=test2disk_iothread2 \
+-nographic -nodefaults -monitor \
+unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \
+-drive file=/dev/HostVG/QEMUGuest1,if=none,id=drive-ide0-0-0 \
+-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \
+-drive file=/var/lib/libvirt/images/iothrtest1.img,if=none,\
+id=drive-virtio-disk1 \
+-device virtio-blk-pci,iothread=test1disk_iothread1,bus=pci.0,addr=0x4,\
+drive=drive-virtio-disk1,id=virtio-disk1 \
+-drive file=/var/lib/libvirt/images/iothrtest2.img,if=none,\
+id=drive-virtio-disk2 \
+-device virtio-blk-pci,iothread=test2disk_iothread2,bus=pci.0,addr=0x3,\
+drive=drive-virtio-disk2,id=virtio-disk2
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.xml
b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.xml
new file mode 100644
index 0000000..85b502f
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-iothreads-name.xml
@@ -0,0 +1,44 @@
+<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>2</iothreads>
+ <iothreadids>
+ <iothread id='1' name='test1disk'/>
+ <iothread id='2' name='test2disk'/>
+ </iothreadids>
+ <os>
+ <type arch='i686' 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</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='0'/>
+ </disk>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='raw' iothread='1'/>
+ <source file='/var/lib/libvirt/images/iothrtest1.img'/>
+ <target dev='vdb' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x04' function='0x0'/>
+ </disk>
+ <disk type='file' device='disk'>
+ <driver name='qemu' type='raw' iothread='2'/>
+ <source file='/var/lib/libvirt/images/iothrtest2.img'/>
+ <target dev='vdc' bus='virtio'/>
+ </disk>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <memballoon model='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index c02555d..1508d59 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1224,6 +1224,10 @@ mymain(void)
DO_TEST("smp", QEMU_CAPS_SMP_TOPOLOGY);
DO_TEST("iothreads", QEMU_CAPS_OBJECT_IOTHREAD);
+ DO_TEST("iothreads-ids", QEMU_CAPS_OBJECT_IOTHREAD);
+ DO_TEST("iothreads-ids-partial", QEMU_CAPS_OBJECT_IOTHREAD);
+ DO_TEST("iothreads-name", QEMU_CAPS_OBJECT_IOTHREAD, QEMU_CAPS_DEVICE,
+ QEMU_CAPS_DRIVE);
DO_TEST("iothreads-disk", QEMU_CAPS_OBJECT_IOTHREAD, QEMU_CAPS_DEVICE,
QEMU_CAPS_DRIVE);
DO_TEST("iothreads-disk-virtio-ccw", QEMU_CAPS_OBJECT_IOTHREAD,
QEMU_CAPS_DEVICE,
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 817e408..a9529fd 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -492,6 +492,9 @@ mymain(void)
DO_TEST("smp");
DO_TEST("iothreads");
+ DO_TEST("iothreads-ids");
+ DO_TEST("iothreads-ids-partial");
+ DO_TEST("iothreads-name");
DO_TEST_DIFFERENT("cputune-iothreads");
DO_TEST("iothreads-disk");
DO_TEST("iothreads-disk-virtio-ccw");
--
2.1.0