[PATCH v9 03/17] config: Introduce ThrottleGroup and corresponding XML parsing

Peter Krempa via Devel posted 17 patches 9 months ago
[PATCH v9 03/17] config: Introduce ThrottleGroup and corresponding XML parsing
Posted by Peter Krempa via Devel 9 months ago
From: Chun Feng Wu <danielwuwy@163.com>

Introduce throttlegroup into domain and provide corresponding methods

* Define new struct 'virDomainThrottleGroupDef' and corresponding destructor
* Add operations(Add, Update, Del, ByName, Copy, Free) for 'virDomainThrottleGroupDef'
* Update _virDomainDef to include virDomainThrottleGroupDef
* Support new resource "Parse" and "Format" for operations between struct and DOM XML
* Make sure "group_name" is defined in xml

Signed-off-by: Chun Feng Wu <danielwuwy@163.com>

* Validation check for zero throttle groups.
* Update of code documentation comments.

Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
---
 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 = 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->nthrottlegroups);
+
     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 formats
+ * the <disk> throttling definition virDomainThrottleGroupFormat. */
+#define PARSE_THROTTLEGROUP(val) \
+    if (virXPathULongLong("string(./" #val ")", \
+                          ctxt, &group->val) == -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 = g_new0(virDomainThrottleGroupDef, 1);
+
+    VIR_XPATH_NODE_AUTORESTORE(ctxt)
+    ctxt->node = 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 = virXPathString("string(./group_name)", ctxt))) {
+        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 == 0)
+        return NULL;
+
+    for (i = 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 = 0;
+    g_autofree xmlNodePtr *nodes = NULL;
+
+    if ((n = virXPathNodeSet("./throttlegroups/throttlegroup", ctxt, &nodes)) < 0)
+        return -1;
+
+    if (n == 0)
+        return 0;
+
+    def->throttlegroups = g_new0(virDomainThrottleGroupDef *, n);
+
+    for (i = 0; i < n; i++) {
+        g_autoptr(virDomainThrottleGroupDef) group = NULL;
+
+        if (!(group = 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++] = g_steal_pointer(&group);
+    }
+    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 = 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 = *src;
+    dst->group_name = 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 =  g_new0(virDomainThrottleGroupDef, 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 = 0; i < def->nthrottlegroups; i++) {
+        virDomainThrottleGroupDef *t = 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 = 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->nthrottlegroups);
+            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 <disk> throttling definition virDomainThrottleGroupDefParseXML
+ */
+#define FORMAT_THROTTLE_GROUP(val) \
+        if (group->val > 0) { \
+            virBufferAsprintf(&childBuf, "<" #val ">%llu</" #val ">\n", \
+                              group->val); \
+        }
+
+
+static void
+virDomainThrottleGroupFormat(virBuffer *buf,
+                             virDomainThrottleGroupDef *group)
+{
+    g_auto(virBuffer) childBuf = 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, "<group_name>%s</group_name>\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 = VIR_BUFFER_INIT_CHILD(buf);
+    size_t n;
+
+    for (n = 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 type);
+
+void
+virDomainThrottleGroupDefFree(virDomainThrottleGroupDef *def);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainThrottleGroupDef, virDomainThrottleGroupDefFree);
+
+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;
-- 
2.48.1
Re: [PATCH v9 03/17] config: Introduce ThrottleGroup and corresponding XML parsing
Posted by Ján Tomko via Devel 9 months ago
On a Tuesday in 2025, Peter Krempa via Devel wrote:
>From: Chun Feng Wu <danielwuwy@163.com>
>
>Introduce throttlegroup into domain and provide corresponding methods
>
>* Define new struct 'virDomainThrottleGroupDef' and corresponding destructor
>* Add operations(Add, Update, Del, ByName, Copy, Free) for 'virDomainThrottleGroupDef'
>* Update _virDomainDef to include virDomainThrottleGroupDef
>* Support new resource "Parse" and "Format" for operations between struct and DOM XML
>* Make sure "group_name" is defined in xml
>
>Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
>
>* Validation check for zero throttle groups.
>* Update of code documentation comments.
>
>Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com>
>Reviewed-by: Peter Krempa <pkrempa@redhat.com>
>Signed-off-by: Peter Krempa <pkrempa@redhat.com>
>---
> 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
>+/**
>+ * 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 =  g_new0(virDomainThrottleGroupDef, 1);

The prevailing style is to stick the asterisk to the variable name.

Also, double space before g_new0.

Jano

>+    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 = 0; i < def->nthrottlegroups; i++) {
>+        virDomainThrottleGroupDef *t = 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 = 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->nthrottlegroups);
>+            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 <disk> throttling definition virDomainThrottleGroupDefParseXML
>+ */
>+#define FORMAT_THROTTLE_GROUP(val) \
>+        if (group->val > 0) { \
>+            virBufferAsprintf(&childBuf, "<" #val ">%llu</" #val ">\n", \
>+                              group->val); \
>+        }
>+
>+
>+static void
>+virDomainThrottleGroupFormat(virBuffer *buf,
>+                             virDomainThrottleGroupDef *group)
>+{
>+    g_auto(virBuffer) childBuf = 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, "<group_name>%s</group_name>\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 = VIR_BUFFER_INIT_CHILD(buf);
>+    size_t n;
>+
>+    for (n = 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 type);
>+
>+void
>+virDomainThrottleGroupDefFree(virDomainThrottleGroupDef *def);
>+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainThrottleGroupDef, virDomainThrottleGroupDefFree);
>+
>+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;
>-- 
>2.48.1
>