Add validation and formatting of the commandline arguments for
'iothread-vq-mapping' parameter. The validation logic mirrors waht qemu
allows.
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
src/hypervisor/domain_driver.c | 15 ++++-
src/qemu/qemu_command.c | 45 +++++++++++++
src/qemu/qemu_validate.c | 117 +++++++++++++++++++++++++++++----
3 files changed, 165 insertions(+), 12 deletions(-)
diff --git a/src/hypervisor/domain_driver.c b/src/hypervisor/domain_driver.c
index d9469ad6f9..85d68b056c 100644
--- a/src/hypervisor/domain_driver.c
+++ b/src/hypervisor/domain_driver.c
@@ -555,7 +555,20 @@ virDomainDriverDelIOThreadCheck(virDomainDef *def,
}
for (i = 0; i < def->ndisks; i++) {
- if (def->disks[i]->iothread == iothread_id) {
+ GSList *n;
+ bool inuse = false;
+
+ for (n = def->disks[i]->iothreads; n; n = n->next) {
+ virDomainDiskIothreadDef *iothread = n->data;
+
+ if (iothread->id == iothread_id) {
+ inuse = true;
+ break;
+ }
+ }
+
+ if (inuse ||
+ 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'"),
iothread_id, def->disks[i]->dst);
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index bc285c0b6f..147b0eabb1 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1731,6 +1731,45 @@ qemuBuildDriveStr(virDomainDiskDef *disk)
}
+static virJSONValue *
+qemuBuildDiskDeviceIothreadMappingProps(GSList *iothreads)
+{
+ g_autoptr(virJSONValue) ret = virJSONValueNewArray();
+ GSList *n;
+
+ for (n = iothreads; n; n = n->next) {
+ virDomainDiskIothreadDef *ioth = n->data;
+ g_autoptr(virJSONValue) props = NULL;
+ g_autoptr(virJSONValue) queues = NULL;
+ g_autofree char *alias = g_strdup_printf("iothread%u", ioth->id);
+ size_t i;
+
+ if (ioth->nqueues > 0) {
+ queues = virJSONValueNewArray();
+
+ for (i = 0; i < ioth->nqueues; i++) {
+ g_autoptr(virJSONValue) vq =
virJSONValueNewNumberUint(ioth->queues[i]);
+
+ if (virJSONValueArrayAppend(queues, &vq))
+ return NULL;
+ }
+ }
+
+ if (virJSONValueObjectAdd(&props,
+ "s:iothread", alias,
+ "A:vqs", &queues,
+ NULL) < 0)
+ return NULL;
+
+
+ if (virJSONValueArrayAppend(ret, &props))
+ return NULL;
+ }
+
+ return g_steal_pointer(&ret);
+}
+
+
virJSONValue *
qemuBuildDiskDeviceProps(const virDomainDef *def,
virDomainDiskDef *disk,
@@ -1792,11 +1831,16 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,
case VIR_DOMAIN_DISK_BUS_VIRTIO: {
virTristateSwitch scsi = VIR_TRISTATE_SWITCH_ABSENT;
+ g_autoptr(virJSONValue) iothreadMapping = NULL;
g_autofree char *iothread = NULL;
if (disk->iothread > 0)
iothread = g_strdup_printf("iothread%u", disk->iothread);
+ if (disk->iothreads &&
+ !(iothreadMapping =
qemuBuildDiskDeviceIothreadMappingProps(disk->iothreads)))
+ return NULL;
+
if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_VHOST_USER
&&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SCSI)) {
/* if sg_io is true but the scsi option isn't supported,
@@ -1820,6 +1864,7 @@ qemuBuildDiskDeviceProps(const virDomainDef *def,
"T:scsi", scsi,
"p:num-queues", disk->queues,
"p:queue-size", disk->queue_size,
+ "A:iothread-vq-mapping",
&iothreadMapping,
NULL) < 0)
return NULL;
}
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index 9e50c2f45b..9611bff767 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -2669,12 +2669,24 @@ qemuValidateDomainDeviceDefDiskSerial(const char *value)
}
-static bool
+static int
qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def,
- const virDomainDiskDef *disk)
+ const virDomainDiskDef *disk,
+ virQEMUCaps *qemuCaps)
{
+ if (disk->iothread == 0 && !disk->iothreads)
+ return 0;
+
switch (disk->bus) {
case VIR_DOMAIN_DISK_BUS_VIRTIO:
+ if (disk->iothreads) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_IOTHREAD_MAPPING)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("IOThread mapping for disk '%1$s' is not
available with this QEMU binary"),
+ disk->dst);
+ return -1;
+ }
+ }
break;
case VIR_DOMAIN_DISK_BUS_IDE:
@@ -2690,18 +2702,101 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef
*def,
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("IOThreads not available for bus %1$s target %2$s"),
virDomainDiskBusTypeToString(disk->bus), disk->dst);
- return false;
+ return -1;
}
- /* Can we find the disk iothread in the iothreadid list? */
- if (!virDomainIOThreadIDFind(def, disk->iothread)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("Disk iothread '%1$u' not defined in
iothreadid"),
- disk->iothread);
- return false;
+ if (disk->iothreads) {
+ virDomainDiskIothreadDef *first_ioth = disk->iothreads->data;
+ g_autoptr(virBitmap) queueMap = NULL;
+ g_autoptr(GHashTable) iothreads = virHashNew(NULL);
+ ssize_t unused;
+ GSList *n;
+
+ if (first_ioth->queues) {
+ if (disk->queues == 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("disk 'queue' count must be configured for
explicit iothread to queue mapping"));
+ return -1;
+ }
+
+ queueMap = virBitmapNew(disk->queues);
+ }
+
+ /* we are validating that:
+ * - there are no duplicate iothreads
+ * - there are only valid iothreads
+ * - if queue mapping is provided
+ * - queue is in range
+ * - it must be provided for all assigned iothreads
+ * - it must be provided for all queues
+ * - queue must be assigned only once
+ */
+ for (n = disk->iothreads; n; n = n->next) {
+ virDomainDiskIothreadDef *ioth = n->data;
+ g_autofree char *alias = g_strdup_printf("iothread%u",
ioth->id);
+ size_t i;
+
+ if (g_hash_table_contains(iothreads, alias)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Duplicate mapping for iothread
'%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 (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;
+ }
}
- return true;
+ return 0;
}
@@ -2956,7 +3051,7 @@ qemuValidateDomainDeviceDefDiskFrontend(const virDomainDiskDef
*disk,
qemuValidateDomainDeviceDefDiskSerial(disk->serial) < 0)
return -1;
- if (disk->iothread && !qemuValidateDomainDeviceDefDiskIOThreads(def,
disk))
+ if (qemuValidateDomainDeviceDefDiskIOThreads(def, disk, qemuCaps) < 0)
return -1;
return 0;
--
2.43.0