From nobody Mon Feb 9 12:49:20 2026 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=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1742319437; cv=none; d=zohomail.com; s=zohoarc; b=YoUFHyuFIdQcN0axGIrDfGul+qT+vMjVQA7spqtHCa6Alpxr5gp9Q7sWgSZyA1XNpqG4+/4ZIlC4HJTPMAB8kpyZxa/9uPvE/TeoQqb9y5ePXQ0SXyRW1WbDDjo6n/33TsShED4VGi9gizZ01Iw5ded+GjNHd17OBnl/mCDJy14= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1742319437; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=wcGmo2SjdbWcUqan5j5ItuPHw6gd9R+U8YN/HRFBcDw=; b=laVk9NxQ/ZiiPhdZTNJWbmZt4ZOTMd+YcyUj1HST1lk4ZcE2QRgZ/s+4Or8WUcZECNvkRgrhm7S4iYgzaau2E7Q2cv2n97sjy38SGWDBYgQ6ddPNLKImo/ijfCxaAjXdC4YLN5KCsVAjhPR/Tj9xGUVuH2dfz49Anged+izqQXk= ARC-Authentication-Results: i=1; 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=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1742319437612457.42924380031195; Tue, 18 Mar 2025 10:37:17 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id AD43D144B; Tue, 18 Mar 2025 13:37:16 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 2FC2C1666; Tue, 18 Mar 2025 13:34:55 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id AC54515E8; Tue, 18 Mar 2025 13:34:50 -0400 (EDT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.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 3F7431601 for ; Tue, 18 Mar 2025 13:34:43 -0400 (EDT) Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-589-ChLLILmxMVOQ9kdnFazNMw-1; Tue, 18 Mar 2025 13:34:40 -0400 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id DD07319560AB; Tue, 18 Mar 2025 17:34:39 +0000 (UTC) Received: from speedmetal.redhat.com (unknown [10.45.242.6]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 895903001D13; Tue, 18 Mar 2025 17:34:38 +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, MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL,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=1742319282; 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=tDTMcIdHWqmCjsBXbnY7a3bY7Ms0azZRztLNeO1nW3k=; b=Nv9UeJFGN4XWo7IPfhejEbf6Zzsm9rJbyrGnOt7JcJRpA6XeCt68uXpfuzEhU6HjNpCvBo 4OaN3kPkAEqjHLf/wTZSiM3E2557g/kn4fqWj6d+EHvNvGDRoJALCsnvNI7CLDbJXqdS3Y fesImu0ZOd6ieprMVDz32RsoK8c/MTM= X-MC-Unique: ChLLILmxMVOQ9kdnFazNMw-1 X-Mimecast-MFC-AGG-ID: ChLLILmxMVOQ9kdnFazNMw_1742319280 To: devel@lists.libvirt.org Subject: [PATCH v9 03/17] config: Introduce ThrottleGroup and corresponding XML parsing Date: Tue, 18 Mar 2025 18:34:17 +0100 Message-ID: <1023221c774675e553ff937d88e6bc5a45783eca.1742318898.git.pkrempa@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: sgoNgnqXnJLTL4hWvIwZixG4FyOqNPQFpMK6JO-RSC4_1742319280 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 3U2KFZNUINJSJ3LPABTZB7ZGWKQTNNBR X-Message-ID-Hash: 3U2KFZNUINJSJ3LPABTZB7ZGWKQTNNBR 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: Harikumar Rajkumar , harikumar.rajkumar@ibm.com 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: From: Peter Krempa via Devel Reply-To: Peter Krempa X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1742319439490019100 Content-Type: text/plain; charset="utf-8" From: Chun Feng Wu Introduce throttlegroup into domain and provide corresponding methods * Define new struct 'virDomainThrottleGroupDef' and corresponding destructor * Add operations(Add, Update, Del, ByName, Copy, Free) for 'virDomainThrott= leGroupDef' * Update _virDomainDef to include virDomainThrottleGroupDef * Support new resource "Parse" and "Format" for operations between struct a= nd DOM XML * Make sure "group_name" is defined in xml Signed-off-by: Chun Feng Wu * Validation check for zero throttle groups. * Update of code documentation comments. Signed-off-by: Harikumar Rajkumar Reviewed-by: Peter Krempa Signed-off-by: Peter Krempa --- src/conf/domain_conf.c | 300 +++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 27 ++++ src/conf/virconftypes.h | 2 + src/libvirt_private.syms | 6 + 4 files changed, 335 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5748a89bd1..8306db7cad 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3808,6 +3808,32 @@ virDomainIOThreadIDDefArrayInit(virDomainDef *def, } +void +virDomainThrottleGroupDefFree(virDomainThrottleGroupDef *def) +{ + if (!def) + return; + g_free(def->group_name); + g_free(def); +} + + +static void +virDomainThrottleGroupDefArrayFree(virDomainThrottleGroupDef **def, + int nthrottlegroups) +{ + size_t i; + + if (!def) + return; + + for (i =3D 0; i < nthrottlegroups; i++) + virDomainThrottleGroupDefFree(def[i]); + + g_free(def); +} + + void virDomainResourceDefFree(virDomainResourceDef *resource) { @@ -4107,6 +4133,8 @@ void virDomainDefFree(virDomainDef *def) virDomainIOThreadIDDefArrayFree(def->iothreadids, def->niothreadids); + virDomainThrottleGroupDefArrayFree(def->throttlegroups, def->nthrottle= groups); + g_free(def->defaultIOThread); virBitmapFree(def->cputune.emulatorpin); @@ -7851,6 +7879,123 @@ virDomainDiskDefIotuneParse(virDomainDiskDef *def, } #undef PARSE_IOTUNE +/* the field changes must also be applied to the other function that forma= ts + * the throttling definition virDomainThrottleGroupFormat. */ +#define PARSE_THROTTLEGROUP(val) \ + if (virXPathULongLong("string(./" #val ")", \ + ctxt, &group->val) =3D=3D -2) { \ + virReportError(VIR_ERR_XML_ERROR, \ + _("throttle group field '%1$s' must be an integer")= , #val); \ + return NULL; \ + } + + +static virDomainThrottleGroupDef * +virDomainThrottleGroupDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt) +{ + g_autoptr(virDomainThrottleGroupDef) group =3D g_new0(virDomainThrottl= eGroupDef, 1); + + VIR_XPATH_NODE_AUTORESTORE(ctxt) + ctxt->node =3D node; + + PARSE_THROTTLEGROUP(total_bytes_sec); + PARSE_THROTTLEGROUP(read_bytes_sec); + PARSE_THROTTLEGROUP(write_bytes_sec); + PARSE_THROTTLEGROUP(total_iops_sec); + PARSE_THROTTLEGROUP(read_iops_sec); + PARSE_THROTTLEGROUP(write_iops_sec); + + PARSE_THROTTLEGROUP(total_bytes_sec_max); + PARSE_THROTTLEGROUP(read_bytes_sec_max); + PARSE_THROTTLEGROUP(write_bytes_sec_max); + PARSE_THROTTLEGROUP(total_iops_sec_max); + PARSE_THROTTLEGROUP(read_iops_sec_max); + PARSE_THROTTLEGROUP(write_iops_sec_max); + + PARSE_THROTTLEGROUP(size_iops_sec); + + PARSE_THROTTLEGROUP(total_bytes_sec_max_length); + PARSE_THROTTLEGROUP(read_bytes_sec_max_length); + PARSE_THROTTLEGROUP(write_bytes_sec_max_length); + PARSE_THROTTLEGROUP(total_iops_sec_max_length); + PARSE_THROTTLEGROUP(read_iops_sec_max_length); + PARSE_THROTTLEGROUP(write_iops_sec_max_length); + + /* group_name is required */ + if (!(group->group_name =3D virXPathString("string(./group_name)", ctx= t))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing group name")); + return NULL; + } + + return g_steal_pointer(&group); +} +#undef PARSE_THROTTLEGROUP + + +/** + * virDomainThrottleGroupByName: + * @def: domain definition + * @name: throttle group name + * + * search throttle group within domain definition + * by @name + * + * Returns a pointer to throttle group found. + */ +virDomainThrottleGroupDef * +virDomainThrottleGroupByName(const virDomainDef *def, + const char *name) +{ + size_t i; + + if (!def->throttlegroups || def->nthrottlegroups =3D=3D 0) + return NULL; + + for (i =3D 0; i < def->nthrottlegroups; i++) { + if (STREQ(def->throttlegroups[i]->group_name, name)) + return def->throttlegroups[i]; + } + + return NULL; +} + + +static int +virDomainDefThrottleGroupsParse(virDomainDef *def, + xmlXPathContextPtr ctxt) +{ + size_t i; + int n =3D 0; + g_autofree xmlNodePtr *nodes =3D NULL; + + if ((n =3D virXPathNodeSet("./throttlegroups/throttlegroup", ctxt, &no= des)) < 0) + return -1; + + if (n =3D=3D 0) + return 0; + + def->throttlegroups =3D g_new0(virDomainThrottleGroupDef *, n); + + for (i =3D 0; i < n; i++) { + g_autoptr(virDomainThrottleGroupDef) group =3D NULL; + + if (!(group =3D virDomainThrottleGroupDefParseXML(nodes[i], ctxt))= ) { + return -1; + } + + if (virDomainThrottleGroupByName(def, group->group_name)) { + virReportError(VIR_ERR_XML_ERROR, + _("duplicate group name '%1$s' found"), + group->group_name); + return -1; + } + def->throttlegroups[def->nthrottlegroups++] =3D g_steal_pointer(&g= roup); + } + return 0; +} + static int virDomainDiskDefMirrorParse(virDomainDiskDef *def, @@ -19239,6 +19384,9 @@ virDomainDefParseXML(xmlXPathContextPtr ctxt, if (virDomainDefParseBootOptions(def, ctxt, xmlopt, flags) < 0) return NULL; + if (virDomainDefThrottleGroupsParse(def, ctxt) < 0) + return NULL; + /* analysis of the disk devices */ if ((n =3D virXPathNodeSet("./devices/disk", ctxt, &nodes)) < 0) return NULL; @@ -22502,6 +22650,94 @@ virDomainIOThreadIDDel(virDomainDef *def, } +/** + * virDomainThrottleGroupDefCopy: + * @src: throttle group to be copied from + * @dst: throttle group to be copied to + * + * copy throttle group content from @src to @dst, + * this function does not allocate memory for @dst - the caller must ensure + * @dst is already allocated before calling this function. + */ +void +virDomainThrottleGroupDefCopy(const virDomainThrottleGroupDef *src, + virDomainThrottleGroupDef *dst) +{ + *dst =3D *src; + dst->group_name =3D g_strdup(src->group_name); +} + + +/** + * virDomainThrottleGroupAdd: + * @def: domain definition + * @throttle_group: throttle group definition within domain + * + * add new throttle group into @def + * + * return a pointer to throttle group added + */ +virDomainThrottleGroupDef * +virDomainThrottleGroupAdd(virDomainDef *def, + virDomainThrottleGroupDef *throttle_group) +{ + virDomainThrottleGroupDef * new_group =3D g_new0(virDomainThrottleGro= upDef, 1); + virDomainThrottleGroupDefCopy(throttle_group, new_group); + VIR_APPEND_ELEMENT_COPY(def->throttlegroups, def->nthrottlegroups, new= _group); + return new_group; +} + + +/** + * virDomainThrottleGroupUpdate: + * @def: domain definition + * @info: throttle group definition within domain + * + * Update corresponding throttle group in @def using new config @info. If a + * throttle group with given name doesn't exist this function does nothing. + */ +void +virDomainThrottleGroupUpdate(virDomainDef *def, + virDomainThrottleGroupDef *info) +{ + size_t i; + + if (!info->group_name) + return; + + for (i =3D 0; i < def->nthrottlegroups; i++) { + virDomainThrottleGroupDef *t =3D def->throttlegroups[i]; + + if (STREQ_NULLABLE(t->group_name, info->group_name)) { + VIR_FREE(t->group_name); + virDomainThrottleGroupDefCopy(info, t); + } + } +} + + +/** + * virDomainThrottleGroupDel: + * @def: domain definition + * @name: throttle group name + * + * Delete throttle group @name in @def + */ +void +virDomainThrottleGroupDel(virDomainDef *def, + const char *name) +{ + size_t i; + for (i =3D 0; i < def->nthrottlegroups; i++) { + if (STREQ_NULLABLE(def->throttlegroups[i]->group_name, name)) { + virDomainThrottleGroupDefFree(def->throttlegroups[i]); + VIR_DELETE_ELEMENT(def->throttlegroups, i, def->nthrottlegroup= s); + return; + } + } +} + + static int virDomainEventActionDefFormat(virBuffer *buf, int type, @@ -27699,6 +27935,68 @@ virDomainDefIOThreadsFormat(virBuffer *buf, virDomainDefaultIOThreadDefFormat(buf, def); } +/* + * the field changes must also be applied to the other function that parses + * the throttling definition virDomainThrottleGroupDefParseXML + */ +#define FORMAT_THROTTLE_GROUP(val) \ + if (group->val > 0) { \ + virBufferAsprintf(&childBuf, "<" #val ">%llu\n", \ + group->val); \ + } + + +static void +virDomainThrottleGroupFormat(virBuffer *buf, + virDomainThrottleGroupDef *group) +{ + g_auto(virBuffer) childBuf =3D VIR_BUFFER_INIT_CHILD(buf); + + FORMAT_THROTTLE_GROUP(total_bytes_sec); + FORMAT_THROTTLE_GROUP(read_bytes_sec); + FORMAT_THROTTLE_GROUP(write_bytes_sec); + FORMAT_THROTTLE_GROUP(total_iops_sec); + FORMAT_THROTTLE_GROUP(read_iops_sec); + FORMAT_THROTTLE_GROUP(write_iops_sec); + + FORMAT_THROTTLE_GROUP(total_bytes_sec_max); + FORMAT_THROTTLE_GROUP(read_bytes_sec_max); + FORMAT_THROTTLE_GROUP(write_bytes_sec_max); + FORMAT_THROTTLE_GROUP(total_iops_sec_max); + FORMAT_THROTTLE_GROUP(read_iops_sec_max); + FORMAT_THROTTLE_GROUP(write_iops_sec_max); + + FORMAT_THROTTLE_GROUP(size_iops_sec); + + FORMAT_THROTTLE_GROUP(total_bytes_sec_max_length); + FORMAT_THROTTLE_GROUP(read_bytes_sec_max_length); + FORMAT_THROTTLE_GROUP(write_bytes_sec_max_length); + FORMAT_THROTTLE_GROUP(total_iops_sec_max_length); + FORMAT_THROTTLE_GROUP(read_iops_sec_max_length); + FORMAT_THROTTLE_GROUP(write_iops_sec_max_length); + + virBufferEscapeString(&childBuf, "%s\n", + group->group_name); + + virXMLFormatElement(buf, "throttlegroup", NULL, &childBuf); +} + +#undef FORMAT_THROTTLE_GROUP + +static void +virDomainDefThrottleGroupsFormat(virBuffer *buf, + const virDomainDef *def) +{ + g_auto(virBuffer) childrenBuf =3D VIR_BUFFER_INIT_CHILD(buf); + size_t n; + + for (n =3D 0; n < def->nthrottlegroups; n++) { + virDomainThrottleGroupFormat(&childrenBuf, def->throttlegroups[n]); + } + + virXMLFormatElement(buf, "throttlegroups", NULL, &childrenBuf); +} + static void virDomainIOMMUDefFormat(virBuffer *buf, @@ -28401,6 +28699,8 @@ virDomainDefFormatInternalSetRootName(virDomainDef = *def, virDomainDefIOThreadsFormat(buf, def); + virDomainDefThrottleGroupsFormat(buf, def); + if (virDomainCputuneDefFormat(buf, def, flags) < 0) return -1; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index cbad1b7f7d..ef582c6f87 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3112,6 +3112,9 @@ struct _virDomainDef { virDomainDefaultIOThreadDef *defaultIOThread; + size_t nthrottlegroups; + virDomainThrottleGroupDef **throttlegroups; + virDomainCputune cputune; virDomainResctrlDef **resctrls; @@ -4633,3 +4636,27 @@ virDomainObjGetMessages(virDomainObj *vm, bool virDomainDefHasGraphics(const virDomainDef *def, virDomainGraphicsType typ= e); + +void +virDomainThrottleGroupDefFree(virDomainThrottleGroupDef *def); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainThrottleGroupDef, virDomainThrottle= GroupDefFree); + +virDomainThrottleGroupDef * +virDomainThrottleGroupAdd(virDomainDef *def, + virDomainThrottleGroupDef *throttle_group); + +void +virDomainThrottleGroupUpdate(virDomainDef *def, + virDomainThrottleGroupDef *info); + +void +virDomainThrottleGroupDel(virDomainDef *def, + const char *name); + +virDomainThrottleGroupDef * +virDomainThrottleGroupByName(const virDomainDef *def, + const char *name); + +void +virDomainThrottleGroupDefCopy(const virDomainThrottleGroupDef *src, + virDomainThrottleGroupDef *dst); diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index 59be61cea4..1936ef6ab1 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -80,6 +80,8 @@ typedef struct _virDomainBlkiotune virDomainBlkiotune; typedef struct _virDomainBlockIoTuneInfo virDomainBlockIoTuneInfo; +typedef struct _virDomainBlockIoTuneInfo virDomainThrottleGroupDef; + typedef struct _virDomainCheckpointDef virDomainCheckpointDef; typedef struct _virDomainCheckpointObj virDomainCheckpointObj; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f4ec26eba3..91a49ecf32 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -682,6 +682,12 @@ virDomainTaintMessageTypeFromString; virDomainTaintMessageTypeToString; virDomainTaintTypeFromString; virDomainTaintTypeToString; +virDomainThrottleGroupAdd; +virDomainThrottleGroupByName; +virDomainThrottleGroupDefCopy; +virDomainThrottleGroupDefFree; +virDomainThrottleGroupDel; +virDomainThrottleGroupUpdate; virDomainTimerModeTypeFromString; virDomainTimerModeTypeToString; virDomainTimerNameTypeFromString; --=20 2.48.1