From nobody Wed Mar 12 17:53:34 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1741016025329285.2381561980684; Mon, 3 Mar 2025 07:33:45 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 91DB31450; Mon, 3 Mar 2025 10:33:44 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id E19811353; Mon, 3 Mar 2025 10:31:25 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id 7DDF8126A; Mon, 3 Mar 2025 10:31:22 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 6947C1392 for ; Mon, 3 Mar 2025 10:31:06 -0500 (EST) Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-318-WfrRYwkYMVSkazg7oKFX3Q-1; Mon, 03 Mar 2025 10:31:04 -0500 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0B2FF1801BC4 for ; Mon, 3 Mar 2025 15:31:04 +0000 (UTC) Received: from speedmetal.lan (unknown [10.44.22.15]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id EAE811956094; Mon, 3 Mar 2025 15:31:02 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1741015866; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0J/HHVMyOpuaqCFzcg4IXpyOZe9GtaPipoQQPu6ZdMQ=; b=dQYaNHGLANMIf+l3v5QHOjN7XFwGd1h+CMOU9jmgVno354TSqn/VI4N8r8sWpZ7TVpSjKe s1I6JW4LFiSiGplE1UCUv/ywH6ujzoLoK77n23wj3RoQ+h6JxD0UkRcVgjNMxPiVM+6nZ3 02dR0yPNavCs2ik0SS3AFfprTo/Tflw= X-MC-Unique: WfrRYwkYMVSkazg7oKFX3Q-1 X-Mimecast-MFC-AGG-ID: WfrRYwkYMVSkazg7oKFX3Q_1741015864 From: Peter Krempa To: devel@lists.libvirt.org Subject: [PATCH v2 07/11] virDomainIothreadMapping: Add support for naming certain virt queues Date: Mon, 3 Mar 2025 16:30:45 +0100 Message-ID: <2801695af62a309c5219dfcb9581bd6e6c3ce9bf.1741015346.git.pkrempa@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: IKJ9Y1QIy7TQX-KYzb0rnHC1wJEIsxDELwosF0jPIFI_1741015864 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: KOE6FMD4RAWTM6LG3V26ANVJIB5EVM4U X-Message-ID-Hash: KOE6FMD4RAWTM6LG3V26ANVJIB5EVM4U X-MailFrom: pkrempa@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: Stefan Hajnoczi X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1741016026120019000 Content-Type: text/plain; charset="utf-8" The 'virtio-scsi' device has two extra queues 'ctrl' and 'event'. Convert the existing code to treat the 'id' virtqueue attribute as a string so that we can use the names of the queues instead of making the user refer to them via magic numbers. This patch converts the code to store the strings instead and modifies the qemu code to pass a mapping hash table to lookup the numbers that qemu understands. Signed-off-by: Peter Krempa --- src/conf/domain_conf.c | 14 ++++---- src/conf/domain_conf.h | 3 +- src/qemu/qemu_command.c | 64 +++++++++++++++++++++++++++++++----- src/qemu/qemu_command.h | 8 +++++ src/qemu/qemu_domain.c | 10 +++--- src/qemu/qemu_validate.c | 71 +++++++++++++++++++++++----------------- 6 files changed, 116 insertions(+), 54 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2b9955a1a0..bb82264758 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2373,7 +2373,7 @@ virDomainIothreadMappingDefFree(virDomainIothreadMapp= ingDef *def) if (!def) return; - g_free(def->queues); + g_strfreev(def->queues); g_free(def); } @@ -7967,14 +7967,12 @@ virDomainIothreadMappingDefParse(xmlNodePtr driverN= ode, if (queueNodes->len > 0) { size_t q; - iothdef->queues =3D g_new0(unsigned int, queueNodes->len); - iothdef->nqueues =3D queueNodes->len; + iothdef->queues =3D g_new0(char *, queueNodes->len + 1); for (q =3D 0; q < queueNodes->len; q++) { xmlNodePtr queueNode =3D g_ptr_array_index(queueNodes, q); - if (virXMLPropUInt(queueNode, "id", 10, VIR_XML_PROP_REQUI= RED, - &(iothdef->queues[q])) < 0) + if (!(iothdef->queues[q] =3D virXMLPropStringRequired(queu= eNode, "id"))) return -1; } } @@ -23182,10 +23180,10 @@ virDomainIothreadMappingDefFormat(virBuffer *buf, virBufferAsprintf(&iothreadAttrBuf, " id=3D'%u'", iothDef->id); if (iothDef->queues) { - size_t q; + char **q; - for (q =3D 0; q < iothDef->nqueues; q++) - virBufferAsprintf(&iothreadChildBuf, "\n= ", iothDef->queues[q]); + for (q =3D iothDef->queues; *q; q++) + virBufferEscapeString(&iothreadChildBuf, "\n", *q); } virXMLFormatElement(&iothreadsChildBuf, "iothread", &iothreadAttrB= uf, &iothreadChildBuf); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d4fa79cb84..d56a5a22e0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -511,8 +511,7 @@ struct _virDomainIothreadMappingDef { unsigned int id; /* optional list of virtqueues the iothread should handle */ - unsigned int *queues; - size_t nqueues; + char **queues; }; typedef struct _virDomainIothreadMappingDef virDomainIothreadMappingDef; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 0ad73af335..14d62b98e4 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1574,7 +1574,8 @@ qemuBuildDriveStr(virDomainDiskDef *disk) static virJSONValue * -qemuBuildIothreadMappingProps(GSList *iothreads) +qemuBuildIothreadMappingProps(GSList *iothreads, + GHashTable *queuemap) { g_autoptr(virJSONValue) ret =3D virJSONValueNewArray(); GSList *n; @@ -1584,13 +1585,20 @@ qemuBuildIothreadMappingProps(GSList *iothreads) g_autoptr(virJSONValue) props =3D NULL; g_autoptr(virJSONValue) queues =3D NULL; g_autofree char *alias =3D g_strdup_printf("iothread%u", ioth->id); - size_t i; - if (ioth->nqueues > 0) { + if (ioth->queues) { + char **q; queues =3D virJSONValueNewArray(); - for (i =3D 0; i < ioth->nqueues; i++) { - g_autoptr(virJSONValue) vq =3D virJSONValueNewNumberUint(i= oth->queues[i]); + for (q =3D ioth->queues; *q; q++) { + g_autoptr(virJSONValue) vq =3D NULL; + struct qemuVirtioIothreadMap *mapent; + + if (!(mapent =3D g_hash_table_lookup(queuemap, *q))) { + return NULL; + } + + vq =3D virJSONValueNewNumberUint(mapent->id); if (virJSONValueArrayAppend(queues, &vq)) return NULL; @@ -1612,6 +1620,43 @@ qemuBuildIothreadMappingProps(GSList *iothreads) } +static GHashTable * +qemuCommandGetVirtioIothreadMap(size_t nqueues, + const char **internal_queues) +{ + g_autoptr(GHashTable) ret =3D virHashNew(g_free); + size_t qid =3D 0; + const char **q; + size_t i; + + for (q =3D internal_queues; q && *q; q++) { + struct qemuVirtioIothreadMap *ent =3D g_new0(struct qemuVirtioIoth= readMap, 1); + + ent->id =3D qid++; + + g_hash_table_insert(ret, g_strdup(*q), ent); + } + + + for (i =3D 0; i < nqueues; i++) { + struct qemuVirtioIothreadMap *ent =3D g_new0(struct qemuVirtioIoth= readMap, 1); + + ent->id =3D qid++; + + g_hash_table_insert(ret, g_strdup_printf("%zu", i), ent); + } + + return g_steal_pointer(&ret); +} + + +GHashTable * +qemuCommandGetVirtioIothreadMapVirtioBlk(const virDomainDiskDef *disk) +{ + return qemuCommandGetVirtioIothreadMap(disk->queues, NULL); +} + + virJSONValue * qemuBuildDiskDeviceProps(const virDomainDef *def, virDomainDiskDef *disk, @@ -1686,9 +1731,12 @@ qemuBuildDiskDeviceProps(const virDomainDef *def, if (disk->iothread > 0) iothread =3D g_strdup_printf("iothread%u", disk->iothread); - if (disk->iothreads && - !(iothreadMapping =3D qemuBuildIothreadMappingProps(disk->ioth= reads))) - return NULL; + if (disk->iothreads) { + g_autoptr(GHashTable) queuemap =3D qemuCommandGetVirtioIothrea= dMapVirtioBlk(disk); + + if (!(iothreadMapping =3D qemuBuildIothreadMappingProps(disk->= iothreads, queuemap))) + return NULL; + } if (virStorageSourceGetActualType(disk->src) !=3D VIR_STORAGE_TYPE= _VHOST_USER && virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SCSI)) { diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 7c891a6c98..636c2571d4 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -272,3 +272,11 @@ qemuAudioDriverTypeFromString(const char *str); int qemuVDPAConnect(const char *devicepath) G_NO_INLINE; + +struct qemuVirtioIothreadMap { + unsigned int id; + bool seen; +}; + +GHashTable * +qemuCommandGetVirtioIothreadMapVirtioBlk(const virDomainDiskDef *disk); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 8be2181156..d16429b6a2 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -6646,7 +6646,6 @@ qemuDomainDiskChangeSupportedIothreads(virDomainDiskD= ef *disk, while (true) { virDomainIothreadMappingDef *old_def; virDomainIothreadMappingDef *new_def; - size_t i; /* match - both empty or both at the end */ if (!old && !new) @@ -6660,13 +6659,12 @@ qemuDomainDiskChangeSupportedIothreads(virDomainDis= kDef *disk, new_def =3D new->data; if (old_def->id !=3D new_def->id || - old_def->nqueues !=3D new_def->nqueues) + !!old_def->queues !=3D !!new_def->queues) goto fail; - for (i =3D 0; i < old_def->nqueues; i++) { - if (old_def->queues[i] !=3D new_def->queues[i]) - goto fail; - } + if (old_def->queues && + !g_strv_equal((const char **) old_def->queues, (const char **)= new_def->queues)) + goto fail; new =3D new->next; old =3D old->next; diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index f3ef1be660..f8586a7dff 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2796,12 +2796,10 @@ qemuValidateDomainDeviceDefDiskSerial(const char *v= alue) static int qemuDomainValidateIothreadMapping(const virDomainDef *def, GSList *iothreads, - size_t queues) + GHashTable *queueMap) { virDomainIothreadMappingDef *first_ioth; - g_autoptr(virBitmap) queueMap =3D NULL; g_autoptr(GHashTable) iothreadMap =3D virHashNew(NULL); - ssize_t unused; GSList *n; if (!iothreads) @@ -2810,13 +2808,11 @@ qemuDomainValidateIothreadMapping(const virDomainDe= f *def, first_ioth =3D iothreads->data; if (first_ioth->queues) { - if (queues =3D=3D 0) { + if (!queueMap) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("'queue' count must be configured for explici= t iothread to queue mapping")); return -1; } - - queueMap =3D virBitmapNew(queues); } /* we are validating that: @@ -2831,7 +2827,6 @@ qemuDomainValidateIothreadMapping(const virDomainDef = *def, for (n =3D iothreads; n; n =3D n->next) { virDomainIothreadMappingDef *ioth =3D n->data; g_autofree char *alias =3D g_strdup_printf("iothread%u", ioth->id); - size_t i; if (g_hash_table_contains(iothreadMap, alias)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -2848,40 +2843,51 @@ qemuDomainValidateIothreadMapping(const virDomainDe= f *def, return -1; } - if (!!queueMap !=3D !!ioth->queues) { + if (!!first_ioth->queues !=3D !!ioth->queues) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("iothread to queue mapping must be provided f= or all iothreads or for none")); return -1; } - for (i =3D 0; i < ioth->nqueues; i++) { - bool hasMapping; + if (ioth->queues) { + char **q; - if (virBitmapGetBit(queueMap, ioth->queues[i], &hasMapping) < = 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("iothread queue '%1$u' mapping out of ran= ge"), - ioth->queues[i]); - return -1; - } + for (q =3D ioth->queues; *q; q++) { + struct qemuVirtioIothreadMap *mapent; - if (hasMapping) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("iothread queue '%1$u' is already assigne= d"), - ioth->queues[i]); - return -1; - } + if (!(mapent =3D g_hash_table_lookup(queueMap, *q))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("iothread queue '%1$s' not available"= ), + *q); + return -1; + } - ignore_value(virBitmapSetBit(queueMap, ioth->queues[i])); + if (mapent->seen) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("iothread queue '%1$s' is already ass= igned"), + *q); + return -1; + } + mapent->seen =3D true; + } } } - if (queueMap) { - if ((unused =3D virBitmapNextClearBit(queueMap, -1)) >=3D 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("missing iothread mapping for queue '%1$zd'"), - unused); - return -1; + if (queueMap && first_ioth->queues) { + GHashTableIter htitr; + char *name; + struct qemuVirtioIothreadMap *mapent; + + g_hash_table_iter_init(&htitr, queueMap); + + while (g_hash_table_iter_next(&htitr, (void **) &name, (void **) = &mapent)) { + if (!mapent->seen) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("missing iothread mapping for queue '%1$s= '"), + name); + return -1; + } } } @@ -2894,6 +2900,8 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDom= ainDef *def, const virDomainDiskDef *disk, virQEMUCaps *qemuCaps) { + g_autoptr(GHashTable) queueMap =3D NULL; + if (disk->iothread =3D=3D 0 && !disk->iothreads) return 0; @@ -2925,7 +2933,10 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDo= mainDef *def, return -1; } - if (qemuDomainValidateIothreadMapping(def, disk->iothreads, disk->queu= es) < 0) + if (disk->queues > 0) + queueMap =3D qemuCommandGetVirtioIothreadMapVirtioBlk(disk); + + if (qemuDomainValidateIothreadMapping(def, disk->iothreads, queueMap) = < 0) return -1; if (disk->iothread !=3D 0 && --=20 2.48.1