[PATCH v8 00/18] *** qemu: block: Support block disk along with throttle filters ***

*** Support block disk along with throttle filters *** Chun Feng Wu (17): schema: Add new domain elements to support multiple throttle groups schema: Add new domain elements to support multiple throttle filters config: Introduce ThrottleGroup and corresponding XML parsing config: Introduce ThrottleFilter and corresponding XML parsing qemu: monitor: Add support for ThrottleGroup operations tests: Test qemuMonitorJSONGetThrottleGroup and qemuMonitorJSONUpdateThrottleGroup remote: New APIs for ThrottleGroup lifecycle management qemu: Refactor qemuDomainSetBlockIoTune to extract common methods qemu: Implement qemu driver for throttle API qemu: helper: throttle filter nodename and preparation processing qemu: block: Support block disk along with throttle filters config: validate: Verify iotune, throttle group and filter qemuxmlconftest: Add 'throttlefilter' tests test_driver: Test throttle group lifecycle APIs virsh: Refactor iotune options for re-use virsh: Add support for throttle group operations virsh: Add option "throttle-groups" to "attach_disk" Harikumar Rajkumar (1): qemustatusxml2xmldata: Add 'throttlefilter' tests This patch version addresses the feedback provided on patch v5, which includes the following modifications: * Reimplementation of the "get throttle group" to query from the XML. * Allowing the SET API to reset the throttle group config fields to its default in QEMU. * Updating the version to 11.1.0. * Implementing various coding style changes as suggested. * Removal of unnecessary comments. docs/formatdomain.rst | 47 ++ docs/manpages/virsh.rst | 137 +++- include/libvirt/libvirt-domain.h | 14 + src/conf/domain_conf.c | 407 ++++++++++ src/conf/domain_conf.h | 47 ++ src/conf/domain_validate.c | 118 ++- src/conf/schemas/domaincommon.rng | 293 ++++--- src/conf/virconftypes.h | 4 + src/driver-hypervisor.h | 14 + src/libvirt-domain.c | 122 +++ src/libvirt_private.syms | 8 + src/libvirt_public.syms | 6 + src/qemu/qemu_block.c | 136 ++++ src/qemu/qemu_block.h | 49 ++ src/qemu/qemu_command.c | 180 +++++ src/qemu/qemu_command.h | 6 + src/qemu/qemu_domain.c | 77 +- src/qemu/qemu_driver.c | 485 +++++++++--- src/qemu/qemu_hotplug.c | 29 + src/qemu/qemu_monitor.c | 21 + src/qemu/qemu_monitor.h | 9 + src/qemu/qemu_monitor_json.c | 129 +++ src/qemu/qemu_monitor_json.h | 14 + src/remote/remote_daemon_dispatch.c | 105 +++ src/remote/remote_driver.c | 3 + src/remote/remote_protocol.x | 50 +- src/remote_protocol-structs | 28 + src/test/test_driver.c | 367 ++++++--- tests/qemumonitorjsontest.c | 86 ++ .../throttlefilter-in.xml | 392 ++++++++++ .../throttlefilter-out.xml | 393 ++++++++++ tests/qemuxmlactivetest.c | 1 + .../throttlefilter-invalid.x86_64-latest.err | 1 + .../throttlefilter-invalid.xml | 89 +++ .../throttlefilter.x86_64-latest.args | 55 ++ .../throttlefilter.x86_64-latest.xml | 105 +++ tests/qemuxmlconfdata/throttlefilter.xml | 95 +++ tests/qemuxmlconftest.c | 2 + tools/virsh-completer-domain.c | 82 ++ tools/virsh-completer-domain.h | 16 + tools/virsh-domain.c | 736 ++++++++++++++---- 41 files changed, 4428 insertions(+), 530 deletions(-) create mode 100644 tests/qemustatusxml2xmldata/throttlefilter-in.xml create mode 100644 tests/qemustatusxml2xmldata/throttlefilter-out.xml create mode 100644 tests/qemuxmlconfdata/throttlefilter-invalid.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/throttlefilter-invalid.xml create mode 100644 tests/qemuxmlconfdata/throttlefilter.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/throttlefilter.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/throttlefilter.xml -- 2.39.5 (Apple Git-154)

From: Chun Feng Wu <danielwuwy@163.com> Introduce schema for defining '<throttlegroups>' element which configures throttling groups which can be configured for multiple disks. * Refactor "diskIoTune" to extract common schema "iotune" * Add new elements '<throttlegroups>' * <ThrottleGroups> contains <ThrottleGroup> defintion, which references "iotune" Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Update the version to 11.1.0. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- docs/formatdomain.rst | 26 +++ src/conf/schemas/domaincommon.rng | 274 ++++++++++++++++-------------- 2 files changed, 174 insertions(+), 126 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 381bf84f67..f8df50ff4a 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -1972,6 +1972,32 @@ advertisements to the guest OS. (NB: Only qemu driver support) the guest OS itself can choose to circumvent the unavailability of the sleep states (e.g. S4 by turning off completely). +Disk Throttle Group Management +------------------------------ + +:since:`Since 11.1.0` it is possible to create multiple named throttle groups +and then reference them within ``throttlefilters``(sub-element of ``disk`` element) +to form filter chain in QEMU for specific disk. The limits(throttlegroups) are +shared within domain, hence the same group can be referenced by different filters. + +:: + + <domain> + ... + <throttlegroups> + <throttlegroup> + <group_name>limit0</group_name> + <total_bytes_sec>10000000</total_bytes_sec> + <read_iops_sec>400000</read_iops_sec> + <write_iops_sec>100000</write_iops_sec> + </throttlegroup> + </throttlegroups> + ... + </domain> + +``throttlegroup`` + It has the same sub-elements as ``iotune`` (See `Hard drives, floppy disks, CDROMs`_), + The difference is that <group_name> is required. Hypervisor features ------------------- diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 3328a63205..a675746311 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -51,6 +51,7 @@ </zeroOrMore> <ref name="os"/> <ref name="clock"/> + <ref name="throttlegroups"/> <ref name="resources"/> <ref name="features"/> <ref name="events"/> @@ -6863,6 +6864,23 @@ </interleave> </element> </define> + <define name="throttlegroup"> + <element name="throttlegroup"> + <ref name="iotune"/> + </element> + </define> + <!-- + A set of optional throttlegroups + --> + <define name="throttlegroups"> + <optional> + <element name="throttlegroups"> + <zeroOrMore> + <ref name="throttlegroup"/> + </zeroOrMore> + </element> + </optional> + </define> <!-- A set of optional features: PAE, APIC, ACPI, GIC, TCG, HyperV Enlightenment, KVM features, paravirtual spinlocks and HAP support @@ -7845,134 +7863,138 @@ </element> </define> + <define name="iotune"> + <interleave> + <choice> + <element name="total_bytes_sec"> + <data type="unsignedLong"/> + </element> + <group> + <interleave> + <optional> + <element name="read_bytes_sec"> + <data type="unsignedLong"/> + </element> + </optional> + <optional> + <element name="write_bytes_sec"> + <data type="unsignedLong"/> + </element> + </optional> + </interleave> + </group> + </choice> + <choice> + <element name="total_iops_sec"> + <data type="unsignedLong"/> + </element> + <group> + <interleave> + <optional> + <element name="read_iops_sec"> + <data type="unsignedLong"/> + </element> + </optional> + <optional> + <element name="write_iops_sec"> + <data type="unsignedLong"/> + </element> + </optional> + </interleave> + </group> + </choice> + <choice> + <element name="total_bytes_sec_max"> + <data type="unsignedLong"/> + </element> + <group> + <interleave> + <optional> + <element name="read_bytes_sec_max"> + <data type="unsignedLong"/> + </element> + </optional> + <optional> + <element name="write_bytes_sec_max"> + <data type="unsignedLong"/> + </element> + </optional> + </interleave> + </group> + </choice> + <choice> + <element name="total_iops_sec_max"> + <data type="unsignedLong"/> + </element> + <group> + <interleave> + <optional> + <element name="read_iops_sec_max"> + <data type="unsignedLong"/> + </element> + </optional> + <optional> + <element name="write_iops_sec_max"> + <data type="unsignedLong"/> + </element> + </optional> + </interleave> + </group> + </choice> + <optional> + <element name="size_iops_sec"> + <data type="unsignedLong"/> + </element> + </optional> + <optional> + <element name="group_name"> + <text/> + </element> + </optional> + <choice> + <element name="total_bytes_sec_max_length"> + <data type="unsignedLong"/> + </element> + <group> + <interleave> + <optional> + <element name="read_bytes_sec_max_length"> + <data type="unsignedLong"/> + </element> + </optional> + <optional> + <element name="write_bytes_sec_max_length"> + <data type="unsignedLong"/> + </element> + </optional> + </interleave> + </group> + </choice> + <choice> + <element name="total_iops_sec_max_length"> + <data type="unsignedLong"/> + </element> + <group> + <interleave> + <optional> + <element name="read_iops_sec_max_length"> + <data type="unsignedLong"/> + </element> + </optional> + <optional> + <element name="write_iops_sec_max_length"> + <data type="unsignedLong"/> + </element> + </optional> + </interleave> + </group> + </choice> + </interleave> + </define> + <define name="diskIoTune"> <element name="iotune"> - <interleave> - <choice> - <element name="total_bytes_sec"> - <data type="unsignedLong"/> - </element> - <group> - <interleave> - <optional> - <element name="read_bytes_sec"> - <data type="unsignedLong"/> - </element> - </optional> - <optional> - <element name="write_bytes_sec"> - <data type="unsignedLong"/> - </element> - </optional> - </interleave> - </group> - </choice> - <choice> - <element name="total_iops_sec"> - <data type="unsignedLong"/> - </element> - <group> - <interleave> - <optional> - <element name="read_iops_sec"> - <data type="unsignedLong"/> - </element> - </optional> - <optional> - <element name="write_iops_sec"> - <data type="unsignedLong"/> - </element> - </optional> - </interleave> - </group> - </choice> - <choice> - <element name="total_bytes_sec_max"> - <data type="unsignedLong"/> - </element> - <group> - <interleave> - <optional> - <element name="read_bytes_sec_max"> - <data type="unsignedLong"/> - </element> - </optional> - <optional> - <element name="write_bytes_sec_max"> - <data type="unsignedLong"/> - </element> - </optional> - </interleave> - </group> - </choice> - <choice> - <element name="total_iops_sec_max"> - <data type="unsignedLong"/> - </element> - <group> - <interleave> - <optional> - <element name="read_iops_sec_max"> - <data type="unsignedLong"/> - </element> - </optional> - <optional> - <element name="write_iops_sec_max"> - <data type="unsignedLong"/> - </element> - </optional> - </interleave> - </group> - </choice> - <optional> - <element name="size_iops_sec"> - <data type="unsignedLong"/> - </element> - </optional> - <optional> - <element name="group_name"> - <text/> - </element> - </optional> - <choice> - <element name="total_bytes_sec_max_length"> - <data type="unsignedLong"/> - </element> - <group> - <interleave> - <optional> - <element name="read_bytes_sec_max_length"> - <data type="unsignedLong"/> - </element> - </optional> - <optional> - <element name="write_bytes_sec_max_length"> - <data type="unsignedLong"/> - </element> - </optional> - </interleave> - </group> - </choice> - <choice> - <element name="total_iops_sec_max_length"> - <data type="unsignedLong"/> - </element> - <group> - <interleave> - <optional> - <element name="read_iops_sec_max_length"> - <data type="unsignedLong"/> - </element> - </optional> - <optional> - <element name="write_iops_sec_max_length"> - <data type="unsignedLong"/> - </element> - </optional> - </interleave> - </group> - </choice> - </interleave> + <ref name="iotune"/> </element> </define> -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:05 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com> +Disk Throttle Group Management +------------------------------ + +:since:`Since 11.1.0` it is possible to create multiple named throttle groups
11.2.0
+and then reference them within ``throttlefilters``(sub-element of ``disk`` element) +to form filter chain in QEMU for specific disk. The limits(throttlegroups) are +shared within domain, hence the same group can be referenced by different filters.
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

From: Chun Feng Wu <danielwuwy@163.com> Introduce schema for defining '<throttlefilters>' element which references throttling groups to form filter chain in qemu for specific disk * Add new elements '<throttlefilters>' * <ThrottleFilters> can include multiple throttlegroup references to form filter chain in qemu * Chained throttle filters feature in qemu is described at https://gitlab.com/qemu-project/qemu/blob/master/docs/throttle.txt Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Update the version to 11.1.0. * Update the commit message URI. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- docs/formatdomain.rst | 21 +++++++++++++++++++++ src/conf/schemas/domaincommon.rng | 19 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index f8df50ff4a..df25d8f36d 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -2791,6 +2791,15 @@ paravirtualized driver is specified via the ``disk`` element. </backingStore> <target dev='vdh' bus='virtio'/> </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2' /> + <source file='/var/lib/libvirt/images/disk.qcow2'/> + <target dev='vdh' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit2'/> + <throttlefilter group='limit012'/> + </throttlefilters> + </disk> </devices> ... @@ -3293,6 +3302,18 @@ paravirtualized driver is specified via the ``disk`` element. :since:`since after 0.4.4`; "sata" attribute value :since:`since 0.9.7`; "removable" attribute value :since:`since 1.1.3`; "rotation_rate" attribute value :since:`since 7.3.0` +``throttlefilters`` + The optional ``throttlefilters`` element provides the ability to provide additional + per-device throttle chain :since:`Since 11.1.0` + For example, if we have four different disks and we want to limit I/O for each one + and we also want to limit combined I/O of all four disks, we can leverage + ``throttlefilters`` to achieve this goal by setting two ``throttlefilter`` for + each disk: disk's own filter(e.g. limit2) and combined filter(e.g. limit012). + The order of such ``throttlefilter`` doesn't matter within ``throttlefilters``. + ``throttlefilters`` and ``iotune`` should be used exclusively. + + ``throttlefilter`` + The optional ``throttlefilter`` element is to reference defined throttle group. ``iotune`` The optional ``iotune`` element provides the ability to provide additional per-device I/O tuning, with values that can vary for each device (contrast diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index a675746311..7bb89bff7a 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -1636,7 +1636,10 @@ <ref name="encryption"/> </optional> <optional> - <ref name="diskIoTune"/> + <choice> + <ref name="throttlefilters"/> + <ref name="diskIoTune"/> + </choice> </optional> <optional> <ref name="alias"/> @@ -6881,6 +6884,20 @@ </element> </optional> </define> + <!-- + A set of throttlefilters to reference throttlegroups + --> + <define name="throttlefilters"> + <element name="throttlefilters"> + <zeroOrMore> + <element name="throttlefilter"> + <attribute name="group"> + <data type="string"/> + </attribute> + </element> + </zeroOrMore> + </element> + </define> <!-- A set of optional features: PAE, APIC, ACPI, GIC, TCG, HyperV Enlightenment, KVM features, paravirtual spinlocks and HAP support -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:06 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Introduce schema for defining '<throttlefilters>' element which references throttling groups to form filter chain in qemu for specific disk
* Add new elements '<throttlefilters>' * <ThrottleFilters> can include multiple throttlegroup references to form filter chain in qemu * Chained throttle filters feature in qemu is described at https://gitlab.com/qemu-project/qemu/blob/master/docs/throttle.txt
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Update the version to 11.1.0. * Update the commit message URI.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- docs/formatdomain.rst | 21 +++++++++++++++++++++ src/conf/schemas/domaincommon.rng | 19 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

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> --- src/conf/domain_conf.c | 300 ++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 27 ++++ src/conf/virconftypes.h | 2 + 3 files changed, 329 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 49555efc56..40f4bf6f49 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3799,6 +3799,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) { @@ -4086,6 +4112,8 @@ void virDomainDefFree(virDomainDef *def) virDomainIOThreadIDDefArrayFree(def->iothreadids, def->niothreadids); + virDomainThrottleGroupDefArrayFree(def->throttlegroups, def->nthrottlegroups); + g_free(def->defaultIOThread); virBitmapFree(def->cputune.emulatorpin); @@ -7830,6 +7858,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, @@ -19212,6 +19357,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; @@ -22510,6 +22658,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, @@ -27697,6 +27933,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, @@ -28399,6 +28697,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 9da6586e66..211054274f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3079,6 +3079,9 @@ struct _virDomainDef { virDomainDefaultIOThreadDef *defaultIOThread; + size_t nthrottlegroups; + virDomainThrottleGroupDef **throttlegroups; + virDomainCputune cputune; virDomainResctrlDef **resctrls; @@ -4600,3 +4603,27 @@ virDomainObjGetMessages(virDomainObj *vm, bool virDomainDefHasSpiceGraphics(const virDomainDef *def); + +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; -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:07 +0530, Harikumar Rajkumar 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>

From: Chun Feng Wu <danielwuwy@163.com> Introduce throttle filter along with corresponding operations. * Define new struct 'virDomainThrottleFilterDef' and corresponding destructor * Update _virDomainDiskDef to include virDomainThrottleFilterDef * Support throttle filter "Parse" and "Format" for operations between DOM XML and structs. Note, this commit just contains parse/format of group name for throttle filter in domain_conf.c, there is other commit to handle throttle filter nodename parse/format between throttlefilter and diskPrivateData for statusxml in qemu_domain.c when processing qemuDomainDiskPrivate and qemuDomainDiskPrivate Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Error handling for null throttle group. * Update of code documentation comments. * Apply suggested coding style changes. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/conf/domain_conf.c | 99 +++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 20 +++++++++ src/conf/virconftypes.h | 2 + 3 files changed, 121 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 40f4bf6f49..303aa7d1ae 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3825,6 +3825,16 @@ virDomainThrottleGroupDefArrayFree(virDomainThrottleGroupDef **def, } +void +virDomainThrottleFilterDefClear(virDomainThrottleFilterDef *def) +{ + if (!def) + return; + g_free(def->group_name); + g_free(def->nodename); +} + + void virDomainResourceDefFree(virDomainResourceDef *resource) { @@ -7976,6 +7986,50 @@ virDomainDefThrottleGroupsParse(virDomainDef *def, } +static virDomainThrottleFilterDef * +virDomainDiskThrottleFilterDefParse(xmlNodePtr node) +{ + g_autoptr(virDomainThrottleFilterDef) filter = g_new0(virDomainThrottleFilterDef, 1); + + filter->group_name = virXMLPropStringRequired(node, "group"); + + return g_steal_pointer(&filter); +} + + +static int +virDomainDiskDefThrottleFiltersParse(virDomainDiskDef *def, + xmlXPathContextPtr ctxt) +{ + size_t i; + int n = 0; + g_autofree xmlNodePtr *nodes = NULL; + + if ((n = virXPathNodeSet("./throttlefilters/throttlefilter", ctxt, &nodes)) < 0) + return -1; + + if (n) + def->throttlefilters = g_new0(virDomainThrottleFilterDef *, n); + + for (i = 0; i < n; i++) { + g_autoptr(virDomainThrottleFilterDef) filter = NULL; + + if (!(filter = virDomainDiskThrottleFilterDefParse(nodes[i]))) { + return -1; + } + + if (virDomainThrottleFilterFind(def, filter->group_name)) { + virReportError(VIR_ERR_XML_ERROR, + _("duplicate filter name '%1$s' found"), + filter->group_name); + return -1; + } + def->throttlefilters[def->nthrottlefilters++] = g_steal_pointer(&filter); + } + return 0; +} + + static int virDomainDiskDefMirrorParse(virDomainDiskDef *def, xmlNodePtr cur, @@ -8466,6 +8520,9 @@ virDomainDiskDefParseXML(virDomainXMLOption *xmlopt, if (virDomainDiskDefIotuneParse(def, ctxt) < 0) return NULL; + if (virDomainDiskDefThrottleFiltersParse(def, ctxt) < 0) + return NULL; + def->domain_name = virXPathString("string(./backenddomain/@name)", ctxt); def->serial = virXPathString("string(./serial)", ctxt); def->wwn = virXPathString("string(./wwn)", ctxt); @@ -22746,6 +22803,31 @@ virDomainThrottleGroupDel(virDomainDef *def, } +/** + * virDomainThrottleFilterFind: + * @def: domain disk definition + * @name: throttle group name + * + * Search domain disk to find throttle filter referencing + * throttle group with name @name. + * + * Return a pointer to throttle filter found + */ +virDomainThrottleFilterDef * +virDomainThrottleFilterFind(const virDomainDiskDef *def, + const char *name) +{ + size_t i; + + for (i = 0; i < def->nthrottlefilters; i++) { + if (STREQ(name, def->throttlefilters[i]->group_name)) + return def->throttlefilters[i]; + } + + return NULL; +} + + static int virDomainEventActionDefFormat(virBuffer *buf, int type, @@ -23396,6 +23478,21 @@ virDomainDiskDefFormatIotune(virBuffer *buf, #undef FORMAT_IOTUNE +static void +virDomainDiskDefFormatThrottleFilters(virBuffer *buf, + virDomainDiskDef *disk) +{ + size_t i; + g_auto(virBuffer) throttleChildBuf = VIR_BUFFER_INIT_CHILD(buf); + for (i = 0; i < disk->nthrottlefilters; i++) { + g_auto(virBuffer) throttleAttrBuf = VIR_BUFFER_INITIALIZER; + virBufferEscapeString(&throttleAttrBuf, " group='%s'", disk->throttlefilters[i]->group_name); + virXMLFormatElement(&throttleChildBuf, "throttlefilter", &throttleAttrBuf, NULL); + } + virXMLFormatElement(buf, "throttlefilters", NULL, &throttleChildBuf); +} + + static void virDomainDiskDefFormatDriver(virBuffer *buf, virDomainDiskDef *disk) @@ -23682,6 +23779,8 @@ virDomainDiskDefFormat(virBuffer *buf, virDomainDiskDefFormatIotune(&childBuf, def); + virDomainDiskDefFormatThrottleFilters(&childBuf, def); + if (def->src->readonly) virBufferAddLit(&childBuf, "<readonly/>\n"); if (def->src->shared) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 211054274f..2a9912bae8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -520,6 +520,15 @@ void virDomainDiskIothreadDefFree(virDomainDiskIothreadDef *def); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainDiskIothreadDef, virDomainDiskIothreadDefFree); +/* Stores information related to a ThrottleFilter resource. */ +struct _virDomainThrottleFilterDef { + char *group_name; + char *nodename; /* node name of throttle filter object */ + /* node name is a qemu driver implementation + detail and not a configuration field */ +}; + + /* Stores the virtual disk configuration */ struct _virDomainDiskDef { virStorageSource *src; /* non-NULL. XXX Allow NULL for empty cdrom? */ @@ -552,6 +561,9 @@ struct _virDomainDiskDef { virDomainBlockIoTuneInfo blkdeviotune; + size_t nthrottlefilters; + virDomainThrottleFilterDef **throttlefilters; + char *driverName; char *serial; @@ -4627,3 +4639,11 @@ virDomainThrottleGroupByName(const virDomainDef *def, void virDomainThrottleGroupDefCopy(const virDomainThrottleGroupDef *src, virDomainThrottleGroupDef *dst); + +void +virDomainThrottleFilterDefClear(virDomainThrottleFilterDef *def); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainThrottleFilterDef, virDomainThrottleFilterDefClear); + +virDomainThrottleFilterDef * +virDomainThrottleFilterFind(const virDomainDiskDef *def, + const char *name); diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index 1936ef6ab1..c70437bc05 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -82,6 +82,8 @@ typedef struct _virDomainBlockIoTuneInfo virDomainBlockIoTuneInfo; typedef struct _virDomainBlockIoTuneInfo virDomainThrottleGroupDef; +typedef struct _virDomainThrottleFilterDef virDomainThrottleFilterDef; + typedef struct _virDomainCheckpointDef virDomainCheckpointDef; typedef struct _virDomainCheckpointObj virDomainCheckpointObj; -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:08 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Introduce throttle filter along with corresponding operations.
* Define new struct 'virDomainThrottleFilterDef' and corresponding destructor * Update _virDomainDiskDef to include virDomainThrottleFilterDef * Support throttle filter "Parse" and "Format" for operations between DOM XML and structs. Note, this commit just contains parse/format of group name for throttle filter in domain_conf.c, there is other commit to handle throttle filter nodename parse/format between throttlefilter and diskPrivateData for statusxml in qemu_domain.c when processing qemuDomainDiskPrivate and qemuDomainDiskPrivate
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Error handling for null throttle group. * Update of code documentation comments. * Apply suggested coding style changes.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/conf/domain_conf.c | 99 +++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 20 +++++++++ src/conf/virconftypes.h | 2 + 3 files changed, 121 insertions(+)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

On Wed, Feb 19, 2025 at 22:27:08 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Introduce throttle filter along with corresponding operations.
* Define new struct 'virDomainThrottleFilterDef' and corresponding destructor * Update _virDomainDiskDef to include virDomainThrottleFilterDef * Support throttle filter "Parse" and "Format" for operations between DOM XML and structs. Note, this commit just contains parse/format of group name for throttle filter in domain_conf.c, there is other commit to handle throttle filter nodename parse/format between throttlefilter and diskPrivateData for statusxml in qemu_domain.c when processing qemuDomainDiskPrivate and qemuDomainDiskPrivate
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Error handling for null throttle group. * Update of code documentation comments. * Apply suggested coding style changes.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/conf/domain_conf.c | 99 +++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 20 +++++++++ src/conf/virconftypes.h | 2 + 3 files changed, 121 insertions(+)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 40f4bf6f49..303aa7d1ae 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3825,6 +3825,16 @@ virDomainThrottleGroupDefArrayFree(virDomainThrottleGroupDef **def, }
+void +virDomainThrottleFilterDefClear(virDomainThrottleFilterDef *def) +{ + if (!def) + return; + g_free(def->group_name); + g_free(def->nodename); +}
This function is registered as the automatic cleanup helper for virDomainThrottleFilterDef thus must also free the whole object, in which case it should be renamed with ...Free suffix.
+ + void virDomainResourceDefFree(virDomainResourceDef *resource) {
[...]
@@ -552,6 +561,9 @@ struct _virDomainDiskDef {
virDomainBlockIoTuneInfo blkdeviotune;
+ size_t nthrottlefilters; + virDomainThrottleFilterDef **throttlefilters;
Corresponding update of virDomainDiskDefFree is missing so the above pointer and each struct in it is leaked.

From: Chun Feng Wu <danielwuwy@163.com> This change contains QMP requests for ThrottleGroup * ThrottleGroup is updated through "qemuMonitorJSONUpdateThrottleGroup" * ThrottleGroup is retrieved through "qemuMonitorJSONGetThrottleGroup" * ThrottleGroup is deleted by reusing "qemuMonitorDelObject" * ThrottleGroup is added by reusing "qemuMonitorAddObject" * "qemuMonitorMakeThrottleGroupLimits" will be used by building qemu cmd as well Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * change throttle group config conversions P to U allow zero. * Apply suggested coding style changes. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_monitor.c | 34 +++++++++ src/qemu/qemu_monitor.h | 14 ++++ src/qemu/qemu_monitor_json.c | 129 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 14 ++++ 4 files changed, 191 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c069d17265..015f8f11cc 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3037,6 +3037,40 @@ qemuMonitorGetBlockIoThrottle(qemuMonitor *mon, } +int +qemuMonitorThrottleGroupLimits(virJSONValue *limits, + const virDomainThrottleGroupDef *group) +{ + return qemuMonitorMakeThrottleGroupLimits(limits, group); +} + + +int +qemuMonitorUpdateThrottleGroup(qemuMonitor *mon, + const char *qomid, + virDomainBlockIoTuneInfo *info) +{ + VIR_DEBUG("qomid=%s, info=%p", qomid, info); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONUpdateThrottleGroup(mon, qomid, info); +} + + +int +qemuMonitorGetThrottleGroup(qemuMonitor *mon, + const char *groupname, + virDomainBlockIoTuneInfo *reply) +{ + VIR_DEBUG("throttle-group=%s, reply=%p", groupname, reply); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONGetThrottleGroup(mon, groupname, reply); +} + + int qemuMonitorVMStatusToPausedReason(const char *status) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index f7b9263b64..7b58d988d8 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1074,6 +1074,20 @@ int qemuMonitorGetBlockIoThrottle(qemuMonitor *mon, const char *qdevid, virDomainBlockIoTuneInfo *reply); +int +qemuMonitorThrottleGroupLimits(virJSONValue *limits, + const virDomainThrottleGroupDef *group); + +int +qemuMonitorUpdateThrottleGroup(qemuMonitor *mon, + const char *qomid, + virDomainBlockIoTuneInfo *info); + +int +qemuMonitorGetThrottleGroup(qemuMonitor *mon, + const char *groupname, + virDomainBlockIoTuneInfo *reply); + int qemuMonitorSystemWakeup(qemuMonitor *mon); int qemuMonitorGetVersion(qemuMonitor *mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 554572b13d..572fdb944d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4685,6 +4685,135 @@ int qemuMonitorJSONGetBlockIoThrottle(qemuMonitor *mon, return qemuMonitorJSONBlockIoThrottleInfo(devices, qdevid, reply); } + +int +qemuMonitorMakeThrottleGroupLimits(virJSONValue *limits, + const virDomainThrottleGroupDef *group) +{ + if (virJSONValueObjectAdd(&limits, + "U:bps-total", group->total_bytes_sec, + "U:bps-read", group->read_bytes_sec, + "U:bps-write", group->write_bytes_sec, + "U:iops-total", group->total_iops_sec, + "U:iops-read", group->read_iops_sec, + "U:iops-write", group->write_iops_sec, + "U:bps-total-max", group->total_bytes_sec_max, + "U:bps-read-max", group->read_bytes_sec_max, + "U:bps-write-max", group->write_bytes_sec_max, + "U:iops-total-max", group->total_iops_sec_max, + "U:iops-read-max", group->read_iops_sec_max, + "U:iops-write-max", group->write_iops_sec_max, + "U:iops-size", group->size_iops_sec, + "P:bps-total-max-length", group->total_bytes_sec_max_length, + "P:bps-read-max-length", group->read_bytes_sec_max_length, + "P:bps-write-max-length", group->write_bytes_sec_max_length, + "P:iops-total-max-length", group->total_iops_sec_max_length, + "P:iops-read-max-length", group->read_iops_sec_max_length, + "P:iops-write-max-length", group->write_iops_sec_max_length, + NULL) < 0) + return -1; + + return 0; +} + + +int +qemuMonitorJSONUpdateThrottleGroup(qemuMonitor *mon, + const char *qomid, + virDomainBlockIoTuneInfo *info) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) result = NULL; + g_autoptr(virJSONValue) limits = virJSONValueNewObject(); + + if (qemuMonitorMakeThrottleGroupLimits(limits, info) < 0) + return -1; + + if (!(cmd = qemuMonitorJSONMakeCommand("qom-set", + "s:property", "limits", + "s:path", qomid, + "a:value", &limits, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &result) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, result) < 0) + return -1; + + return 0; +} + + +#define GET_THROTTLE_GROUP_VALUE(FIELD, STORE) \ + if (virJSONValueObjectHasKey(ret, FIELD)) { \ + if (virJSONValueObjectGetNumberUlong(ret, FIELD, &reply->STORE) < 0) { \ + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, \ + _("value of throttle group field '%1$s' is malformed"), \ + #STORE); \ + return -1; \ + } \ + } + + +int +qemuMonitorJSONGetThrottleGroup(qemuMonitor *mon, + const char *gname, + virDomainBlockIoTuneInfo *reply) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) result = NULL; + g_autofree char *groupCopy = NULL; + virJSONValue *ret; + + /* prefix group name with "throttle-" in QOM */ + g_autofree char *path = g_strdup_printf("/objects/throttle-%s", gname); + if (!(cmd = qemuMonitorJSONMakeCommand("qom-get", + "s:path", path, + "s:property", "limits", + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &result) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, result) < 0) + return -1; + + if (!(ret = qemuMonitorJSONGetReply(cmd, result, VIR_JSON_TYPE_OBJECT))) + return -1; + + GET_THROTTLE_GROUP_VALUE("bps-total", total_bytes_sec); + GET_THROTTLE_GROUP_VALUE("bps-read", read_bytes_sec); + GET_THROTTLE_GROUP_VALUE("bps-write", write_bytes_sec); + GET_THROTTLE_GROUP_VALUE("iops-total", total_iops_sec); + GET_THROTTLE_GROUP_VALUE("iops-read", read_iops_sec); + GET_THROTTLE_GROUP_VALUE("iops-write", write_iops_sec); + + GET_THROTTLE_GROUP_VALUE("bps-total-max", total_bytes_sec_max); + GET_THROTTLE_GROUP_VALUE("bps-read-max", read_bytes_sec_max); + GET_THROTTLE_GROUP_VALUE("bps-write-max", write_bytes_sec_max); + GET_THROTTLE_GROUP_VALUE("iops-total-max", total_iops_sec_max); + GET_THROTTLE_GROUP_VALUE("iops-read-max", read_iops_sec_max); + GET_THROTTLE_GROUP_VALUE("iops-write-max", write_iops_sec_max); + GET_THROTTLE_GROUP_VALUE("iops-size", size_iops_sec); + + GET_THROTTLE_GROUP_VALUE("bps-total-max-length", total_bytes_sec_max_length); + GET_THROTTLE_GROUP_VALUE("bps-read-max-length", read_bytes_sec_max_length); + GET_THROTTLE_GROUP_VALUE("bps-write-max-length", write_bytes_sec_max_length); + GET_THROTTLE_GROUP_VALUE("iops-total-max-length", total_iops_sec_max_length); + GET_THROTTLE_GROUP_VALUE("iops-read-max-length", read_iops_sec_max_length); + GET_THROTTLE_GROUP_VALUE("iops-write-max-length", write_iops_sec_max_length); + + groupCopy = g_strdup(gname); + reply->group_name = g_steal_pointer(&groupCopy); + + return 0; +} +#undef GET_THROTTLE_GROUP_VALUE + + int qemuMonitorJSONSystemWakeup(qemuMonitor *mon) { g_autoptr(virJSONValue) cmd = NULL; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 25e3ae2cbb..23c845ac76 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -375,6 +375,20 @@ qemuMonitorJSONSetBlockIoThrottle(qemuMonitor *mon, const char *qomid, virDomainBlockIoTuneInfo *info); +int +qemuMonitorMakeThrottleGroupLimits(virJSONValue *limits, + const virDomainThrottleGroupDef *group); + +int +qemuMonitorJSONUpdateThrottleGroup(qemuMonitor *mon, + const char *qomid, + virDomainBlockIoTuneInfo *info); + +int +qemuMonitorJSONGetThrottleGroup(qemuMonitor *mon, + const char *gname, + virDomainBlockIoTuneInfo *reply); + int qemuMonitorJSONGetBlockIoThrottle(qemuMonitor *mon, const char *qdevid, -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:09 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
This change contains QMP requests for ThrottleGroup
* ThrottleGroup is updated through "qemuMonitorJSONUpdateThrottleGroup" * ThrottleGroup is retrieved through "qemuMonitorJSONGetThrottleGroup" * ThrottleGroup is deleted by reusing "qemuMonitorDelObject" * ThrottleGroup is added by reusing "qemuMonitorAddObject" * "qemuMonitorMakeThrottleGroupLimits" will be used by building qemu cmd as well
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* change throttle group config conversions P to U allow zero. * Apply suggested coding style changes.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_monitor.c | 34 +++++++++ src/qemu/qemu_monitor.h | 14 ++++ src/qemu/qemu_monitor_json.c | 129 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 14 ++++ 4 files changed, 191 insertions(+)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

On Wed, Feb 19, 2025 at 22:27:09 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
This change contains QMP requests for ThrottleGroup
* ThrottleGroup is updated through "qemuMonitorJSONUpdateThrottleGroup" * ThrottleGroup is retrieved through "qemuMonitorJSONGetThrottleGroup" * ThrottleGroup is deleted by reusing "qemuMonitorDelObject" * ThrottleGroup is added by reusing "qemuMonitorAddObject" * "qemuMonitorMakeThrottleGroupLimits" will be used by building qemu cmd as well
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* change throttle group config conversions P to U allow zero. * Apply suggested coding style changes.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_monitor.c | 34 +++++++++ src/qemu/qemu_monitor.h | 14 ++++ src/qemu/qemu_monitor_json.c | 129 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 14 ++++ 4 files changed, 191 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c069d17265..015f8f11cc 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c
[...]
+int +qemuMonitorGetThrottleGroup(qemuMonitor *mon, + const char *groupname, + virDomainBlockIoTuneInfo *reply)
The getter is used only in tests which makes no sense. If it's not used just don't add it. I've noticed it by this hunk bein misplaced.

From: Chun Feng Wu <danielwuwy@163.com> Within "testQemuMonitorJSONqemuMonitorJSONUpdateThrottleGroup" * Test qemuMonitorJSONGetThrottleGroup * Test qemuMonitorJSONUpdateThrottleGroup, which updates limits through "qom-set" Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * fix test Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- tests/qemumonitorjsontest.c | 86 +++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index f0f6a329c8..bd1fda145b 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1914,6 +1914,91 @@ testQemuMonitorJSONqemuMonitorJSONSetBlockIoThrottle(const void *opaque) return ret; } + +static int +testQemuMonitorJSONqemuMonitorJSONUpdateThrottleGroup(const void *opaque) +{ + const testGenericData *data = opaque; + virDomainXMLOption *xmlopt = data->xmlopt; + virDomainBlockIoTuneInfo info, expectedInfo; + g_autoptr(qemuMonitorTest) test = NULL; + + if (!(test = qemuMonitorTestNewSchema(xmlopt, data->schema))) + return -1; + + expectedInfo = (virDomainBlockIoTuneInfo) {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, NULL, 15, 16, 17, 18, 19, 20}; + expectedInfo.group_name = g_strdup("limit0"); + + if (qemuMonitorTestAddItem(test, "qom-get", + "{" + " \"return\": {" + " \"bps-total\": 1," + " \"bps-read\": 2," + " \"bps-write\": 3," + " \"iops-total\": 4," + " \"iops-read\": 5," + " \"iops-write\": 6," + " \"bps-total-max\": 7," + " \"bps-read-max\": 8," + " \"bps-write-max\": 9," + " \"iops-total-max\": 10," + " \"iops-read-max\": 11," + " \"iops-write-max\": 12," + " \"iops-size\": 13," + " \"bps-total-max-length\": 15," + " \"bps-read-max-length\": 16," + " \"bps-write-max-length\": 17," + " \"iops-total-max-length\": 18," + " \"iops-read-max-length\": 19," + " \"iops-write-max-length\": 20" + " }," + " \"id\": \"libvirt-12\"" + "}") < 0) + return -1; + + if (qemuMonitorTestAddItemVerbatim(test, + "{\"execute\":\"qom-set\"," + " \"arguments\":{\"property\": \"limits\"," + " \"path\": \"throttle-limit1\"," + " \"value\":{\"bps-total\": 1," + " \"bps-read\": 2," + " \"bps-write\": 3," + " \"iops-total\": 4," + " \"iops-read\": 5," + " \"iops-write\": 6," + " \"bps-total-max\": 7," + " \"bps-read-max\": 8," + " \"bps-write-max\": 9," + " \"iops-total-max\": 10," + " \"iops-read-max\": 11," + " \"iops-write-max\": 12," + " \"iops-size\": 13," + " \"bps-total-max-length\": 15," + " \"bps-read-max-length\": 16," + " \"bps-write-max-length\": 17," + " \"iops-total-max-length\": 18," + " \"iops-read-max-length\": 19," + " \"iops-write-max-length\": 20}}," + " \"id\":\"libvirt-2\"}", + NULL, + "{ \"return\" : {}}") < 0) + return -1; + + if (qemuMonitorJSONGetThrottleGroup(qemuMonitorTestGetMonitor(test), + "limit0", &info) < 0) + return -1; + + if (testValidateGetBlockIoThrottle(&info, &expectedInfo) < 0) + return -1; + + if (qemuMonitorJSONUpdateThrottleGroup(qemuMonitorTestGetMonitor(test), + "throttle-limit1", &info) < 0) + return -1; + + return 0; +} + + static int testQemuMonitorJSONqemuMonitorJSONGetTargetArch(const void *opaque) { @@ -3011,6 +3096,7 @@ mymain(void) DO_TEST(qemuMonitorJSONGetMigrationStats); DO_TEST(qemuMonitorJSONGetChardevInfo); DO_TEST(qemuMonitorJSONSetBlockIoThrottle); + DO_TEST(qemuMonitorJSONUpdateThrottleGroup); DO_TEST(qemuMonitorJSONGetTargetArch); DO_TEST(qemuMonitorJSONGetMigrationCapabilities); DO_TEST(qemuMonitorJSONQueryCPUsFast); -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:10 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Within "testQemuMonitorJSONqemuMonitorJSONUpdateThrottleGroup" * Test qemuMonitorJSONGetThrottleGroup * Test qemuMonitorJSONUpdateThrottleGroup, which updates limits through "qom-set"
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* fix test
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- tests/qemumonitorjsontest.c | 86 +++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+)
Reviewed-by: Peter Krempa <pkrempa@redhat.com>

From: Chun Feng Wu <danielwuwy@163.com> Defined new public APIs: * virDomainSetThrottleGroup to add or update throttlegroup within specific domain, it will be referenced by throttlefilter later in disk to do limits * virDomainGetThrottleGroup to get throttlegroup info, old-style is discarded (APIs to query first for the number of parameters and then give it a reasonably-sized pointer), instead, the new approach is adopted that API returns allocated array of fields and number of fileds that are in it. * virDomainDelThrottleGroup to delete throttlegroup, it fails if this throttlegroup is still referenced by some throttlefilter Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Reimplement getter API to fetch data from XML. * Apply suggested coding style changes. * Update of code documentation comments. * Update the version to 11.1.0. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- include/libvirt/libvirt-domain.h | 14 ++++ src/driver-hypervisor.h | 14 ++++ src/libvirt-domain.c | 122 ++++++++++++++++++++++++++++ src/libvirt_private.syms | 8 ++ src/libvirt_public.syms | 6 ++ src/qemu/qemu_monitor.c | 13 --- src/qemu/qemu_monitor.h | 5 -- src/remote/remote_daemon_dispatch.c | 105 ++++++++++++++++++++++++ src/remote/remote_driver.c | 3 + src/remote/remote_protocol.x | 50 +++++++++++- src/remote_protocol-structs | 28 +++++++ 11 files changed, 349 insertions(+), 19 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index f5420bca6e..a34dafc705 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -6612,4 +6612,18 @@ virDomainGraphicsReload(virDomainPtr domain, unsigned int type, unsigned int flags); + +int +virDomainSetThrottleGroup(virDomainPtr dom, + const char *group, + virTypedParameterPtr params, + int nparams, + unsigned int flags); + +int +virDomainDelThrottleGroup(virDomainPtr dom, + const char *group, + unsigned int flags); + + #endif /* LIBVIRT_DOMAIN_H */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 4ce8da078d..124b1a12e3 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1453,6 +1453,18 @@ typedef int unsigned int type, unsigned int flags); +typedef int +(*virDrvDomainSetThrottleGroup)(virDomainPtr dom, + const char *groupname, + virTypedParameterPtr params, + int nparams, + unsigned int flags); + +typedef int +(*virDrvDomainDelThrottleGroup)(virDomainPtr dom, + const char *groupname, + unsigned int flags); + typedef struct _virHypervisorDriver virHypervisorDriver; /** @@ -1726,4 +1738,6 @@ struct _virHypervisorDriver { virDrvDomainStartDirtyRateCalc domainStartDirtyRateCalc; virDrvDomainFDAssociate domainFDAssociate; virDrvDomainGraphicsReload domainGraphicsReload; + virDrvDomainSetThrottleGroup domainSetThrottleGroup; + virDrvDomainDelThrottleGroup domainDelThrottleGroup; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 072cc32255..326bbd9abb 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -14162,3 +14162,125 @@ virDomainGraphicsReload(virDomainPtr domain, virDispatchError(domain->conn); return -1; } + + +/** + * virDomainSetThrottleGroup: + * @dom: pointer to domain object + * @group: throttle group name + * @params: Pointer to blkio parameter objects + * @nparams: Number of blkio parameters (this value can be the same or + * less than the number of parameters supported) + * @flags: bitwise-OR of virDomainModificationImpact + * + * Add throttlegroup or change all of the throttlegroup options + * within specific domain + * + * The @group parameter is the name for new or existing throttlegroup, + * it cannot be NULL, detailed throttlegroup info is included in @params, + * it either creates new throttlegroup with @params or updates existing + * throttlegroup with @params, throttlegroup can be referenced by throttle + * filter in attached disk to do limits, the difference from iotune is that + * multiple throttlegroups can be referenced within attached disk + * + * Returns -1 in case of error, 0 in case of success. + * + * Since: 11.1.0 + */ +int +virDomainSetThrottleGroup(virDomainPtr dom, + const char *group, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "params=%p, group='%s', nparams=%d, flags=0x%x", + params, group, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + conn = dom->conn; + + virCheckReadOnlyGoto(conn->flags, error); + virCheckNonNullArgGoto(group, error); + virCheckPositiveArgGoto(nparams, error); + virCheckNonNullArgGoto(params, error); + + if (virTypedParameterValidateSet(dom->conn, params, nparams) < 0) + goto error; + + if (conn->driver->domainSetThrottleGroup) { + int ret; + ret = conn->driver->domainSetThrottleGroup(dom, group, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + + +/** + * virDomainDelThrottleGroup: + * @dom: pointer to domain object + * @group: throttle group name + * @flags: bitwise-OR of virDomainModificationImpact + * + * Delete an throttlegroup from the domain. @group cannot be NULL, + * and the @group to be deleted must not have a throttlefilter associated with + * it and can be any of the current valid group. + * + * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG. + * Both flags may be set. + * If VIR_DOMAIN_AFFECT_LIVE is set, the change affects a running domain + * and may fail if domain is not alive. + * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state, + * and will fail for transient domains. If neither flag is specified (that is, + * @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain modifies + * persistent setup, while an active domain is hypervisor-dependent on whether + * just live or both live and persistent state is changed. + * + * Returns -1 in case of error, 0 in case of success. + * + * Since: 11.1.0 + */ +int +virDomainDelThrottleGroup(virDomainPtr dom, + const char *group, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "group='%s', flags=0x%x", + group, flags); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + virCheckNonNullArgGoto(group, error); + + conn = dom->conn; + + if (conn->driver->domainDelThrottleGroup) { + int ret; + ret = conn->driver->domainDelThrottleGroup(dom, group, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 30a9f806f0..df7481c144 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -680,6 +680,14 @@ virDomainTaintMessageTypeFromString; virDomainTaintMessageTypeToString; virDomainTaintTypeFromString; virDomainTaintTypeToString; +virDomainThrottleFilterDefClear; +virDomainThrottleFilterFind; +virDomainThrottleGroupAdd; +virDomainThrottleGroupByName; +virDomainThrottleGroupDefCopy; +virDomainThrottleGroupDefFree; +virDomainThrottleGroupDel; +virDomainThrottleGroupUpdate; virDomainTimerModeTypeFromString; virDomainTimerModeTypeToString; virDomainTimerNameTypeFromString; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 7a3492d9d7..7448bcb1f0 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -948,4 +948,10 @@ LIBVIRT_10.2.0 { virDomainGraphicsReload; } LIBVIRT_10.1.0; +LIBVIRT_11.1.0 { + global: + virDomainSetThrottleGroup; + virDomainDelThrottleGroup; +} LIBVIRT_10.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 015f8f11cc..89669ccb15 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3058,19 +3058,6 @@ qemuMonitorUpdateThrottleGroup(qemuMonitor *mon, } -int -qemuMonitorGetThrottleGroup(qemuMonitor *mon, - const char *groupname, - virDomainBlockIoTuneInfo *reply) -{ - VIR_DEBUG("throttle-group=%s, reply=%p", groupname, reply); - - QEMU_CHECK_MONITOR(mon); - - return qemuMonitorJSONGetThrottleGroup(mon, groupname, reply); -} - - int qemuMonitorVMStatusToPausedReason(const char *status) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 7b58d988d8..c6332630bd 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1083,11 +1083,6 @@ qemuMonitorUpdateThrottleGroup(qemuMonitor *mon, const char *qomid, virDomainBlockIoTuneInfo *info); -int -qemuMonitorGetThrottleGroup(qemuMonitor *mon, - const char *groupname, - virDomainBlockIoTuneInfo *reply); - int qemuMonitorSystemWakeup(qemuMonitor *mon); int qemuMonitorGetVersion(qemuMonitor *mon, diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index e812f5c3e9..2781e70396 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -3618,6 +3618,111 @@ remoteDispatchDomainGetBlockIoTune(virNetServer *server G_GNUC_UNUSED, return rv; } +#define PARSE_THROTTLER_GROUP(val) \ + if (virXPathULongLong("string(./" #val ")", \ + ctxt, &throttleGroup->val) == -2) { \ + goto cleanup; \ + } \ + if ((virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + #val, throttleGroup->val) < 0)) { \ + goto cleanup; \ + } + +static int +remoteDispatchDomainGetThrottleGroup(virNetServer *server G_GNUC_UNUSED, + virNetServerClient *client, + virNetMessage *hdr G_GNUC_UNUSED, + struct virNetMessageError *rerr, + remote_domain_get_throttle_group_args *args, + remote_domain_get_throttle_group_ret *ret) +{ + virDomainPtr dom = NULL; + int rv = -1; + g_autofree char *doc = NULL; + g_autoptr(xmlDoc) xml = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *node = NULL; + int n = 0; + int maxparams = 0; + size_t i; + + virTypedParameterPtr params = NULL; + int nparams = 0; + virConnectPtr conn = remoteGetHypervisorConn(client); + + if (!conn) + goto cleanup; + + if (!(dom = get_nonnull_domain(conn, args->dom))) + goto cleanup; + virCheckNonNullArgGoto(*args->group, cleanup); + + doc = virDomainGetXMLDesc(dom, args->flags); + + if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt))) { + return false; + } + + if ((n = virXPathNodeSet("/domain/throttlegroups/throttlegroup", ctxt, &node)) < 0) + goto cleanup; + + if (n == 0) + return 0; + + for (i = 0; i < n; i++) { + g_autoptr(virDomainThrottleGroupDef) throttleGroup = g_new0(virDomainThrottleGroupDef, 1); + + VIR_XPATH_NODE_AUTORESTORE(ctxt) + ctxt->node = node[i]; + + if (STREQ(*args->group, virXPathString("string(./group_name)", ctxt)) == 0) { + continue; + } + + PARSE_THROTTLER_GROUP(total_bytes_sec); + PARSE_THROTTLER_GROUP(read_bytes_sec); + PARSE_THROTTLER_GROUP(write_bytes_sec); + PARSE_THROTTLER_GROUP(total_iops_sec); + PARSE_THROTTLER_GROUP(read_iops_sec); + PARSE_THROTTLER_GROUP(write_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max); + PARSE_THROTTLER_GROUP(read_bytes_sec_max); + PARSE_THROTTLER_GROUP(write_bytes_sec_max); + PARSE_THROTTLER_GROUP(total_iops_sec_max); + PARSE_THROTTLER_GROUP(read_iops_sec_max); + PARSE_THROTTLER_GROUP(write_iops_sec_max); + + PARSE_THROTTLER_GROUP(size_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(read_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(write_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(total_iops_sec_max_length); + PARSE_THROTTLER_GROUP(read_iops_sec_max_length); + PARSE_THROTTLER_GROUP(write_iops_sec_max_length); + } + + /* Serialize the ThrottleGroup parameters. */ + if (virTypedParamsSerialize(params, nparams, + REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX, + (struct _virTypedParameterRemote **) &ret->params.params_val, + &ret->params.params_len, + args->flags) < 0) + goto cleanup; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virTypedParamsFree(params, nparams); + virObjectUnref(dom); + return rv; +} +#undef PARSE_THROTTLEGROUP + + /*-------------------------------------------------------------*/ static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 307f9ca945..a76212a22b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2582,6 +2582,7 @@ static int remoteDomainGetBlockIoTune(virDomainPtr domain, return 0; } + static int remoteDomainGetCPUStats(virDomainPtr domain, virTypedParameterPtr params, unsigned int nparams, @@ -7835,6 +7836,8 @@ static virHypervisorDriver hypervisor_driver = { .domainSetLaunchSecurityState = remoteDomainSetLaunchSecurityState, /* 8.0.0 */ .domainFDAssociate = remoteDomainFDAssociate, /* 9.0.0 */ .domainGraphicsReload = remoteDomainGraphicsReload, /* 10.2.0 */ + .domainSetThrottleGroup = remoteDomainSetThrottleGroup, /* 11.1.0 */ + .domainDelThrottleGroup = remoteDomainDelThrottleGroup, /* 11.1.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 41c045ff78..1d3e8a7a8c 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -113,6 +113,9 @@ const REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX = 16; /* Upper limit on list of blockio tuning parameters. */ const REMOTE_DOMAIN_BLOCK_IO_TUNE_PARAMETERS_MAX = 32; +/* Upper limit on list of throttle group parameters. */ +const REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX = 32; + /* Upper limit on list of numa parameters. */ const REMOTE_DOMAIN_NUMA_PARAMETERS_MAX = 16; @@ -1475,6 +1478,29 @@ struct remote_domain_get_block_io_tune_ret { int nparams; }; +struct remote_domain_set_throttle_group_args { + remote_nonnull_domain dom; + remote_nonnull_string group; + remote_typed_param params<REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX>; + unsigned int flags; +}; + +struct remote_domain_get_throttle_group_args { + remote_nonnull_domain dom; + remote_string group; + unsigned int flags; +}; + +struct remote_domain_get_throttle_group_ret { + remote_typed_param params<REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX>; +}; + +struct remote_domain_del_throttle_group_args { + remote_nonnull_domain dom; + remote_string group; + unsigned int flags; +}; + struct remote_domain_get_cpu_stats_args { remote_nonnull_domain dom; unsigned int nparams; @@ -7048,5 +7074,27 @@ enum remote_procedure { * @generate: both * @acl: domain:write */ - REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448 + REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448, + + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE + * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG + */ + REMOTE_PROC_DOMAIN_SET_THROTTLE_GROUP = 449, + + /** + * @generate: none + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_GET_THROTTLE_GROUP = 450, + + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE + * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG + */ + REMOTE_PROC_DOMAIN_DEL_THROTTLE_GROUP = 451 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 4d3dc2d249..a94e29f9c9 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1050,6 +1050,31 @@ struct remote_domain_get_block_io_tune_ret { } params; int nparams; }; +struct remote_domain_set_throttle_group_args { + remote_nonnull_domain dom; + remote_nonnull_string group; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + u_int flags; +}; +struct remote_domain_get_throttle_group_args { + remote_nonnull_domain dom; + remote_string group; + u_int flags; +}; +struct remote_domain_get_throttle_group_ret { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; +}; +struct remote_domain_del_throttle_group_args { + remote_nonnull_domain dom; + remote_string group; + u_int flags; +}; struct remote_domain_get_cpu_stats_args { remote_nonnull_domain dom; u_int nparams; @@ -3755,4 +3780,7 @@ enum remote_procedure { REMOTE_PROC_NETWORK_EVENT_CALLBACK_METADATA_CHANGE = 446, REMOTE_PROC_NODE_DEVICE_UPDATE = 447, REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448, + REMOTE_PROC_DOMAIN_SET_THROTTLE_GROUP = 449, + REMOTE_PROC_DOMAIN_GET_THROTTLE_GROUP = 450, + REMOTE_PROC_DOMAIN_DEL_THROTTLE_GROUP = 451, }; -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:11 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Defined new public APIs: * virDomainSetThrottleGroup to add or update throttlegroup within specific domain, it will be referenced by throttlefilter later in disk to do limits * virDomainGetThrottleGroup to get throttlegroup info, old-style is discarded (APIs to query first for the number of parameters and then give it a reasonably-sized pointer), instead, the new approach is adopted that API returns allocated array of fields and number of fileds that are in it. * virDomainDelThrottleGroup to delete throttlegroup, it fails if this throttlegroup is still referenced by some throttlefilter
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Reimplement getter API to fetch data from XML. * Apply suggested coding style changes. * Update of code documentation comments. * Update the version to 11.1.0.
I'll update all including this to 11.2.0
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- include/libvirt/libvirt-domain.h | 14 ++++ src/driver-hypervisor.h | 14 ++++ src/libvirt-domain.c | 122 ++++++++++++++++++++++++++++ src/libvirt_private.syms | 8 ++ src/libvirt_public.syms | 6 ++ src/qemu/qemu_monitor.c | 13 --- src/qemu/qemu_monitor.h | 5 -- src/remote/remote_daemon_dispatch.c | 105 ++++++++++++++++++++++++ src/remote/remote_driver.c | 3 + src/remote/remote_protocol.x | 50 +++++++++++- src/remote_protocol-structs | 28 +++++++ 11 files changed, 349 insertions(+), 19 deletions(-)
[...]
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 072cc32255..326bbd9abb 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -14162,3 +14162,125 @@ virDomainGraphicsReload(virDomainPtr domain, virDispatchError(domain->conn); return -1; } + + +/** + * virDomainSetThrottleGroup: + * @dom: pointer to domain object + * @group: throttle group name + * @params: Pointer to blkio parameter objects + * @nparams: Number of blkio parameters (this value can be the same or + * less than the number of parameters supported) + * @flags: bitwise-OR of virDomainModificationImpact + * + * Add throttlegroup or change all of the throttlegroup options + * within specific domain + * + * The @group parameter is the name for new or existing throttlegroup, + * it cannot be NULL, detailed throttlegroup info is included in @params, + * it either creates new throttlegroup with @params or updates existing + * throttlegroup with @params, throttlegroup can be referenced by throttle + * filter in attached disk to do limits, the difference from iotune is that + * multiple throttlegroups can be referenced within attached disk + * + * Returns -1 in case of error, 0 in case of success. + * + * Since: 11.1.0
^^^
+ */ +int +virDomainSetThrottleGroup(virDomainPtr dom, + const char *group, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "params=%p, group='%s', nparams=%d, flags=0x%x", + params, group, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + conn = dom->conn; + + virCheckReadOnlyGoto(conn->flags, error); + virCheckNonNullArgGoto(group, error); + virCheckPositiveArgGoto(nparams, error); + virCheckNonNullArgGoto(params, error); + + if (virTypedParameterValidateSet(dom->conn, params, nparams) < 0) + goto error; + + if (conn->driver->domainSetThrottleGroup) { + int ret; + ret = conn->driver->domainSetThrottleGroup(dom, group, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + + +/** + * virDomainDelThrottleGroup: + * @dom: pointer to domain object + * @group: throttle group name + * @flags: bitwise-OR of virDomainModificationImpact + * + * Delete an throttlegroup from the domain. @group cannot be NULL, + * and the @group to be deleted must not have a throttlefilter associated with + * it and can be any of the current valid group. + * + * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG. + * Both flags may be set. + * If VIR_DOMAIN_AFFECT_LIVE is set, the change affects a running domain + * and may fail if domain is not alive. + * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state, + * and will fail for transient domains. If neither flag is specified (that is, + * @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain modifies + * persistent setup, while an active domain is hypervisor-dependent on whether + * just live or both live and persistent state is changed. + * + * Returns -1 in case of error, 0 in case of success. + * + * Since: 11.1.0
^^^
+ */ +int +virDomainDelThrottleGroup(virDomainPtr dom, + const char *group, + unsigned int flags) +{
[...]
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 7a3492d9d7..7448bcb1f0 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -948,4 +948,10 @@ LIBVIRT_10.2.0 { virDomainGraphicsReload; } LIBVIRT_10.1.0;
+LIBVIRT_11.1.0 {
^^^
+ global: + virDomainSetThrottleGroup; + virDomainDelThrottleGroup; +} LIBVIRT_10.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 015f8f11cc..89669ccb15 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3058,19 +3058,6 @@ qemuMonitorUpdateThrottleGroup(qemuMonitor *mon, }
-int -qemuMonitorGetThrottleGroup(qemuMonitor *mon, - const char *groupname, - virDomainBlockIoTuneInfo *reply) -{ - VIR_DEBUG("throttle-group=%s, reply=%p", groupname, reply); - - QEMU_CHECK_MONITOR(mon); - - return qemuMonitorJSONGetThrottleGroup(mon, groupname, reply); -} - - int qemuMonitorVMStatusToPausedReason(const char *status) {
This definitely doesn't belong to this patch.
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 7b58d988d8..c6332630bd 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1083,11 +1083,6 @@ qemuMonitorUpdateThrottleGroup(qemuMonitor *mon, const char *qomid, virDomainBlockIoTuneInfo *info);
-int -qemuMonitorGetThrottleGroup(qemuMonitor *mon, - const char *groupname, - virDomainBlockIoTuneInfo *reply); - int qemuMonitorSystemWakeup(qemuMonitor *mon);
Same here.
int qemuMonitorGetVersion(qemuMonitor *mon, diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index e812f5c3e9..2781e70396 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -3618,6 +3618,111 @@ remoteDispatchDomainGetBlockIoTune(virNetServer *server G_GNUC_UNUSED, return rv; }
+#define PARSE_THROTTLER_GROUP(val) \ + if (virXPathULongLong("string(./" #val ")", \ + ctxt, &throttleGroup->val) == -2) { \ + goto cleanup; \ + } \ + if ((virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + #val, throttleGroup->val) < 0)) { \ + goto cleanup; \ + } + +static int +remoteDispatchDomainGetThrottleGroup(virNetServer *server G_GNUC_UNUSED, + virNetServerClient *client, + virNetMessage *hdr G_GNUC_UNUSED, + struct virNetMessageError *rerr, + remote_domain_get_throttle_group_args *args, + remote_domain_get_throttle_group_ret *ret)
The getter doesn't exist so this doesn't make sense either.
+{ + virDomainPtr dom = NULL; + int rv = -1; + g_autofree char *doc = NULL; + g_autoptr(xmlDoc) xml = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *node = NULL; + int n = 0; + int maxparams = 0; + size_t i; + + virTypedParameterPtr params = NULL; + int nparams = 0; + virConnectPtr conn = remoteGetHypervisorConn(client); + + if (!conn) + goto cleanup; + + if (!(dom = get_nonnull_domain(conn, args->dom))) + goto cleanup; + virCheckNonNullArgGoto(*args->group, cleanup); + + doc = virDomainGetXMLDesc(dom, args->flags); + + if (!(xml = virXMLParseStringCtxt(doc, _("(domain_definition)"), &ctxt))) { + return false; + } + + if ((n = virXPathNodeSet("/domain/throttlegroups/throttlegroup", ctxt, &node)) < 0) + goto cleanup; + + if (n == 0) + return 0; + + for (i = 0; i < n; i++) { + g_autoptr(virDomainThrottleGroupDef) throttleGroup = g_new0(virDomainThrottleGroupDef, 1); + + VIR_XPATH_NODE_AUTORESTORE(ctxt) + ctxt->node = node[i]; + + if (STREQ(*args->group, virXPathString("string(./group_name)", ctxt)) == 0) { + continue; + } + + PARSE_THROTTLER_GROUP(total_bytes_sec); + PARSE_THROTTLER_GROUP(read_bytes_sec); + PARSE_THROTTLER_GROUP(write_bytes_sec); + PARSE_THROTTLER_GROUP(total_iops_sec); + PARSE_THROTTLER_GROUP(read_iops_sec); + PARSE_THROTTLER_GROUP(write_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max); + PARSE_THROTTLER_GROUP(read_bytes_sec_max); + PARSE_THROTTLER_GROUP(write_bytes_sec_max); + PARSE_THROTTLER_GROUP(total_iops_sec_max); + PARSE_THROTTLER_GROUP(read_iops_sec_max); + PARSE_THROTTLER_GROUP(write_iops_sec_max); + + PARSE_THROTTLER_GROUP(size_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(read_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(write_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(total_iops_sec_max_length); + PARSE_THROTTLER_GROUP(read_iops_sec_max_length); + PARSE_THROTTLER_GROUP(write_iops_sec_max_length); + } + + /* Serialize the ThrottleGroup parameters. */ + if (virTypedParamsSerialize(params, nparams, + REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX, + (struct _virTypedParameterRemote **) &ret->params.params_val, + &ret->params.params_len, + args->flags) < 0) + goto cleanup; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virTypedParamsFree(params, nparams); + virObjectUnref(dom); + return rv; +} +#undef PARSE_THROTTLEGROUP + + /*-------------------------------------------------------------*/
static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 307f9ca945..a76212a22b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2582,6 +2582,7 @@ static int remoteDomainGetBlockIoTune(virDomainPtr domain, return 0; }
+ static int remoteDomainGetCPUStats(virDomainPtr domain,
Spurious newline.
virTypedParameterPtr params, unsigned int nparams, @@ -7835,6 +7836,8 @@ static virHypervisorDriver hypervisor_driver = { .domainSetLaunchSecurityState = remoteDomainSetLaunchSecurityState, /* 8.0.0 */ .domainFDAssociate = remoteDomainFDAssociate, /* 9.0.0 */ .domainGraphicsReload = remoteDomainGraphicsReload, /* 10.2.0 */ + .domainSetThrottleGroup = remoteDomainSetThrottleGroup, /* 11.1.0 */ + .domainDelThrottleGroup = remoteDomainDelThrottleGroup, /* 11.1.0 */ };
static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 41c045ff78..1d3e8a7a8c 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -113,6 +113,9 @@ const REMOTE_DOMAIN_MEMORY_PARAMETERS_MAX = 16; /* Upper limit on list of blockio tuning parameters. */ const REMOTE_DOMAIN_BLOCK_IO_TUNE_PARAMETERS_MAX = 32;
+/* Upper limit on list of throttle group parameters. */ +const REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX = 32;
We should be able to reuse the former.
+ /* Upper limit on list of numa parameters. */ const REMOTE_DOMAIN_NUMA_PARAMETERS_MAX = 16;
@@ -1475,6 +1478,29 @@ struct remote_domain_get_block_io_tune_ret { int nparams; };
+struct remote_domain_set_throttle_group_args { + remote_nonnull_domain dom; + remote_nonnull_string group; + remote_typed_param params<REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX>; + unsigned int flags; +}; + +struct remote_domain_get_throttle_group_args { + remote_nonnull_domain dom; + remote_string group; + unsigned int flags; +}; + +struct remote_domain_get_throttle_group_ret { + remote_typed_param params<REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX>; +};
Getter doesn't exist yet you declare RPC for it.
+ +struct remote_domain_del_throttle_group_args { + remote_nonnull_domain dom; + remote_string group; + unsigned int flags; +}; + struct remote_domain_get_cpu_stats_args { remote_nonnull_domain dom; unsigned int nparams; @@ -7048,5 +7074,27 @@ enum remote_procedure { * @generate: both * @acl: domain:write */ - REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448 + REMOTE_PROC_DOMAIN_GRAPHICS_RELOAD = 448, + + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE + * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG + */ + REMOTE_PROC_DOMAIN_SET_THROTTLE_GROUP = 449, + + /** + * @generate: none + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_GET_THROTTLE_GROUP = 450,
... and here too.
+ + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE + * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG + */ + REMOTE_PROC_DOMAIN_DEL_THROTTLE_GROUP = 451 };

On Wed, Feb 19, 2025 at 22:27:11 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Defined new public APIs: * virDomainSetThrottleGroup to add or update throttlegroup within specific domain, it will be referenced by throttlefilter later in disk to do limits * virDomainGetThrottleGroup to get throttlegroup info, old-style is discarded (APIs to query first for the number of parameters and then give it a reasonably-sized pointer), instead, the new approach is adopted that API returns allocated array of fields and number of fileds that are in it. * virDomainDelThrottleGroup to delete throttlegroup, it fails if this throttlegroup is still referenced by some throttlefilter
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Reimplement getter API to fetch data from XML. * Apply suggested coding style changes. * Update of code documentation comments. * Update the version to 11.1.0.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> ---
[...]
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 30a9f806f0..df7481c144 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -680,6 +680,14 @@ virDomainTaintMessageTypeFromString; virDomainTaintMessageTypeToString; virDomainTaintTypeFromString; virDomainTaintTypeToString; +virDomainThrottleFilterDefClear; +virDomainThrottleFilterFind; +virDomainThrottleGroupAdd; +virDomainThrottleGroupByName; +virDomainThrottleGroupDefCopy; +virDomainThrottleGroupDefFree; +virDomainThrottleGroupDel; +virDomainThrottleGroupUpdate; virDomainTimerModeTypeFromString; virDomainTimerModeTypeToString; virDomainTimerNameTypeFromString;
This also doesn't belong into this patch.

From: Chun Feng Wu <danielwuwy@163.com> extract common methods from "qemuDomainSetBlockIoTune" to be reused by throttle handling later, common methods include: * "qemuDomainValidateBlockIoTune", which is to validate that PARAMS contains only recognized parameter names with correct types * "qemuDomainSetBlockIoTuneFields", which is to load parameters into internal object virDomainBlockIoTuneInfo * "qemuDomainCheckBlockIoTuneMutualExclusion", which is to check rules like "total and read/write of bytes_sec cannot be set at the same time" * "qemuDomainCheckBlockIoTuneMax", which is to check "max" rules within iotune Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Apply suggested coding style changes. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_driver.c | 229 +++++++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 88 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1d0da1028f..31c543175f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14988,35 +14988,8 @@ qemuDomainCheckBlockIoTuneReset(virDomainDiskDef *disk, static int -qemuDomainSetBlockIoTune(virDomainPtr dom, - const char *path, - virTypedParameterPtr params, - int nparams, - unsigned int flags) +qemuDomainValidateBlockIoTune(virTypedParameterPtr params, int nparams) { - virQEMUDriver *driver = dom->conn->privateData; - virDomainObj *vm = NULL; - qemuDomainObjPrivate *priv; - virDomainDef *def = NULL; - virDomainDef *persistentDef = NULL; - virDomainBlockIoTuneInfo info = { 0 }; - virDomainBlockIoTuneInfo conf_info = { 0 }; - int ret = -1; - size_t i; - virDomainDiskDef *conf_disk = NULL; - virDomainDiskDef *disk; - qemuBlockIoTuneSetFlags set_fields = 0; - g_autoptr(virQEMUDriverConfig) cfg = NULL; - virObjectEvent *event = NULL; - virTypedParameterPtr eventParams = NULL; - int eventNparams = 0; - int eventMaxparams = 0; - virDomainBlockIoTuneInfo *cur_info; - virDomainBlockIoTuneInfo *conf_cur_info; - - - virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | - VIR_DOMAIN_AFFECT_CONFIG, -1); if (virTypedParamsValidate(params, nparams, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC, VIR_TYPED_PARAM_ULLONG, @@ -15061,35 +15034,30 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, NULL) < 0) return -1; - if (!(vm = qemuDomainObjFromDomain(dom))) - return -1; - - if (virDomainSetBlockIoTuneEnsureACL(dom->conn, vm->def, flags) < 0) - goto cleanup; - - cfg = virQEMUDriverGetConfig(driver); - - if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) - goto cleanup; + return 0; +} - priv = vm->privateData; - if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) - goto endjob; - - if (virTypedParamsAddString(&eventParams, &eventNparams, &eventMaxparams, - VIR_DOMAIN_TUNABLE_BLKDEV_DISK, path) < 0) - goto endjob; +static int +qemuDomainSetBlockIoTuneFields(virDomainBlockIoTuneInfo *info, + virTypedParameterPtr params, + int nparams, + qemuBlockIoTuneSetFlags *set_fields, + virTypedParameterPtr *eventParams, + int *eventNparams, + int *eventMaxparams) +{ + size_t i; #define SET_IOTUNE_FIELD(FIELD, BOOL, CONST) \ if (STREQ(param->field, VIR_DOMAIN_BLOCK_IOTUNE_##CONST)) { \ - info.FIELD = param->value.ul; \ - set_fields |= QEMU_BLOCK_IOTUNE_SET_##BOOL; \ - if (virTypedParamsAddULLong(&eventParams, &eventNparams, \ - &eventMaxparams, \ + info->FIELD = param->value.ul; \ + *set_fields |= QEMU_BLOCK_IOTUNE_SET_##BOOL; \ + if (virTypedParamsAddULLong(eventParams, eventNparams, \ + eventMaxparams, \ VIR_DOMAIN_TUNABLE_BLKDEV_##CONST, \ param->value.ul) < 0) \ - goto endjob; \ + return -1; \ continue; \ } @@ -15100,7 +15068,7 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("block I/O throttle limit value must be no more than %1$llu"), QEMU_BLOCK_IOTUNE_MAX); - goto endjob; + return -1; } SET_IOTUNE_FIELD(total_bytes_sec, BYTES, TOTAL_BYTES_SEC); @@ -15126,13 +15094,13 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, /* NB: Cannot use macro since this is a value.s not a value.ul */ if (STREQ(param->field, VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME)) { - info.group_name = g_strdup(param->value.s); - set_fields |= QEMU_BLOCK_IOTUNE_SET_GROUP_NAME; - if (virTypedParamsAddString(&eventParams, &eventNparams, - &eventMaxparams, + info->group_name = g_strdup(param->value.s); + *set_fields |= QEMU_BLOCK_IOTUNE_SET_GROUP_NAME; + if (virTypedParamsAddString(eventParams, eventNparams, + eventMaxparams, VIR_DOMAIN_TUNABLE_BLKDEV_GROUP_NAME, param->value.s) < 0) - goto endjob; + return -1; continue; } @@ -15152,56 +15120,53 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, #undef SET_IOTUNE_FIELD - if ((info.total_bytes_sec && info.read_bytes_sec) || - (info.total_bytes_sec && info.write_bytes_sec)) { + return 0; +} + + +static int +qemuDomainCheckBlockIoTuneMutualExclusion(virDomainBlockIoTuneInfo *info) +{ + if ((info->total_bytes_sec && info->read_bytes_sec) || + (info->total_bytes_sec && info->write_bytes_sec)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of bytes_sec cannot be set at the same time")); - goto endjob; + return -1; } - if ((info.total_iops_sec && info.read_iops_sec) || - (info.total_iops_sec && info.write_iops_sec)) { + if ((info->total_iops_sec && info->read_iops_sec) || + (info->total_iops_sec && info->write_iops_sec)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of iops_sec cannot be set at the same time")); - goto endjob; + return -1; } - if ((info.total_bytes_sec_max && info.read_bytes_sec_max) || - (info.total_bytes_sec_max && info.write_bytes_sec_max)) { + if ((info->total_bytes_sec_max && info->read_bytes_sec_max) || + (info->total_bytes_sec_max && info->write_bytes_sec_max)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of bytes_sec_max cannot be set at the same time")); - goto endjob; + return -1; } - if ((info.total_iops_sec_max && info.read_iops_sec_max) || - (info.total_iops_sec_max && info.write_iops_sec_max)) { + if ((info->total_iops_sec_max && info->read_iops_sec_max) || + (info->total_iops_sec_max && info->write_iops_sec_max)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of iops_sec_max cannot be set at the same time")); - goto endjob; + return -1; } - virDomainBlockIoTuneInfoCopy(&info, &conf_info); - - if (def) { - if (!(disk = qemuDomainDiskByName(def, path))) - goto endjob; - - if (!qemuDomainDiskBlockIoTuneIsSupported(disk)) - goto endjob; - - cur_info = qemuDomainFindGroupBlockIoTune(def, disk, &info); + return 0; +} - if (qemuDomainSetBlockIoTuneDefaults(&info, cur_info, - set_fields) < 0) - goto endjob; - if (qemuDomainCheckBlockIoTuneReset(disk, &info) < 0) - goto endjob; +static int +qemuDomainCheckBlockIoTuneMax(virDomainBlockIoTuneInfo *info) +{ #define CHECK_MAX(val, _bool) \ do { \ - if (info.val##_max) { \ - if (!info.val) { \ + if (info->val##_max) { \ + if (!info->val) { \ if (QEMU_BLOCK_IOTUNE_SET_##_bool) { \ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ _("cannot reset '%1$s' when '%2$s' is set"), \ @@ -15211,13 +15176,13 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, _("value '%1$s' cannot be set if '%2$s' is not set"), \ #val "_max", #val); \ } \ - goto endjob; \ + return -1; \ } \ - if (info.val##_max < info.val) { \ + if (info->val##_max < info->val) { \ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ _("value '%1$s' cannot be smaller than '%2$s'"), \ #val "_max", #val); \ - goto endjob; \ + return -1; \ } \ } \ } while (false) @@ -15231,6 +15196,94 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, #undef CHECK_MAX + return 0; +} + + +static int +qemuDomainSetBlockIoTune(virDomainPtr dom, + const char *path, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virQEMUDriver *driver = dom->conn->privateData; + virDomainObj *vm = NULL; + qemuDomainObjPrivate *priv; + virDomainDef *def = NULL; + virDomainDef *persistentDef = NULL; + virDomainBlockIoTuneInfo info = { 0 }; + virDomainBlockIoTuneInfo conf_info = { 0 }; + int ret = -1; + virDomainDiskDef *conf_disk = NULL; + virDomainDiskDef *disk; + qemuBlockIoTuneSetFlags set_fields = 0; + g_autoptr(virQEMUDriverConfig) cfg = NULL; + virObjectEvent *event = NULL; + virTypedParameterPtr eventParams = NULL; + int eventNparams = 0; + int eventMaxparams = 0; + virDomainBlockIoTuneInfo *cur_info; + virDomainBlockIoTuneInfo *conf_cur_info; + + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + if (qemuDomainValidateBlockIoTune(params, nparams) < 0) + return -1; + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainSetBlockIoTuneEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + cfg = virQEMUDriverGetConfig(driver); + + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) + goto cleanup; + + priv = vm->privateData; + + if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) + goto endjob; + + if (virTypedParamsAddString(&eventParams, &eventNparams, &eventMaxparams, + VIR_DOMAIN_TUNABLE_BLKDEV_DISK, path) < 0) + goto endjob; + + if (qemuDomainSetBlockIoTuneFields(&info, + params, + nparams, + &set_fields, + &eventParams, + &eventNparams, + &eventMaxparams) < 0) + goto endjob; + + if (qemuDomainCheckBlockIoTuneMutualExclusion(&info) < 0) + goto endjob; + + virDomainBlockIoTuneInfoCopy(&info, &conf_info); + + if (def) { + if (!(disk = qemuDomainDiskByName(def, path))) + goto endjob; + + if (!qemuDomainDiskBlockIoTuneIsSupported(disk)) + goto endjob; + + cur_info = qemuDomainFindGroupBlockIoTune(def, disk, &info); + + if (qemuDomainSetBlockIoTuneDefaults(&info, cur_info, set_fields) < 0) + goto endjob; + + if (qemuDomainCheckBlockIoTuneReset(disk, &info) < 0) + goto endjob; + + if (qemuDomainCheckBlockIoTuneMax(&info) < 0) + goto endjob; + /* blockdev-based qemu doesn't want to set the throttling when a cdrom * is empty. Skip the monitor call here since we will set the throttling * once new media is inserted */ -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:12 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
extract common methods from "qemuDomainSetBlockIoTune" to be reused by throttle handling later, common methods include: * "qemuDomainValidateBlockIoTune", which is to validate that PARAMS contains only recognized parameter names with correct types * "qemuDomainSetBlockIoTuneFields", which is to load parameters into internal object virDomainBlockIoTuneInfo * "qemuDomainCheckBlockIoTuneMutualExclusion", which is to check rules like "total and read/write of bytes_sec cannot be set at the same time" * "qemuDomainCheckBlockIoTuneMax", which is to check "max" rules within iotune
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Apply suggested coding style changes.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_driver.c | 229 +++++++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 88 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1d0da1028f..31c543175f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14988,35 +14988,8 @@ qemuDomainCheckBlockIoTuneReset(virDomainDiskDef *disk,
static int -qemuDomainSetBlockIoTune(virDomainPtr dom, - const char *path, - virTypedParameterPtr params, - int nparams, - unsigned int flags) +qemuDomainValidateBlockIoTune(virTypedParameterPtr params, int nparams)
With formatting of this fixed Reviewed-by: Peter Krempa <pkrempa@redhat.com>

From: Chun Feng Wu <danielwuwy@163.com> ThrottleGroup lifecycle implementation, note, in QOM, throttlegroup name is prefixed with "throttle-" to clearly separate throttle group objects into their own namespace. * "qemuDomainSetThrottleGroup", this method is to add("object-add") or update("qom-set") throttlegroup in QOM and update corresponding objects in DOM * "qemuDomainGetThrottleGroup", this method queries throttlegroup info by groupname * "qemuDomainDelThrottleGroup", this method checks if group is referenced by any throttle in disks and delete it if it's not used anymore * Check flag "QEMU_CAPS_OBJECT_JSON" during qemuDomainSetThrottleGroup when vm is active, throttle group feature requries such flag * "objectAddNoWrap"("props") check is done by reusing qemuMonitorAddObject in qemuDomainSetThrottleGroup Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Apply suggested coding style changes. * cleanup qemu Get ThrottleGroup. * Update the version to 11.1.0. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_driver.c | 243 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 31c543175f..afb41a4c89 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20133,6 +20133,246 @@ qemuDomainGraphicsReload(virDomainPtr domain, return ret; } + +static int +qemuDomainSetThrottleGroup(virDomainPtr dom, + const char *groupname, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virQEMUDriver *driver = dom->conn->privateData; + virDomainObj *vm = NULL; + virDomainDef *def = NULL; + virDomainDef *persistentDef = NULL; + virDomainThrottleGroupDef info = { 0 }; + virDomainThrottleGroupDef conf_info = { 0 }; + int ret = -1; + qemuBlockIoTuneSetFlags set_fields = 0; + g_autoptr(virQEMUDriverConfig) cfg = NULL; + virObjectEvent *event = NULL; + virTypedParameterPtr eventParams = NULL; + int eventNparams = 0; + int eventMaxparams = 0; + virDomainThrottleGroupDef *cur_info; + virDomainThrottleGroupDef *conf_cur_info; + int rc = 0; + g_autoptr(virJSONValue) props = NULL; + g_autoptr(virJSONValue) limits = virJSONValueNewObject(); + qemuDomainObjPrivate *priv = NULL; + virQEMUCaps *qemuCaps = NULL; + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name = g_strdup_printf("throttle-%s", groupname); + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (qemuDomainValidateBlockIoTune(params, nparams) < 0) + return -1; + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainSetThrottleGroupEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + cfg = virQEMUDriverGetConfig(driver); + + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) + goto endjob; + + if (virTypedParamsAddString(&eventParams, &eventNparams, &eventMaxparams, + VIR_DOMAIN_TUNABLE_BLKDEV_GROUP_NAME, groupname) < 0) + goto endjob; + + if (qemuDomainSetBlockIoTuneFields(&info, + params, + nparams, + &set_fields, + &eventParams, + &eventNparams, + &eventMaxparams) < 0) + goto endjob; + + if (qemuDomainCheckBlockIoTuneMutualExclusion(&info) < 0) + goto endjob; + + virDomainThrottleGroupDefCopy(&info, &conf_info); + + priv = vm->privateData; + qemuCaps = priv->qemuCaps; + if (virDomainObjIsActive(vm)) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_JSON)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("This QEMU doesn't support throttle group creation")); + return -1; + } + } + + if (def) { + if (qemuDomainCheckBlockIoTuneMax(&info) < 0) + goto endjob; + + cur_info = virDomainThrottleGroupByName(def, groupname); + /* Update existing group. */ + if (cur_info != NULL) { + if (qemuDomainSetBlockIoTuneDefaults(&info, cur_info, set_fields) < 0) + goto endjob; + qemuDomainObjEnterMonitor(vm); + rc = qemuMonitorUpdateThrottleGroup(qemuDomainGetMonitor(vm), + prefixed_group_name, + &info); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto endjob; + virDomainThrottleGroupUpdate(def, &info); + } else { + if (qemuMonitorThrottleGroupLimits(limits, &info)<0) + goto endjob; + if (qemuMonitorCreateObjectProps(&props, + "throttle-group", prefixed_group_name, + "a:limits", &limits, + NULL) < 0) + goto endjob; + qemuDomainObjEnterMonitor(vm); + rc = qemuMonitorAddObject(qemuDomainGetMonitor(vm), &props, NULL); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto endjob; + virDomainThrottleGroupAdd(def, &info); + } + + qemuDomainSaveStatus(vm); + + if (eventNparams) { + event = virDomainEventTunableNewFromDom(dom, &eventParams, eventNparams); + virObjectEventStateQueue(driver->domainEventState, event); + } + } + + if (persistentDef) { + conf_cur_info = virDomainThrottleGroupByName(persistentDef, groupname); + + if (conf_cur_info != NULL) { + if (qemuDomainSetBlockIoTuneDefaults(&conf_info, conf_cur_info, + set_fields) < 0) + goto endjob; + virDomainThrottleGroupUpdate(persistentDef, &conf_info); + } else { + virDomainThrottleGroupAdd(persistentDef, &conf_info); + } + + + if (virDomainDefSave(persistentDef, driver->xmlopt, + cfg->configDir) < 0) + goto endjob; + } + + ret = 0; + endjob: + virDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + virTypedParamsFree(eventParams, eventNparams); + return ret; +} + + +static int +qemuDomainCheckThrottleGroupRef(virDomainDef *def, + const char *group_name) +{ + size_t i; + for (i = 0; i < def->ndisks; i++) { + virDomainDiskDef *disk = def->disks[i]; + if (virDomainThrottleFilterFind(disk, group_name)) { + virReportError(VIR_ERR_INVALID_ARG, + _("throttle group '%1$s' is still being used by disk %2$s"), + group_name, disk->dst); + return -1; + } + } + return 0; +} + + +static int +qemuDomainDelThrottleGroup(virDomainPtr dom, + const char *groupname, + unsigned int flags) +{ + virQEMUDriver *driver = dom->conn->privateData; + virDomainObj *vm = NULL; + virDomainDef *def = NULL; + virDomainDef *persistentDef = NULL; + g_autoptr(virQEMUDriverConfig) cfg = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainDelThrottleGroupEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) + goto endjob; + + /* check if this group is still being used by disks, catch errors upfront and + * avoid a partial success with error reported */ + if (def) { + if (qemuDomainCheckThrottleGroupRef(def, groupname) < 0) + goto endjob; + } + if (persistentDef) { + if (qemuDomainCheckThrottleGroupRef(persistentDef, groupname) < 0) + goto endjob; + } + + if (def) { + int rc = 0; + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name = g_strdup_printf("throttle-%s", groupname); + + qemuDomainObjEnterMonitor(vm); + rc = qemuMonitorDelObject(qemuDomainGetMonitor(vm), prefixed_group_name, true); + qemuDomainObjExitMonitor(vm); + + if (rc < 0) + goto endjob; + + virDomainThrottleGroupDel(def, groupname); + qemuDomainSaveStatus(vm); + } + + if (persistentDef) { + cfg = virQEMUDriverGetConfig(driver); + virDomainThrottleGroupDel(persistentDef, groupname); + if (virDomainDefSave(persistentDef, driver->xmlopt, + cfg->configDir) < 0) + goto endjob; + } + + ret = 0; + + endjob: + virDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -20383,6 +20623,9 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainSetLaunchSecurityState = qemuDomainSetLaunchSecurityState, /* 8.0.0 */ .domainFDAssociate = qemuDomainFDAssociate, /* 9.0.0 */ .domainGraphicsReload = qemuDomainGraphicsReload, /* 10.2.0 */ + .domainSetThrottleGroup = qemuDomainSetThrottleGroup, /* 11.1.0 */ + .domainDelThrottleGroup = qemuDomainDelThrottleGroup, /* 11.1.0 */ + }; -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:13 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
ThrottleGroup lifecycle implementation, note, in QOM, throttlegroup name is prefixed with "throttle-" to clearly separate throttle group objects into their own namespace. * "qemuDomainSetThrottleGroup", this method is to add("object-add") or update("qom-set") throttlegroup in QOM and update corresponding objects in DOM * "qemuDomainGetThrottleGroup", this method queries throttlegroup info by groupname * "qemuDomainDelThrottleGroup", this method checks if group is referenced by any throttle in disks and delete it if it's not used anymore * Check flag "QEMU_CAPS_OBJECT_JSON" during qemuDomainSetThrottleGroup when vm is active, throttle group feature requries such flag * "objectAddNoWrap"("props") check is done by reusing qemuMonitorAddObject in qemuDomainSetThrottleGroup
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Apply suggested coding style changes. * cleanup qemu Get ThrottleGroup. * Update the version to 11.1.0.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_driver.c | 243 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+)
[...]
+ priv = vm->privateData; + qemuCaps = priv->qemuCaps; + if (virDomainObjIsActive(vm)) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_JSON)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("This QEMU doesn't support throttle group creation")); + return -1; + }
While looking at this I noticed that QEMU_CAPS_OBJECT_JSON is no longer needed as it's always present with qemu versions we currently support: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/message/7ROU... Once the patches are reviewed I'll rebase this on top and delete this.

On Wed, Feb 19, 2025 at 22:27:13 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
ThrottleGroup lifecycle implementation, note, in QOM, throttlegroup name is prefixed with "throttle-" to clearly separate throttle group objects into their own namespace. * "qemuDomainSetThrottleGroup", this method is to add("object-add") or update("qom-set") throttlegroup in QOM and update corresponding objects in DOM * "qemuDomainGetThrottleGroup", this method queries throttlegroup info by groupname * "qemuDomainDelThrottleGroup", this method checks if group is referenced by any throttle in disks and delete it if it's not used anymore * Check flag "QEMU_CAPS_OBJECT_JSON" during qemuDomainSetThrottleGroup when vm is active, throttle group feature requries such flag * "objectAddNoWrap"("props") check is done by reusing qemuMonitorAddObject in qemuDomainSetThrottleGroup
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Apply suggested coding style changes. * cleanup qemu Get ThrottleGroup. * Update the version to 11.1.0.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_driver.c | 243 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+)
I've removed the check mentioned in my other reply and fixed the version info. Reviewed-by: Peter Krempa <pkrempa@redhat.com>

From: Chun Feng Wu <danielwuwy@163.com> It contains throttle filter nodename processing(new nodename, topnodename, parse and format nodename), throttle filter attaching/detaching preparation and implementation. * Updated "qemuDomainDiskGetTopNodename", so if throttlefilter is used together with copyOnRead, top node is throttle filter node, e.g. device -> throttle -> copyOnRead Layer-> image chain * In qemuBuildThrottleFiltersAttachPrepareBlockdev, if copy_on_read is on, build throttle nodename chain on top of copy_on_read nodename * In status xml, throttle filter nodename(virDomainDiskDef.nodename) is saved at disk/privateData/nodenames/nodename(type='throttle-filter'), corresponding parse/format sits in qemuDomainDiskPrivateParse and qemuDomainDiskPrivateFormat * If filter nodename hasn't been set by qemuDomainDiskPrivateParse, in qemuDomainPrepareThrottleFilterBlockdev, filter nodename index can be generated by reusing qemuDomainStorageIDNew and current global sequence number is persistented in virDomainObj- >privateData(qemuDomainObjPrivate)->nodenameindex. qemuDomainPrepareThrottleFilterBlockdev is called by qemuDomainPrepareDiskSourceBlockdev, which in turn used by both hotplug and qemuProcessStart to prepare throttle filter node name * Define method qemuBlockThrottleFilterGetProps, which is used by both hotplug and command to build throttle object for QEMU * Define methods for throttle filter attach/detach/rollback Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Apply suggested coding style changes. * Update of code documentation comments. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_block.c | 136 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_block.h | 49 +++++++++++++++ src/qemu/qemu_command.c | 81 ++++++++++++++++++++++++ src/qemu/qemu_command.h | 6 ++ src/qemu/qemu_domain.c | 77 +++++++++++++++++++++-- 5 files changed, 345 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 35dca8ee7b..254ea87465 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -2775,6 +2775,142 @@ qemuBlockStorageSourceCreateDetectSize(GHashTable *blockNamedNodeData, } +void +qemuBlockThrottleFilterSetNodename(virDomainThrottleFilterDef *filter, + char *nodename) +{ + g_free(filter->nodename); + filter->nodename = nodename; +} + + +const char * +qemuBlockThrottleFilterGetNodename(virDomainThrottleFilterDef *filter) +{ + return filter->nodename; +} + + +/** + * qemuBlockThrottleFilterGetProps: + * @filter: throttle filter + * @parentNodeName: parent nodename of @filter + * + * Build "arguments" part of "blockdev-add" QMP cmd. + */ +static inline virJSONValue * +qemuBlockThrottleFilterGetProps(virDomainThrottleFilterDef *filter, + const char *parentNodeName) +{ + g_autoptr(virJSONValue) props = NULL; + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name = g_strdup_printf("throttle-%s", filter->group_name); + if (virJSONValueObjectAdd(&props, + "s:driver", "throttle", + "s:node-name", qemuBlockThrottleFilterGetNodename(filter), + "s:throttle-group", prefixed_group_name, + "s:file", parentNodeName, + NULL) < 0) + return NULL; + + return g_steal_pointer(&props); +} + + +void +qemuBlockThrottleFilterAttachDataFree(qemuBlockThrottleFilterAttachData *data) +{ + if (!data) + return; + + virJSONValueFree(data->filterProps); + g_free(data); +} + + +qemuBlockThrottleFilterAttachData * +qemuBlockThrottleFilterAttachPrepareBlockdev(virDomainThrottleFilterDef *filter, + const char *parentNodeName) +{ + g_autoptr(qemuBlockThrottleFilterAttachData) data = NULL; + + data = g_new0(qemuBlockThrottleFilterAttachData, 1); + + if (!(data->filterProps = qemuBlockThrottleFilterGetProps(filter, parentNodeName))) + return NULL; + + data->filterNodeName = qemuBlockThrottleFilterGetNodename(filter); + + return g_steal_pointer(&data); +} + + +void +qemuBlockThrottleFilterAttachRollback(qemuMonitor *mon, + qemuBlockThrottleFilterAttachData *data) +{ + virErrorPtr orig_err; + + virErrorPreserveLast(&orig_err); + + if (data->filterAttached) + ignore_value(qemuMonitorBlockdevDel(mon, data->filterNodeName)); + + virErrorRestore(&orig_err); +} + + +void +qemuBlockThrottleFiltersDataFree(qemuBlockThrottleFiltersData *data) +{ + size_t i; + + if (!data) + return; + + for (i = 0; i < data->nfilterdata; i++) + qemuBlockThrottleFilterAttachDataFree(data->filterdata[i]); + + g_free(data->filterdata); + g_free(data); +} + + +/** + * qemuBlockThrottleFiltersAttach: + * @mon: monitor object + * @data: filter chain data + * + * Attach throttle filters. + * Caller must enter @mon prior calling this function. + */ +int +qemuBlockThrottleFiltersAttach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data) +{ + size_t i; + + for (i = 0; i < data->nfilterdata; i++) { + if (qemuMonitorBlockdevAdd(mon, &data->filterdata[i]->filterProps) < 0) + return -1; + data->filterdata[i]->filterAttached = true; + } + + return 0; +} + + +void +qemuBlockThrottleFiltersDetach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data) +{ + size_t i; + + for (i = data->nfilterdata; i > 0; i--) + qemuBlockThrottleFilterAttachRollback(mon, data->filterdata[i-1]); +} + + int qemuBlockRemoveImageMetadata(virQEMUDriver *driver, virDomainObj *vm, diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index f13a4a4a9a..b9e950e494 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -215,6 +215,55 @@ qemuBlockStorageSourceCreateDetectSize(GHashTable *blockNamedNodeData, virStorageSource *src, virStorageSource *templ); +void +qemuBlockThrottleFilterSetNodename(virDomainThrottleFilterDef *filter, + char *nodename); + +const char * +qemuBlockThrottleFilterGetNodename(virDomainThrottleFilterDef *filter); + +typedef struct qemuBlockThrottleFilterAttachData qemuBlockThrottleFilterAttachData; +struct qemuBlockThrottleFilterAttachData { + virJSONValue *filterProps; + const char *filterNodeName; + bool filterAttached; +}; + +qemuBlockThrottleFilterAttachData * +qemuBlockThrottleFilterAttachPrepareBlockdev(virDomainThrottleFilterDef *throttlefilter, + const char *parentNodeName); + +void +qemuBlockThrottleFilterAttachDataFree(qemuBlockThrottleFilterAttachData *data); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockThrottleFilterAttachData, + qemuBlockThrottleFilterAttachDataFree); + +void +qemuBlockThrottleFilterAttachRollback(qemuMonitor *mon, + qemuBlockThrottleFilterAttachData *data); + +struct _qemuBlockThrottleFiltersData { + qemuBlockThrottleFilterAttachData **filterdata; + size_t nfilterdata; +}; + +typedef struct _qemuBlockThrottleFiltersData qemuBlockThrottleFiltersData; + +void +qemuBlockThrottleFiltersDataFree(qemuBlockThrottleFiltersData *data); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockThrottleFiltersData, + qemuBlockThrottleFiltersDataFree); + +int +qemuBlockThrottleFiltersAttach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data); + +void +qemuBlockThrottleFiltersDetach(qemuMonitor *mon, + qemuBlockThrottleFiltersData *data); + int qemuBlockRemoveImageMetadata(virQEMUDriver *driver, virDomainObj *vm, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 54130ac4f0..750edd4faa 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11043,6 +11043,87 @@ qemuBuildStorageSourceChainAttachPrepareBlockdevOne(qemuBlockStorageSourceChainD } +/** + * qemuBuildThrottleFiltersAttachPrepareBlockdevOne: + * @data: filter chain data, which consists of array of filters and size of such array + * @throttlefilter: new filter to be added into filter array + * @parentNodeName: parent nodename for this new throttlefilter + * + * Build filter node chain to provide more flexibility for block disk I/O limits + */ +static int +qemuBuildThrottleFiltersAttachPrepareBlockdevOne(qemuBlockThrottleFiltersData *data, + virDomainThrottleFilterDef *throttlefilter, + const char *parentNodeName) +{ + g_autoptr(qemuBlockThrottleFilterAttachData) elem = NULL; + + if (!(elem = qemuBlockThrottleFilterAttachPrepareBlockdev(throttlefilter, parentNodeName))) + return -1; + + VIR_APPEND_ELEMENT(data->filterdata, data->nfilterdata, elem); + return 0; +} + + +/** + * qemuBuildThrottleFiltersAttachPrepareBlockdev: + * @disk: domain disk + * + * Build filter node chain to provide more flexibility for block disk I/O limits + */ +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersAttachPrepareBlockdev(virDomainDiskDef *disk) +{ + g_autoptr(qemuBlockThrottleFiltersData) data = NULL; + size_t i; + const char *parentNodeName = NULL; + qemuDomainDiskPrivate *priv = QEMU_DOMAIN_DISK_PRIVATE(disk); + + data = g_new0(qemuBlockThrottleFiltersData, 1); + /* if copy_on_read is enabled, put throttle chain on top of it */ + if (disk->copy_on_read == VIR_TRISTATE_SWITCH_ON) { + parentNodeName = priv->nodeCopyOnRead; + } else { + parentNodeName = qemuBlockStorageSourceGetEffectiveNodename(disk->src); + + } + /* build filterdata, which contains all filters info and sequence info through parentNodeName */ + for (i = 0; i < disk->nthrottlefilters; i++) { + if (qemuBuildThrottleFiltersAttachPrepareBlockdevOne(data, disk->throttlefilters[i], parentNodeName) < 0) + return NULL; + parentNodeName = disk->throttlefilters[i]->nodename; + } + + return g_steal_pointer(&data); +} + + +/** + * qemuBuildThrottleFiltersDetachPrepareBlockdev: + * @disk: domain disk + * + * Build filters data for later "blockdev-del" + */ +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersDetachPrepareBlockdev(virDomainDiskDef *disk) +{ + g_autoptr(qemuBlockThrottleFiltersData) data = g_new0(qemuBlockThrottleFiltersData, 1); + size_t i; + + /* build filterdata, which contains filters info and sequence info */ + for (i = 0; i < disk->nthrottlefilters; i++) { + g_autoptr(qemuBlockThrottleFilterAttachData) elem = g_new0(qemuBlockThrottleFilterAttachData, 1); + /* ignore other fields since the following info are enough for "blockdev-del" */ + elem->filterNodeName = qemuBlockThrottleFilterGetNodename(disk->throttlefilters[i]); + elem->filterAttached = true; + + VIR_APPEND_ELEMENT(data->filterdata, data->nfilterdata, elem); + } + return g_steal_pointer(&data); +} + + /** * qemuBuildStorageSourceChainAttachPrepareBlockdev: * @top: storage source chain diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 76c514b5f7..1b0296ea42 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -116,6 +116,12 @@ qemuBlockStorageSourceChainData * qemuBuildStorageSourceChainAttachPrepareBlockdevTop(virStorageSource *top, virStorageSource *backingStore); +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersAttachPrepareBlockdev(virDomainDiskDef *disk); + +qemuBlockThrottleFiltersData * +qemuBuildThrottleFiltersDetachPrepareBlockdev(virDomainDiskDef *disk); + virJSONValue * qemuBuildDiskDeviceProps(const virDomainDef *def, virDomainDiskDef *disk, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index df1ed0223d..5ef7e3782f 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2235,6 +2235,33 @@ qemuStorageSourcePrivateDataFormat(virStorageSource *src, } +static int +virDomainDiskThrottleFilterNodeNamesParse(xmlXPathContextPtr ctxt, + virDomainDiskDef *def) +{ + size_t i; + int n = 0; + g_autofree xmlNodePtr *nodes = NULL; + g_autoptr(GHashTable) throttleFiltersMap = virHashNew(g_free); + + if ((n = virXPathNodeSet("./nodenames/nodename[@type='throttle-filter']", ctxt, &nodes)) < 0) + return -1; + + for (i = 0; i < n; i++) { + g_hash_table_insert(throttleFiltersMap, virXMLPropString(nodes[i], "group"), virXMLPropString(nodes[i], "name")); + } + + for (i = 0; i < def->nthrottlefilters; i++) { + char *nodename = g_hash_table_lookup(throttleFiltersMap, def->throttlefilters[i]->group_name); + if (nodename) { + qemuBlockThrottleFilterSetNodename(def->throttlefilters[i], g_strdup(nodename)); + } + } + + return 0; +} + + static int qemuDomainDiskPrivateParse(xmlXPathContextPtr ctxt, virDomainDiskDef *disk) @@ -2244,6 +2271,9 @@ qemuDomainDiskPrivateParse(xmlXPathContextPtr ctxt, priv->qomName = virXPathString("string(./qom/@name)", ctxt); priv->nodeCopyOnRead = virXPathString("string(./nodenames/nodename[@type='copyOnRead']/@name)", ctxt); + if (virDomainDiskThrottleFilterNodeNamesParse(ctxt, disk) < 0) + return -1; + return 0; } @@ -2253,14 +2283,27 @@ qemuDomainDiskPrivateFormat(virDomainDiskDef *disk, virBuffer *buf) { qemuDomainDiskPrivate *priv = QEMU_DOMAIN_DISK_PRIVATE(disk); + size_t i; virBufferEscapeString(buf, "<qom name='%s'/>\n", priv->qomName); - if (priv->nodeCopyOnRead) { + if (priv->nodeCopyOnRead || disk->nthrottlefilters > 0) { virBufferAddLit(buf, "<nodenames>\n"); virBufferAdjustIndent(buf, 2); - virBufferEscapeString(buf, "<nodename type='copyOnRead' name='%s'/>\n", - priv->nodeCopyOnRead); + if (priv->nodeCopyOnRead) + virBufferEscapeString(buf, "<nodename type='copyOnRead' name='%s'/>\n", + priv->nodeCopyOnRead); + if (disk->nthrottlefilters > 0) { + for (i = 0; i < disk->nthrottlefilters; i++) { + + if (disk->throttlefilters[i]->nodename) + virBufferEscapeString(buf, "<nodename type='throttle-filter' name='%s' ", + disk->throttlefilters[i]->nodename); + + if (disk->throttlefilters[i]->group_name) + virBufferEscapeString(buf, "group='%s'/>\n", disk->throttlefilters[i]->group_name); + } + } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</nodenames>\n"); } @@ -6293,7 +6336,8 @@ qemuDomainDetermineDiskChain(virQEMUDriver *driver, * @disk: disk definition object * * Returns the pointer to the node-name of the topmost layer used by @disk as - * backend. Currently returns the nodename of the copy-on-read filter if enabled + * backend. Currently returns the nodename of top throttle filter if enabled + * or the nodename of the copy-on-read filter if enabled * or the nodename of the top image's format driver. Empty disks return NULL. * This must be used only with disks instantiated via -blockdev (thus not * for SD cards). @@ -6306,6 +6350,10 @@ qemuDomainDiskGetTopNodename(virDomainDiskDef *disk) if (virStorageSourceIsEmpty(disk->src)) return NULL; + /* If disk has throttles, take top throttle node name */ + if (disk->nthrottlefilters > 0) + return disk->throttlefilters[disk->nthrottlefilters - 1]->nodename; + if (disk->copy_on_read == VIR_TRISTATE_SWITCH_ON) return priv->nodeCopyOnRead; @@ -9708,6 +9756,22 @@ qemuDomainPrepareStorageSourceBlockdevNodename(virDomainDiskDef *disk, } +static void +qemuDomainPrepareThrottleFilterBlockdev(virDomainThrottleFilterDef *filter, + qemuDomainObjPrivate *priv) +{ + g_autofree char *nodenameprefix = NULL; + + /* skip setting throttle filter nodename if it's set by parsing statusxml */ + if (filter->nodename) { + return; + } + nodenameprefix = g_strdup_printf("libvirt-%u", qemuDomainStorageIDNew(priv)); + + qemuBlockThrottleFilterSetNodename(filter, g_strdup_printf("%s-filter", nodenameprefix)); +} + + int qemuDomainPrepareStorageSourceBlockdev(virDomainDiskDef *disk, virStorageSource *src, @@ -9731,6 +9795,7 @@ qemuDomainPrepareDiskSourceBlockdev(virDomainDiskDef *disk, { qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); virStorageSource *n; + size_t i; if (disk->copy_on_read == VIR_TRISTATE_SWITCH_ON && !diskPriv->nodeCopyOnRead) @@ -9745,6 +9810,10 @@ qemuDomainPrepareDiskSourceBlockdev(virDomainDiskDef *disk, return -1; } + for (i = 0; i < disk->nthrottlefilters; i++) { + qemuDomainPrepareThrottleFilterBlockdev(disk->throttlefilters[i], priv); + } + return 0; } -- 2.39.5 (Apple Git-154)

From: Chun Feng Wu <danielwuwy@163.com> For hot attaching/detaching * Leverage qemuBlockThrottleFiltersData to prepare attaching/detaching throttle filter data for qemuMonitorBlockdevAdd and qemuMonitorBlockdevDel * For hot attaching, within qemuDomainAttachDiskGeneric,prepare throttle filters json data, and create corresponding blockdev for QMP request ("blockdev-add" with "driver":"throttle") * Each filter has a nodename, and those filters are chained up, create them in sequence, and delete them reversely * Delete filters by "qemuBlockThrottleFiltersDetach"("blockdev-del") when detaching device For throttle group commandline * Add qemuBuildThrottleGroupCommandLine in qemuBuildCommandLine to add "object" of throttle-group * Verify throttle group definition when lauching vm * Check QEMU_CAPS_OBJECT_JSON before "qemuBuildObjectCommandlineFromJSON", which is to build "-object" option For throttle filter commandline * Add qemuBuildDiskThrottleFiltersCommandLine in qemuBuildDiskCommandLine to add "blockdev" Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Apply suggested coding style changes. * Update of code documentation comments. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_command.c | 99 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.c | 21 +++++++++ 2 files changed, 120 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 750edd4faa..6d9efbe887 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2045,6 +2045,99 @@ qemuBuildBlockStorageSourceAttachDataCommandline(virCommand *cmd, } +static inline bool +qemuDiskConfigThrottleGroupEnabled(const virDomainThrottleGroupDef *group) +{ + return !!group->group_name && + virDomainBlockIoTuneInfoHasAny(group); +} + + +/** + * qemuBuildThrottleGroupCommandLine: + * @cmd: the command to modify + * @def: domain definition + * @qemuCaps: qemu capabilities object + * + * build throttle group object in json format + */ +static int +qemuBuildThrottleGroupCommandLine(virCommand *cmd, + const virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + size_t i; + + for (i = 0; i < def->nthrottlegroups; i++) { + g_autoptr(virJSONValue) props = NULL; + g_autoptr(virJSONValue) limits = virJSONValueNewObject(); + virDomainThrottleGroupDef *group = def->throttlegroups[i]; + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name = g_strdup_printf("throttle-%s", group->group_name); + + if (!qemuDiskConfigThrottleGroupEnabled(group)) { + continue; + } + + if (qemuMonitorThrottleGroupLimits(limits, group) < 0) + return -1; + + if (qemuMonitorCreateObjectProps(&props, "throttle-group", prefixed_group_name, + "a:limits", &limits, + NULL) < 0) + return -1; + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_JSON)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("This QEMU doesn't support throttle group creation")); + return -1; + } + + if (qemuBuildObjectCommandlineFromJSON(cmd, props, qemuCaps) < 0) + return -1; + } + + return 0; +} + + +static int +qemuBuildBlockThrottleFilterCommandline(virCommand *cmd, + qemuBlockThrottleFilterAttachData *data) +{ + if (data->filterProps) { + g_autofree char *tmp = NULL; + if (!(tmp = virJSONValueToString(data->filterProps, false))) + return -1; + + virCommandAddArgList(cmd, "-blockdev", tmp, NULL); + } + + return 0; +} + + +static int +qemuBuildDiskThrottleFiltersCommandLine(virCommand *cmd, + virDomainDiskDef *disk) +{ + g_autoptr(qemuBlockThrottleFiltersData) data = NULL; + size_t i; + + data = qemuBuildThrottleFiltersAttachPrepareBlockdev(disk); + if (!data) + return -1; + + for (i = 0; i < data->nfilterdata; i++) { + if (qemuBuildBlockThrottleFilterCommandline(cmd, + data->filterdata[i]) < 0) + return -1; + } + + return 0; +} + + static int qemuBuildDiskSourceCommandLine(virCommand *cmd, virDomainDiskDef *disk, @@ -2102,6 +2195,9 @@ qemuBuildDiskCommandLine(virCommand *cmd, if (qemuBuildDiskSourceCommandLine(cmd, disk, qemuCaps) < 0) return -1; + if (qemuBuildDiskThrottleFiltersCommandLine(cmd, disk) < 0) + return -1; + /* SD cards are currently instantiated via -drive if=sd, so the -device * part must be skipped */ if (qemuDiskBusIsSD(disk->bus)) @@ -10479,6 +10575,9 @@ qemuBuildCommandLine(virDomainObj *vm, if (qemuBuildIOThreadCommandLine(cmd, def, qemuCaps) < 0) return NULL; + if (qemuBuildThrottleGroupCommandLine(cmd, def, qemuCaps) < 0) + return NULL; + if (virDomainNumaGetNodeCount(def->numa) && qemuBuildNumaCommandLine(cfg, def, cmd, priv) < 0) return NULL; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 28ca321c5c..75437164ca 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -708,6 +708,7 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, virDomainAsyncJob asyncJob) { g_autoptr(qemuBlockStorageSourceChainData) data = NULL; + g_autoptr(qemuBlockThrottleFiltersData) filterData = NULL; qemuDomainObjPrivate *priv = vm->privateData; g_autoptr(virJSONValue) devprops = NULL; bool extensionDeviceAttached = false; @@ -746,6 +747,15 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, if (rc < 0) goto rollback; + if ((filterData = qemuBuildThrottleFiltersAttachPrepareBlockdev(disk))) { + if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0) + return -1; + rc = qemuBlockThrottleFiltersAttach(priv->mon, filterData); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto rollback; + } + if (disk->transient) { g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL; g_autoptr(GHashTable) blockNamedNodeData = NULL; @@ -817,6 +827,8 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, if (extensionDeviceAttached) ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &disk->info)); + qemuBlockThrottleFiltersDetach(priv->mon, filterData); + qemuBlockStorageSourceChainDetach(priv->mon, data); qemuDomainObjExitMonitor(vm); @@ -4686,6 +4698,7 @@ qemuDomainRemoveDiskDevice(virQEMUDriver *driver, { qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); g_autoptr(qemuBlockStorageSourceChainData) diskBackend = NULL; + g_autoptr(qemuBlockThrottleFiltersData) filterData = NULL; size_t i; qemuDomainObjPrivate *priv = vm->privateData; int ret = -1; @@ -4726,6 +4739,14 @@ qemuDomainRemoveDiskDevice(virQEMUDriver *driver, qemuDomainObjEnterMonitor(vm); + if ((filterData = qemuBuildThrottleFiltersDetachPrepareBlockdev(disk))) { + + qemuBlockThrottleFiltersDetach(priv->mon, filterData); + } + qemuDomainObjExitMonitor(vm); + + qemuDomainObjEnterMonitor(vm); + if (diskBackend) qemuBlockStorageSourceChainDetach(priv->mon, diskBackend); -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:15 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
For hot attaching/detaching * Leverage qemuBlockThrottleFiltersData to prepare attaching/detaching throttle filter data for qemuMonitorBlockdevAdd and qemuMonitorBlockdevDel * For hot attaching, within qemuDomainAttachDiskGeneric,prepare throttle filters json data, and create corresponding blockdev for QMP request ("blockdev-add" with "driver":"throttle") * Each filter has a nodename, and those filters are chained up, create them in sequence, and delete them reversely * Delete filters by "qemuBlockThrottleFiltersDetach"("blockdev-del") when detaching device
For throttle group commandline * Add qemuBuildThrottleGroupCommandLine in qemuBuildCommandLine to add "object" of throttle-group * Verify throttle group definition when lauching vm * Check QEMU_CAPS_OBJECT_JSON before "qemuBuildObjectCommandlineFromJSON", which is to build "-object" option
For throttle filter commandline * Add qemuBuildDiskThrottleFiltersCommandLine in qemuBuildDiskCommandLine to add "blockdev"
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Apply suggested coding style changes. * Update of code documentation comments.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/qemu/qemu_command.c | 99 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.c | 21 +++++++++ 2 files changed, 120 insertions(+)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 750edd4faa..6d9efbe887 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2045,6 +2045,99 @@ qemuBuildBlockStorageSourceAttachDataCommandline(virCommand *cmd, }
+static inline bool +qemuDiskConfigThrottleGroupEnabled(const virDomainThrottleGroupDef *group) +{ + return !!group->group_name && + virDomainBlockIoTuneInfoHasAny(group); +} + + +/** + * qemuBuildThrottleGroupCommandLine: + * @cmd: the command to modify + * @def: domain definition + * @qemuCaps: qemu capabilities object + * + * build throttle group object in json format + */ +static int +qemuBuildThrottleGroupCommandLine(virCommand *cmd, + const virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + size_t i; + + for (i = 0; i < def->nthrottlegroups; i++) { + g_autoptr(virJSONValue) props = NULL; + g_autoptr(virJSONValue) limits = virJSONValueNewObject(); + virDomainThrottleGroupDef *group = def->throttlegroups[i]; + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name = g_strdup_printf("throttle-%s", group->group_name); + + if (!qemuDiskConfigThrottleGroupEnabled(group)) { + continue; + } + + if (qemuMonitorThrottleGroupLimits(limits, group) < 0) + return -1; + + if (qemuMonitorCreateObjectProps(&props, "throttle-group", prefixed_group_name, + "a:limits", &limits, + NULL) < 0) + return -1; + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_JSON)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("This QEMU doesn't support throttle group creation")); + return -1;
This would be considered too late for this check; Luckily all of it can be deledet now.
+ } + + if (qemuBuildObjectCommandlineFromJSON(cmd, props, qemuCaps) < 0) + return -1; + } + + return 0; +} + + +static int +qemuBuildBlockThrottleFilterCommandline(virCommand *cmd, + qemuBlockThrottleFilterAttachData *data) +{ + if (data->filterProps) { + g_autofree char *tmp = NULL; + if (!(tmp = virJSONValueToString(data->filterProps, false))) + return -1; + + virCommandAddArgList(cmd, "-blockdev", tmp, NULL); + } + + return 0; +} + + +static int +qemuBuildDiskThrottleFiltersCommandLine(virCommand *cmd, + virDomainDiskDef *disk) +{ + g_autoptr(qemuBlockThrottleFiltersData) data = NULL; + size_t i; + + data = qemuBuildThrottleFiltersAttachPrepareBlockdev(disk); + if (!data) + return -1; + + for (i = 0; i < data->nfilterdata; i++) { + if (qemuBuildBlockThrottleFilterCommandline(cmd, + data->filterdata[i]) < 0) + return -1; + } + + return 0; +} + + static int qemuBuildDiskSourceCommandLine(virCommand *cmd, virDomainDiskDef *disk, @@ -2102,6 +2195,9 @@ qemuBuildDiskCommandLine(virCommand *cmd, if (qemuBuildDiskSourceCommandLine(cmd, disk, qemuCaps) < 0) return -1;
+ if (qemuBuildDiskThrottleFiltersCommandLine(cmd, disk) < 0) + return -1; + /* SD cards are currently instantiated via -drive if=sd, so the -device * part must be skipped */ if (qemuDiskBusIsSD(disk->bus)) @@ -10479,6 +10575,9 @@ qemuBuildCommandLine(virDomainObj *vm, if (qemuBuildIOThreadCommandLine(cmd, def, qemuCaps) < 0) return NULL;
+ if (qemuBuildThrottleGroupCommandLine(cmd, def, qemuCaps) < 0) + return NULL; + if (virDomainNumaGetNodeCount(def->numa) && qemuBuildNumaCommandLine(cfg, def, cmd, priv) < 0) return NULL; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 28ca321c5c..75437164ca 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -708,6 +708,7 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, virDomainAsyncJob asyncJob) { g_autoptr(qemuBlockStorageSourceChainData) data = NULL; + g_autoptr(qemuBlockThrottleFiltersData) filterData = NULL; qemuDomainObjPrivate *priv = vm->privateData; g_autoptr(virJSONValue) devprops = NULL; bool extensionDeviceAttached = false; @@ -746,6 +747,15 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, if (rc < 0) goto rollback;
+ if ((filterData = qemuBuildThrottleFiltersAttachPrepareBlockdev(disk))) { + if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0) + return -1; + rc = qemuBlockThrottleFiltersAttach(priv->mon, filterData); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto rollback; + } + if (disk->transient) { g_autoptr(qemuBlockStorageSourceAttachData) backend = NULL; g_autoptr(GHashTable) blockNamedNodeData = NULL; @@ -817,6 +827,8 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, if (extensionDeviceAttached) ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &disk->info));
+ qemuBlockThrottleFiltersDetach(priv->mon, filterData); + qemuBlockStorageSourceChainDetach(priv->mon, data);
qemuDomainObjExitMonitor(vm); @@ -4686,6 +4698,7 @@ qemuDomainRemoveDiskDevice(virQEMUDriver *driver, { qemuDomainDiskPrivate *diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); g_autoptr(qemuBlockStorageSourceChainData) diskBackend = NULL; + g_autoptr(qemuBlockThrottleFiltersData) filterData = NULL; size_t i; qemuDomainObjPrivate *priv = vm->privateData; int ret = -1; @@ -4726,6 +4739,14 @@ qemuDomainRemoveDiskDevice(virQEMUDriver *driver,
qemuDomainObjEnterMonitor(vm);
+ if ((filterData = qemuBuildThrottleFiltersDetachPrepareBlockdev(disk))) {
I'll have to also check the layering with copy on read which is detached below
+ + qemuBlockThrottleFiltersDetach(priv->mon, filterData); + } + qemuDomainObjExitMonitor(vm); + + qemuDomainObjEnterMonitor(vm);
No need to exit the monitor to just enter it back.
+ if (diskBackend) qemuBlockStorageSourceChainDetach(priv->mon, diskBackend);
-- 2.39.5 (Apple Git-154)

From: Chun Feng Wu <danielwuwy@163.com> Refactor iotune verification, and verify some rules * Disk iotune validation can be reused for throttle group validation, refactor it into common method "virDomainDiskIoTuneValidate" * Add "virDomainDefValidateThrottleGroups" to validate throttle groups, which in turn calls "virDomainDiskIoTuneValidate" * Make sure referenced throttle group exists * Use "iotune" and "throttlefilters" exclusively for specific disk * Throttle filters cannot be used together with CDROM Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Update of code documentation comments. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/conf/domain_conf.c | 8 +++ src/conf/domain_validate.c | 118 ++++++++++++++++++++++++++----------- src/qemu/qemu_driver.c | 13 ++++ src/qemu/qemu_hotplug.c | 8 +++ 4 files changed, 113 insertions(+), 34 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 303aa7d1ae..0cc318d1f9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -8008,6 +8008,14 @@ virDomainDiskDefThrottleFiltersParse(virDomainDiskDef *def, if ((n = virXPathNodeSet("./throttlefilters/throttlefilter", ctxt, &nodes)) < 0) return -1; + if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (n > 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cdrom device with throttle filters isn't supported")); + return -1; + } + } + if (n) def->throttlefilters = g_new0(virDomainThrottleFilterDef *, n); diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 563558d920..d51682d155 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -685,11 +685,55 @@ virDomainDiskDefValidateStartupPolicy(const virDomainDiskDef *disk) } +static int +virDomainDiskIoTuneValidate(const virDomainBlockIoTuneInfo blkdeviotune) +{ + if ((blkdeviotune.total_bytes_sec && + blkdeviotune.read_bytes_sec) || + (blkdeviotune.total_bytes_sec && + blkdeviotune.write_bytes_sec)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write bytes_sec cannot be set at the same time")); + return -1; + } + + if ((blkdeviotune.total_iops_sec && + blkdeviotune.read_iops_sec) || + (blkdeviotune.total_iops_sec && + blkdeviotune.write_iops_sec)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write iops_sec cannot be set at the same time")); + return -1; + } + + if ((blkdeviotune.total_bytes_sec_max && + blkdeviotune.read_bytes_sec_max) || + (blkdeviotune.total_bytes_sec_max && + blkdeviotune.write_bytes_sec_max)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write bytes_sec_max cannot be set at the same time")); + return -1; + } + + if ((blkdeviotune.total_iops_sec_max && + blkdeviotune.read_iops_sec_max) || + (blkdeviotune.total_iops_sec_max && + blkdeviotune.write_iops_sec_max)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write iops_sec_max cannot be set at the same time")); + return -1; + } + + return 0; +} + + static int virDomainDiskDefValidate(const virDomainDef *def, const virDomainDiskDef *disk) { virStorageSource *next; + size_t i; /* disk target is used widely in other code so it must be validated first */ if (!disk->dst) { @@ -739,41 +783,8 @@ virDomainDiskDefValidate(const virDomainDef *def, } /* Validate IotuneParse */ - if ((disk->blkdeviotune.total_bytes_sec && - disk->blkdeviotune.read_bytes_sec) || - (disk->blkdeviotune.total_bytes_sec && - disk->blkdeviotune.write_bytes_sec)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write bytes_sec cannot be set at the same time")); + if (virDomainDiskIoTuneValidate(disk->blkdeviotune) < 0) return -1; - } - - if ((disk->blkdeviotune.total_iops_sec && - disk->blkdeviotune.read_iops_sec) || - (disk->blkdeviotune.total_iops_sec && - disk->blkdeviotune.write_iops_sec)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write iops_sec cannot be set at the same time")); - return -1; - } - - if ((disk->blkdeviotune.total_bytes_sec_max && - disk->blkdeviotune.read_bytes_sec_max) || - (disk->blkdeviotune.total_bytes_sec_max && - disk->blkdeviotune.write_bytes_sec_max)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write bytes_sec_max cannot be set at the same time")); - return -1; - } - - if ((disk->blkdeviotune.total_iops_sec_max && - disk->blkdeviotune.read_iops_sec_max) || - (disk->blkdeviotune.total_iops_sec_max && - disk->blkdeviotune.write_iops_sec_max)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write iops_sec_max cannot be set at the same time")); - return -1; - } /* Reject disks with a bus type that is not compatible with the * given address type. The function considers only buses that are @@ -974,6 +985,26 @@ virDomainDiskDefValidate(const virDomainDef *def, return -1; } + /* referenced group should be defined */ + for (i = 0; i < disk->nthrottlefilters; i++) { + virDomainThrottleFilterDef *filter = disk->throttlefilters[i]; + if (!virDomainThrottleGroupByName(def, filter->group_name)) { + virReportError(VIR_ERR_XML_ERROR, + _("throttle group '%1$s' not found"), + filter->group_name); + return -1; + } + } + + if (disk->throttlefilters && + (disk->blkdeviotune.group_name || + virDomainBlockIoTuneInfoHasAny(&disk->blkdeviotune))) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("block 'throttlefilters' can't be used together with 'iotune' for disk '%1$s'"), + disk->dst); + return -1; + } + return 0; } @@ -1874,6 +1905,22 @@ virDomainDefLaunchSecurityValidate(const virDomainDef *def) #undef CHECK_BASE64_LEN +static int +virDomainDefValidateThrottleGroups(const virDomainDef *def) +{ + size_t i; + + for (i = 0; i < def->nthrottlegroups; i++) { + virDomainThrottleGroupDef *throttleGroup = def->throttlegroups[i]; + + if (virDomainDiskIoTuneValidate(*throttleGroup) < 0) + return -1; + } + + return 0; +} + + static int virDomainDefValidateInternal(const virDomainDef *def, virDomainXMLOption *xmlopt) @@ -1932,6 +1979,9 @@ virDomainDefValidateInternal(const virDomainDef *def, if (virDomainDefLaunchSecurityValidate(def) < 0) return -1; + if (virDomainDefValidateThrottleGroups(def) < 0) + return -1; + return 0; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index afb41a4c89..6b1e8d01e0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6703,6 +6703,13 @@ qemuDomainAttachDeviceConfig(virDomainDef *vmdef, _("target %1$s already exists"), disk->dst); return -1; } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (disk->nthrottlefilters > 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cdrom device with throttle filters isn't supported")); + return -1; + } + } if (virDomainDiskTranslateSourcePool(disk) < 0) return -1; if (qemuCheckDiskConfigAgainstDomain(vmdef, disk) < 0) @@ -14861,6 +14868,12 @@ qemuDomainDiskBlockIoTuneIsSupported(virDomainDiskDef *disk) return false; } + if (disk->throttlefilters) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("block 'iotune' can't be used together with 'throttlefilters' for disk '%1$s'"), disk->dst); + return false; + } + return true; } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 75437164ca..ff29cd55f4 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -985,6 +985,14 @@ qemuDomainAttachDeviceDiskLiveInternal(virQEMUDriver *driver, bool releaseSeclabel = false; int ret = -1; + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (disk->nthrottlefilters > 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cdrom device with throttle filters isn't supported")); + return -1; + } + } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", _("floppy device hotplug isn't supported")); -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:16 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Refactor iotune verification, and verify some rules
* Disk iotune validation can be reused for throttle group validation, refactor it into common method "virDomainDiskIoTuneValidate" * Add "virDomainDefValidateThrottleGroups" to validate throttle groups, which in turn calls "virDomainDiskIoTuneValidate" * Make sure referenced throttle group exists * Use "iotune" and "throttlefilters" exclusively for specific disk * Throttle filters cannot be used together with CDROM
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Update of code documentation comments.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/conf/domain_conf.c | 8 +++ src/conf/domain_validate.c | 118 ++++++++++++++++++++++++++----------- src/qemu/qemu_driver.c | 13 ++++ src/qemu/qemu_hotplug.c | 8 +++ 4 files changed, 113 insertions(+), 34 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 303aa7d1ae..0cc318d1f9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -8008,6 +8008,14 @@ virDomainDiskDefThrottleFiltersParse(virDomainDiskDef *def, if ((n = virXPathNodeSet("./throttlefilters/throttlefilter", ctxt, &nodes)) < 0) return -1;
+ if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (n > 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cdrom device with throttle filters isn't supported")); + return -1; + } + }
Normally adding any form of validation into the parser is not allowed because it makes VMs vanish. as this is happening in hthe same series it's technically allowable, but ...
+ if (n) def->throttlefilters = g_new0(virDomainThrottleFilterDef *, n);
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 563558d920..d51682d155 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -685,11 +685,55 @@ virDomainDiskDefValidateStartupPolicy(const virDomainDiskDef *disk) }
+static int +virDomainDiskIoTuneValidate(const virDomainBlockIoTuneInfo blkdeviotune) +{ + if ((blkdeviotune.total_bytes_sec && + blkdeviotune.read_bytes_sec) || + (blkdeviotune.total_bytes_sec && + blkdeviotune.write_bytes_sec)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write bytes_sec cannot be set at the same time")); + return -1; + } + + if ((blkdeviotune.total_iops_sec && + blkdeviotune.read_iops_sec) || + (blkdeviotune.total_iops_sec && + blkdeviotune.write_iops_sec)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write iops_sec cannot be set at the same time")); + return -1; + } + + if ((blkdeviotune.total_bytes_sec_max && + blkdeviotune.read_bytes_sec_max) || + (blkdeviotune.total_bytes_sec_max && + blkdeviotune.write_bytes_sec_max)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write bytes_sec_max cannot be set at the same time")); + return -1; + } + + if ((blkdeviotune.total_iops_sec_max && + blkdeviotune.read_iops_sec_max) || + (blkdeviotune.total_iops_sec_max && + blkdeviotune.write_iops_sec_max)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("total and read/write iops_sec_max cannot be set at the same time")); + return -1; + } + + return 0; +} + + static int virDomainDiskDefValidate(const virDomainDef *def, const virDomainDiskDef *disk) { virStorageSource *next; + size_t i;
/* disk target is used widely in other code so it must be validated first */ if (!disk->dst) { @@ -739,41 +783,8 @@ virDomainDiskDefValidate(const virDomainDef *def, }
/* Validate IotuneParse */ - if ((disk->blkdeviotune.total_bytes_sec && - disk->blkdeviotune.read_bytes_sec) || - (disk->blkdeviotune.total_bytes_sec && - disk->blkdeviotune.write_bytes_sec)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write bytes_sec cannot be set at the same time")); + if (virDomainDiskIoTuneValidate(disk->blkdeviotune) < 0) return -1; - } - - if ((disk->blkdeviotune.total_iops_sec && - disk->blkdeviotune.read_iops_sec) || - (disk->blkdeviotune.total_iops_sec && - disk->blkdeviotune.write_iops_sec)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write iops_sec cannot be set at the same time")); - return -1; - } - - if ((disk->blkdeviotune.total_bytes_sec_max && - disk->blkdeviotune.read_bytes_sec_max) || - (disk->blkdeviotune.total_bytes_sec_max && - disk->blkdeviotune.write_bytes_sec_max)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write bytes_sec_max cannot be set at the same time")); - return -1; - } - - if ((disk->blkdeviotune.total_iops_sec_max && - disk->blkdeviotune.read_iops_sec_max) || - (disk->blkdeviotune.total_iops_sec_max && - disk->blkdeviotune.write_iops_sec_max)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("total and read/write iops_sec_max cannot be set at the same time")); - return -1; - }
/* Reject disks with a bus type that is not compatible with the * given address type. The function considers only buses that are @@ -974,6 +985,26 @@ virDomainDiskDefValidate(const virDomainDef *def, return -1; }
+ /* referenced group should be defined */ + for (i = 0; i < disk->nthrottlefilters; i++) { + virDomainThrottleFilterDef *filter = disk->throttlefilters[i]; + if (!virDomainThrottleGroupByName(def, filter->group_name)) { + virReportError(VIR_ERR_XML_ERROR, + _("throttle group '%1$s' not found"), + filter->group_name); + return -1; + } + } + + if (disk->throttlefilters && + (disk->blkdeviotune.group_name || + virDomainBlockIoTuneInfoHasAny(&disk->blkdeviotune))) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("block 'throttlefilters' can't be used together with 'iotune' for disk '%1$s'"), + disk->dst); + return -1;
... really belongs here into the validation code.
+ } + return 0; }
@@ -1874,6 +1905,22 @@ virDomainDefLaunchSecurityValidate(const virDomainDef *def)
#undef CHECK_BASE64_LEN
+static int +virDomainDefValidateThrottleGroups(const virDomainDef *def) +{ + size_t i; + + for (i = 0; i < def->nthrottlegroups; i++) { + virDomainThrottleGroupDef *throttleGroup = def->throttlegroups[i]; + + if (virDomainDiskIoTuneValidate(*throttleGroup) < 0) + return -1; + } + + return 0; +} + + static int virDomainDefValidateInternal(const virDomainDef *def, virDomainXMLOption *xmlopt) @@ -1932,6 +1979,9 @@ virDomainDefValidateInternal(const virDomainDef *def, if (virDomainDefLaunchSecurityValidate(def) < 0) return -1;
+ if (virDomainDefValidateThrottleGroups(def) < 0) + return -1; + return 0; }
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index afb41a4c89..6b1e8d01e0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6703,6 +6703,13 @@ qemuDomainAttachDeviceConfig(virDomainDef *vmdef, _("target %1$s already exists"), disk->dst); return -1; } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (disk->nthrottlefilters > 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cdrom device with throttle filters isn't supported")); + return -1;
This is dead code, the validation refuses this.
+ } + } if (virDomainDiskTranslateSourcePool(disk) < 0) return -1; if (qemuCheckDiskConfigAgainstDomain(vmdef, disk) < 0) @@ -14861,6 +14868,12 @@ qemuDomainDiskBlockIoTuneIsSupported(virDomainDiskDef *disk) return false; }
+ if (disk->throttlefilters) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("block 'iotune' can't be used together with 'throttlefilters' for disk '%1$s'"), disk->dst); + return false; + } + return true; }
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 75437164ca..ff29cd55f4 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -985,6 +985,14 @@ qemuDomainAttachDeviceDiskLiveInternal(virQEMUDriver *driver, bool releaseSeclabel = false; int ret = -1;
+ if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (disk->nthrottlefilters > 0) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cdrom device with throttle filters isn't supported")); + return -1; + } + }
Dead code.
+ if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", _("floppy device hotplug isn't supported")); -- 2.39.5 (Apple Git-154)

From: Chun Feng Wu <danielwuwy@163.com> * Add tests for throttlegroup domain xml processing, including groups referenced and not referenced by filters * Add tests for throttlefilter domain xml processing, including throttle group referenced by different disks * Add negative test case to report error when iotune is configured together with throttle filters Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Isolate domain xml test Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- .../throttlefilter-invalid.x86_64-latest.err | 1 + .../throttlefilter-invalid.xml | 89 +++++++++++++++ .../throttlefilter.x86_64-latest.args | 55 +++++++++ .../throttlefilter.x86_64-latest.xml | 105 ++++++++++++++++++ tests/qemuxmlconfdata/throttlefilter.xml | 95 ++++++++++++++++ tests/qemuxmlconftest.c | 2 + 6 files changed, 347 insertions(+) create mode 100644 tests/qemuxmlconfdata/throttlefilter-invalid.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/throttlefilter-invalid.xml create mode 100644 tests/qemuxmlconfdata/throttlefilter.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/throttlefilter.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/throttlefilter.xml diff --git a/tests/qemuxmlconfdata/throttlefilter-invalid.x86_64-latest.err b/tests/qemuxmlconfdata/throttlefilter-invalid.x86_64-latest.err new file mode 100644 index 0000000000..ddb9670617 --- /dev/null +++ b/tests/qemuxmlconfdata/throttlefilter-invalid.x86_64-latest.err @@ -0,0 +1 @@ +Operation not supported: block 'throttlefilters' can't be used together with 'iotune' for disk 'vda' diff --git a/tests/qemuxmlconfdata/throttlefilter-invalid.xml b/tests/qemuxmlconfdata/throttlefilter-invalid.xml new file mode 100644 index 0000000000..ebd250acac --- /dev/null +++ b/tests/qemuxmlconfdata/throttlefilter-invalid.xml @@ -0,0 +1,89 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <throttlegroups> + <throttlegroup> + <total_bytes_sec>4000</total_bytes_sec> + <total_iops_sec>4000</total_iops_sec> + <group_name>limit0</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>5000</read_bytes_sec> + <write_bytes_sec>5000</write_bytes_sec> + <total_iops_sec>5000</total_iops_sec> + <group_name>limit1</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>6000</read_bytes_sec> + <write_bytes_sec>6000</write_bytes_sec> + <total_iops_sec>6000</total_iops_sec> + <group_name>limit2</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>7000</read_bytes_sec> + <write_bytes_sec>7000</write_bytes_sec> + <total_iops_sec>7000</total_iops_sec> + <group_name>limit12</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>7000</read_bytes_sec> + <write_bytes_sec>7000</write_bytes_sec> + <total_iops_sec>7000</total_iops_sec> + <group_name>limit3</group_name> + </throttlegroup> + </throttlegroups> + <os> + <type arch='x86_64' 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-system-x86_64</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='vda' bus='virtio'/> + <iotune> + <total_bytes_sec>10000000</total_bytes_sec> + <read_iops_sec>400000</read_iops_sec> + <write_iops_sec>100000</write_iops_sec> + </iotune> + <throttlefilters> + <throttlefilter group='limit0'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest2'/> + <target dev='vdb' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit1'/> + <throttlefilter group='limit12'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest3'/> + <target dev='vdc' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit2'/> + <throttlefilter group='limit12'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxmlconfdata/throttlefilter.x86_64-latest.args b/tests/qemuxmlconfdata/throttlefilter.x86_64-latest.args new file mode 100644 index 0000000000..4012e7740f --- /dev/null +++ b/tests/qemuxmlconfdata/throttlefilter.x86_64-latest.args @@ -0,0 +1,55 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \ +-accel tcg \ +-cpu qemu64 \ +-m size=219136k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-object '{"qom-type":"throttle-group","id":"throttle-limit0","limits":{"bps-total":4000,"bps-read":0,"bps-write":0,"iops-total":4000,"iops-read":0,"iops-write":0,"bps-total-max":0,"bps-read-max":0,"bps-write-max":0,"iops-total-max":0,"iops-read-max":0,"iops-write-max":0,"iops-size":0}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit1","limits":{"bps-total":0,"bps-read":5000,"bps-write":5000,"iops-total":5000,"iops-read":0,"iops-write":0,"bps-total-max":0,"bps-read-max":0,"bps-write-max":0,"iops-total-max":0,"iops-read-max":0,"iops-write-max":0,"iops-size":0}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit2","limits":{"bps-total":0,"bps-read":6000,"bps-write":6000,"iops-total":6000,"iops-read":0,"iops-write":0,"bps-total-max":0,"bps-read-max":0,"bps-write-max":0,"iops-total-max":0,"iops-read-max":0,"iops-write-max":0,"iops-size":0}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit12","limits":{"bps-total":0,"bps-read":7000,"bps-write":7000,"iops-total":7000,"iops-read":0,"iops-write":0,"bps-total-max":0,"bps-read-max":0,"bps-write-max":0,"iops-total-max":0,"iops-read-max":0,"iops-write-max":0,"iops-size":0}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit3","limits":{"bps-total":0,"bps-read":8000,"bps-write":8000,"iops-total":8000,"iops-read":0,"iops-write":0,"bps-total-max":0,"bps-read-max":0,"bps-write-max":0,"iops-total-max":0,"iops-read-max":0,"iops-write-max":0,"iops-size":0}}' \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot strict=on \ +-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \ +-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-8-storage","auto-read-only":true,"discard":"unmap","cache":{"direct":true,"no-flush":false}}' \ +-blockdev '{"node-name":"libvirt-8-format","read-only":false,"cache":{"direct":true,"no-flush":false},"driver":"qcow2","file":"libvirt-8-storage"}' \ +-blockdev '{"driver":"throttle","node-name":"libvirt-9-filter","throttle-group":"throttle-limit0","file":"libvirt-8-format"}' \ +-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x4","drive":"libvirt-9-filter","id":"virtio-disk0","bootindex":1,"write-cache":"on"}' \ +-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest2","node-name":"libvirt-5-storage","auto-read-only":true,"discard":"unmap","cache":{"direct":true,"no-flush":false}}' \ +-blockdev '{"node-name":"libvirt-5-format","read-only":false,"cache":{"direct":true,"no-flush":false},"driver":"qcow2","file":"libvirt-5-storage"}' \ +-blockdev '{"driver":"throttle","node-name":"libvirt-6-filter","throttle-group":"throttle-limit1","file":"libvirt-5-format"}' \ +-blockdev '{"driver":"throttle","node-name":"libvirt-7-filter","throttle-group":"throttle-limit12","file":"libvirt-6-filter"}' \ +-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x5","drive":"libvirt-7-filter","id":"virtio-disk1","write-cache":"on"}' \ +-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest3","node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap","cache":{"direct":true,"no-flush":false}}' \ +-blockdev '{"node-name":"libvirt-2-format","read-only":false,"cache":{"direct":true,"no-flush":false},"driver":"qcow2","file":"libvirt-2-storage"}' \ +-blockdev '{"driver":"throttle","node-name":"libvirt-3-filter","throttle-group":"throttle-limit2","file":"libvirt-2-format"}' \ +-blockdev '{"driver":"throttle","node-name":"libvirt-4-filter","throttle-group":"throttle-limit12","file":"libvirt-3-filter"}' \ +-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x6","drive":"libvirt-4-filter","id":"virtio-disk2","write-cache":"on"}' \ +-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest4","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap","cache":{"direct":true,"no-flush":false}}' \ +-blockdev '{"node-name":"libvirt-1-format","read-only":false,"cache":{"direct":true,"no-flush":false},"driver":"qcow2","file":"libvirt-1-storage"}' \ +-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x7","drive":"libvirt-1-format","id":"virtio-disk3","write-cache":"on"}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-device '{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x2"}' \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxmlconfdata/throttlefilter.x86_64-latest.xml b/tests/qemuxmlconfdata/throttlefilter.x86_64-latest.xml new file mode 100644 index 0000000000..32d57f1723 --- /dev/null +++ b/tests/qemuxmlconfdata/throttlefilter.x86_64-latest.xml @@ -0,0 +1,105 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <throttlegroups> + <throttlegroup> + <total_bytes_sec>4000</total_bytes_sec> + <total_iops_sec>4000</total_iops_sec> + <group_name>limit0</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>5000</read_bytes_sec> + <write_bytes_sec>5000</write_bytes_sec> + <total_iops_sec>5000</total_iops_sec> + <group_name>limit1</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>6000</read_bytes_sec> + <write_bytes_sec>6000</write_bytes_sec> + <total_iops_sec>6000</total_iops_sec> + <group_name>limit2</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>7000</read_bytes_sec> + <write_bytes_sec>7000</write_bytes_sec> + <total_iops_sec>7000</total_iops_sec> + <group_name>limit12</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>8000</read_bytes_sec> + <write_bytes_sec>8000</write_bytes_sec> + <total_iops_sec>8000</total_iops_sec> + <group_name>limit3</group_name> + </throttlegroup> + </throttlegroups> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='vda' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit0'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest2'/> + <target dev='vdb' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit1'/> + <throttlefilter group='limit12'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest3'/> + <target dev='vdc' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit2'/> + <throttlefilter group='limit12'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest4'/> + <target dev='vdd' bus='virtio'/> + <iotune> + <total_bytes_sec>10000000</total_bytes_sec> + <read_iops_sec>400000</read_iops_sec> + <write_iops_sec>100000</write_iops_sec> + </iotune> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxmlconfdata/throttlefilter.xml b/tests/qemuxmlconfdata/throttlefilter.xml new file mode 100644 index 0000000000..d7db766755 --- /dev/null +++ b/tests/qemuxmlconfdata/throttlefilter.xml @@ -0,0 +1,95 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <throttlegroups> + <throttlegroup> + <total_bytes_sec>4000</total_bytes_sec> + <total_iops_sec>4000</total_iops_sec> + <group_name>limit0</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>5000</read_bytes_sec> + <write_bytes_sec>5000</write_bytes_sec> + <total_iops_sec>5000</total_iops_sec> + <group_name>limit1</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>6000</read_bytes_sec> + <write_bytes_sec>6000</write_bytes_sec> + <total_iops_sec>6000</total_iops_sec> + <group_name>limit2</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>7000</read_bytes_sec> + <write_bytes_sec>7000</write_bytes_sec> + <total_iops_sec>7000</total_iops_sec> + <group_name>limit12</group_name> + </throttlegroup> + <throttlegroup> + <read_bytes_sec>8000</read_bytes_sec> + <write_bytes_sec>8000</write_bytes_sec> + <total_iops_sec>8000</total_iops_sec> + <group_name>limit3</group_name> + </throttlegroup> + </throttlegroups> + <os> + <type arch='x86_64' 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-system-x86_64</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='vda' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit0'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest2'/> + <target dev='vdb' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit1'/> + <throttlefilter group='limit12'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest3'/> + <target dev='vdc' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit2'/> + <throttlefilter group='limit12'/> + </throttlefilters> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + </disk> + <disk type='block' device='disk'> + <driver name='qemu' type='qcow2' cache='none'/> + <source dev='/dev/HostVG/QEMUGuest4'/> + <target dev='vdd' bus='virtio'/> + <iotune> + <total_bytes_sec>10000000</total_bytes_sec> + <read_iops_sec>400000</read_iops_sec> + <write_iops_sec>100000</write_iops_sec> + </iotune> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index c271170d25..490a34788f 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -2360,6 +2360,8 @@ mymain(void) DO_TEST_CAPS_LATEST("blkdeviotune-max"); DO_TEST_CAPS_LATEST("blkdeviotune-group-num"); DO_TEST_CAPS_LATEST("blkdeviotune-max-length"); + DO_TEST_CAPS_LATEST("throttlefilter"); + DO_TEST_CAPS_LATEST_PARSE_ERROR("throttlefilter-invalid"); DO_TEST_CAPS_LATEST("multifunction-pci-device"); -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:17 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
* Add tests for throttlegroup domain xml processing, including groups referenced and not referenced by filters * Add tests for throttlefilter domain xml processing, including throttle group referenced by different disks * Add negative test case to report error when iotune is configured together with throttle filters
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Isolate domain xml test
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- .../throttlefilter-invalid.x86_64-latest.err | 1 + .../throttlefilter-invalid.xml | 89 +++++++++++++++ .../throttlefilter.x86_64-latest.args | 55 +++++++++ .../throttlefilter.x86_64-latest.xml | 105 ++++++++++++++++++ tests/qemuxmlconfdata/throttlefilter.xml | 95 ++++++++++++++++ tests/qemuxmlconftest.c | 2 + 6 files changed, 347 insertions(+) create mode 100644 tests/qemuxmlconfdata/throttlefilter-invalid.x86_64-latest.err create mode 100644 tests/qemuxmlconfdata/throttlefilter-invalid.xml create mode 100644 tests/qemuxmlconfdata/throttlefilter.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/throttlefilter.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/throttlefilter.xml
One of the test cases ought to test also the case when copy-on-read is enabled.

* Add tests for throttlefilter nodename parse and format for statusxml (disk/privateData/nodenames/nodename with type='throttle-filter') * Add iotune limited disk tests to make sure iotune refactory works Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Isolate status xml test Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- .../throttlefilter-in.xml | 392 +++++++++++++++++ .../throttlefilter-out.xml | 393 ++++++++++++++++++ tests/qemuxmlactivetest.c | 1 + 3 files changed, 786 insertions(+) create mode 100644 tests/qemustatusxml2xmldata/throttlefilter-in.xml create mode 100644 tests/qemustatusxml2xmldata/throttlefilter-out.xml diff --git a/tests/qemustatusxml2xmldata/throttlefilter-in.xml b/tests/qemustatusxml2xmldata/throttlefilter-in.xml new file mode 100644 index 0000000000..2a98d6f5e2 --- /dev/null +++ b/tests/qemustatusxml2xmldata/throttlefilter-in.xml @@ -0,0 +1,392 @@ +<domstatus state='running' reason='booted' pid='7690'> + <taint flag='high-privileges'/> + <monitor path='/var/lib/libvirt/qemu/domain-4-copy/monitor.sock' type='unix'/> + <namespaces> + <mount/> + </namespaces> + <vcpus> + <vcpu id='0' pid='7696'/> + </vcpus> + <qemuCaps> + <flag name='kvm'/> + <flag name='no-hpet'/> + <flag name='spice'/> + <flag name='hda-duplex'/> + <flag name='ccid-emulated'/> + <flag name='ccid-passthru'/> + <flag name='virtio-tx-alg'/> + <flag name='virtio-blk-pci.ioeventfd'/> + <flag name='sga'/> + <flag name='virtio-blk-pci.event_idx'/> + <flag name='virtio-net-pci.event_idx'/> + <flag name='piix3-usb-uhci'/> + <flag name='piix4-usb-uhci'/> + <flag name='usb-ehci'/> + <flag name='ich9-usb-ehci1'/> + <flag name='vt82c686b-usb-uhci'/> + <flag name='pci-ohci'/> + <flag name='usb-redir'/> + <flag name='usb-hub'/> + <flag name='ich9-ahci'/> + <flag name='no-acpi'/> + <flag name='virtio-blk-pci.scsi'/> + <flag name='scsi-disk.channel'/> + <flag name='scsi-block'/> + <flag name='transaction'/> + <flag name='block-job-async'/> + <flag name='scsi-cd'/> + <flag name='ide-cd'/> + <flag name='hda-micro'/> + <flag name='dump-guest-memory'/> + <flag name='nec-usb-xhci'/> + <flag name='balloon-event'/> + <flag name='lsi'/> + <flag name='virtio-scsi-pci'/> + <flag name='blockio'/> + <flag name='disable-s3'/> + <flag name='disable-s4'/> + <flag name='usb-redir.filter'/> + <flag name='ide-drive.wwn'/> + <flag name='scsi-disk.wwn'/> + <flag name='seccomp-sandbox'/> + <flag name='reboot-timeout'/> + <flag name='seamless-migration'/> + <flag name='block-commit'/> + <flag name='vnc'/> + <flag name='drive-mirror'/> + <flag name='blockdev-snapshot-sync'/> + <flag name='qxl'/> + <flag name='VGA'/> + <flag name='cirrus-vga'/> + <flag name='vmware-svga'/> + <flag name='device-video-primary'/> + <flag name='usb-serial'/> + <flag name='nbd-server'/> + <flag name='virtio-rng'/> + <flag name='rng-random'/> + <flag name='rng-egd'/> + <flag name='megasas'/> + <flag name='tpm-passthrough'/> + <flag name='tpm-tis'/> + <flag name='pci-bridge'/> + <flag name='vfio-pci'/> + <flag name='mem-merge'/> + <flag name='drive-discard'/> + <flag name='mlock'/> + <flag name='device-del-event'/> + <flag name='dmi-to-pci-bridge'/> + <flag name='i440fx-pci-hole64-size'/> + <flag name='q35-pci-hole64-size'/> + <flag name='usb-storage'/> + <flag name='usb-storage.removable'/> + <flag name='ich9-intel-hda'/> + <flag name='kvm-pit-lost-tick-policy'/> + <flag name='boot-strict'/> + <flag name='pvpanic'/> + <flag name='spice-file-xfer-disable'/> + <flag name='usb-kbd'/> + <flag name='msg-timestamp'/> + <flag name='active-commit'/> + <flag name='change-backing-file'/> + <flag name='memory-backend-ram'/> + <flag name='numa'/> + <flag name='memory-backend-file'/> + <flag name='usb-audio'/> + <flag name='rtc-reset-reinjection'/> + <flag name='splash-timeout'/> + <flag name='iothread'/> + <flag name='migrate-rdma'/> + <flag name='ivshmem'/> + <flag name='drive-iotune-max'/> + <flag name='VGA.vgamem_mb'/> + <flag name='vmware-svga.vgamem_mb'/> + <flag name='qxl.vgamem_mb'/> + <flag name='pc-dimm'/> + <flag name='machine-vmport-opt'/> + <flag name='aes-key-wrap'/> + <flag name='dea-key-wrap'/> + <flag name='pci-serial'/> + <flag name='vhost-user-multiqueue'/> + <flag name='migration-event'/> + <flag name='ioh3420'/> + <flag name='x3130-upstream'/> + <flag name='xio3130-downstream'/> + <flag name='rtl8139'/> + <flag name='e1000'/> + <flag name='virtio-net'/> + <flag name='gic-version'/> + <flag name='incoming-defer'/> + <flag name='virtio-gpu'/> + <flag name='virtio-gpu.virgl'/> + <flag name='virtio-keyboard'/> + <flag name='virtio-mouse'/> + <flag name='virtio-tablet'/> + <flag name='virtio-input-host'/> + <flag name='chardev-file-append'/> + <flag name='ich9-disable-s3'/> + <flag name='ich9-disable-s4'/> + <flag name='vserport-change-event'/> + <flag name='virtio-balloon-pci.deflate-on-oom'/> + <flag name='mptsas1068'/> + <flag name='spice-gl'/> + <flag name='qxl.vram64_size_mb'/> + <flag name='chardev-logfile'/> + <flag name='debug-threads'/> + <flag name='secret'/> + <flag name='pxb'/> + <flag name='pxb-pcie'/> + <flag name='device-tray-moved-event'/> + <flag name='nec-usb-xhci-ports'/> + <flag name='virtio-scsi-pci.iothread'/> + <flag name='name-guest'/> + <flag name='qxl.max_outputs'/> + <flag name='spice-unix'/> + <flag name='drive-detect-zeroes'/> + <flag name='tls-creds-x509'/> + <flag name='intel-iommu'/> + <flag name='smm'/> + <flag name='virtio-pci-disable-legacy'/> + <flag name='query-hotpluggable-cpus'/> + <flag name='virtio-net.rx_queue_size'/> + <flag name='virtio-vga'/> + <flag name='drive-iotune-max-length'/> + <flag name='ivshmem-plain'/> + <flag name='ivshmem-doorbell'/> + <flag name='query-qmp-schema'/> + <flag name='gluster.debug_level'/> + <flag name='vhost-scsi'/> + <flag name='drive-iotune-group'/> + <flag name='query-cpu-model-expansion'/> + <flag name='virtio-net.host_mtu'/> + <flag name='spice-rendernode'/> + <flag name='nvdimm'/> + <flag name='pcie-root-port'/> + <flag name='query-cpu-definitions'/> + <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> + <flag name='cpu-cache'/> + <flag name='qemu-xhci'/> + <flag name='kernel-irqchip'/> + <flag name='kernel-irqchip.split'/> + <flag name='intel-iommu.intremap'/> + <flag name='intel-iommu.caching-mode'/> + <flag name='intel-iommu.eim'/> + <flag name='intel-iommu.device-iotlb'/> + <flag name='virtio.iommu_platform'/> + <flag name='virtio.ats'/> + <flag name='loadparm'/> + <flag name='vnc-multi-servers'/> + <flag name='virtio-net.tx_queue_size'/> + <flag name='chardev-reconnect'/> + <flag name='virtio-gpu.max_outputs'/> + <flag name='vxhs'/> + <flag name='virtio-blk.num-queues'/> + <flag name='vmcoreinfo'/> + <flag name='numa.dist'/> + <flag name='disk-share-rw'/> + <flag name='iscsi.password-secret'/> + <flag name='isa-serial'/> + <flag name='dump-completed'/> + <flag name='qcow2-luks'/> + <flag name='pcie-pci-bridge'/> + <flag name='seccomp-blacklist'/> + <flag name='query-cpus-fast'/> + <flag name='disk-write-cache'/> + <flag name='nbd-tls'/> + <flag name='tpm-crb'/> + <flag name='pr-manager-helper'/> + <flag name='qom-list-properties'/> + <flag name='memory-backend-file.discard-data'/> + <flag name='sdl-gl'/> + <flag name='screendump_device'/> + <flag name='hda-output'/> + <flag name='blockdev-del'/> + <flag name='vmgenid'/> + <flag name='vhost-vsock'/> + <flag name='chardev-fd-pass'/> + <flag name='tpm-emulator'/> + <flag name='mch'/> + <flag name='mch.extended-tseg-mbytes'/> + <flag name='usb-storage.werror'/> + <flag name='egl-headless'/> + <flag name='vfio-pci.display'/> + <flag name='blockdev'/> + <flag name='memory-backend-memfd'/> + <flag name='memory-backend-memfd.hugetlb'/> + <flag name='iothread.poll-max-ns'/> + <flag name='egl-headless.rendernode'/> + <flag name='incremental-backup'/> + </qemuCaps> + <devices> + <device alias='rng0'/> + <device alias='sound0-codec0'/> + <device alias='virtio-disk0'/> + <device alias='virtio-serial0'/> + <device alias='video0'/> + <device alias='serial0'/> + <device alias='sound0'/> + <device alias='channel1'/> + <device alias='channel0'/> + <device alias='usb'/> + </devices> + <libDir path='/var/lib/libvirt/qemu/domain-4-copy'/> + <channelTargetDir path='/var/lib/libvirt/qemu/channel/target/domain-4-copy'/> + <chardevStdioLogd/> + <allowReboot value='yes'/> + <nodename index='0'/> + <fdset index='0'/> + <blockjobs active='no'/> + <agentTimeout>-2</agentTimeout> + <domain type='kvm' id='4'> + <name>copy</name> + <uuid>0439a4a8-db56-4933-9183-d8681d7b0746</uuid> + <memory unit='KiB'>1024000</memory> + <currentMemory unit='KiB'>1024000</currentMemory> + <vcpu placement='static'>1</vcpu> + <throttlegroups> + <throttlegroup> + <total_iops_sec>200</total_iops_sec> + <total_iops_sec_max>200</total_iops_sec_max> + <group_name>limit0</group_name> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + </throttlegroup> + <throttlegroup> + <total_iops_sec>250</total_iops_sec> + <total_iops_sec_max>250</total_iops_sec_max> + <group_name>limit1</group_name> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + </throttlegroup> + <throttlegroup> + <total_iops_sec>300</total_iops_sec> + <total_iops_sec_max>300</total_iops_sec_max> + <group_name>limit2</group_name> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + </throttlegroup> + <throttlegroup> + <total_iops_sec>400</total_iops_sec> + <total_iops_sec_max>400</total_iops_sec_max> + <group_name>limit012</group_name> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + </throttlegroup> + </throttlegroups> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64' machine='pc-i440fx-2.9'>hvm</type> + <boot dev='hd'/> + <bootmenu enable='yes'/> + </os> + <features> + <acpi/> + <apic/> + <vmport state='off'/> + </features> + <clock offset='utc'> + <timer name='rtc' tickpolicy='catchup'/> + <timer name='pit' tickpolicy='delay'/> + <timer name='hpet' present='no'/> + </clock> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <pm> + <suspend-to-mem enabled='no'/> + <suspend-to-disk enabled='no'/> + </pm> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/pull4.qcow2' index='3'> + <privateData> + <nodenames> + <nodename type='storage' name='libvirt-3-storage'/> + <nodename type='format' name='libvirt-3-format'/> + </nodenames> + </privateData> + </source> + <target dev='vda' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit0'/> + <throttlefilter group='limit012'/> + </throttlefilters> + <alias name='virtio-disk1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/> + <privateData> + <qom name='/machine/peripheral/virtio-disk1/virtio-backend'/> + <nodenames> + <nodename type='throttle-filter' name='libvirt-4-filter' group='limit0'/> + <nodename type='throttle-filter' name='libvirt-5-filter' group='limit012'/> + </nodenames> + </privateData> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/pull5.qcow2' index='4'> + <privateData> + <nodenames> + <nodename type='storage' name='libvirt-4-storage'/> + <nodename type='format' name='libvirt-4-format'/> + </nodenames> + </privateData> + </source> + <target dev='vda' bus='virtio'/> + <iotune> + <total_bytes_sec>10000000</total_bytes_sec> + <read_iops_sec>400000</read_iops_sec> + <write_iops_sec>100000</write_iops_sec> + </iotune> + <alias name='virtio-disk2'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0c' function='0x0'/> + <privateData> + <qom name='/machine/peripheral/virtio-disk2/virtio-backend'/> + </privateData> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci.0'/> + </controller> + <serial type='pty'> + <source path='/dev/pts/34'/> + <target type='isa-serial' port='0'> + <model name='isa-serial'/> + </target> + <alias name='serial0'/> + </serial> + <console type='pty' tty='/dev/pts/34'> + <source path='/dev/pts/34'/> + <target type='serial' port='0'/> + <alias name='serial0'/> + </console> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'> + <listen type='address' address='127.0.0.1' fromConfig='1' autoGenerated='no'/> + <image compression='off'/> + </graphics> + <audio id='1' type='spice'/> + <video> + <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/> + <alias name='video0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + <memballoon model='none'/> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <label>unconfined_u:unconfined_r:svirt_t:s0:c550,c786</label> + <imagelabel>unconfined_u:object_r:svirt_image_t:s0:c550,c786</imagelabel> + </seclabel> + <seclabel type='dynamic' model='dac' relabel='yes'> + <label>+0:+0</label> + <imagelabel>+0:+0</imagelabel> + </seclabel> + </domain> +</domstatus> diff --git a/tests/qemustatusxml2xmldata/throttlefilter-out.xml b/tests/qemustatusxml2xmldata/throttlefilter-out.xml new file mode 100644 index 0000000000..8751a42cce --- /dev/null +++ b/tests/qemustatusxml2xmldata/throttlefilter-out.xml @@ -0,0 +1,393 @@ +<domstatus state='running' reason='booted' pid='7690'> + <taint flag='high-privileges'/> + <monitor path='/var/lib/libvirt/qemu/domain-4-copy/monitor.sock' type='unix'/> + <namespaces> + <mount/> + </namespaces> + <vcpus> + <vcpu id='0' pid='7696'/> + </vcpus> + <qemuCaps> + <flag name='kvm'/> + <flag name='no-hpet'/> + <flag name='spice'/> + <flag name='hda-duplex'/> + <flag name='ccid-emulated'/> + <flag name='ccid-passthru'/> + <flag name='virtio-tx-alg'/> + <flag name='virtio-blk-pci.ioeventfd'/> + <flag name='sga'/> + <flag name='virtio-blk-pci.event_idx'/> + <flag name='virtio-net-pci.event_idx'/> + <flag name='piix3-usb-uhci'/> + <flag name='piix4-usb-uhci'/> + <flag name='usb-ehci'/> + <flag name='ich9-usb-ehci1'/> + <flag name='vt82c686b-usb-uhci'/> + <flag name='pci-ohci'/> + <flag name='usb-redir'/> + <flag name='usb-hub'/> + <flag name='ich9-ahci'/> + <flag name='no-acpi'/> + <flag name='virtio-blk-pci.scsi'/> + <flag name='scsi-disk.channel'/> + <flag name='scsi-block'/> + <flag name='transaction'/> + <flag name='block-job-async'/> + <flag name='scsi-cd'/> + <flag name='ide-cd'/> + <flag name='hda-micro'/> + <flag name='dump-guest-memory'/> + <flag name='nec-usb-xhci'/> + <flag name='balloon-event'/> + <flag name='lsi'/> + <flag name='virtio-scsi-pci'/> + <flag name='blockio'/> + <flag name='disable-s3'/> + <flag name='disable-s4'/> + <flag name='usb-redir.filter'/> + <flag name='ide-drive.wwn'/> + <flag name='scsi-disk.wwn'/> + <flag name='seccomp-sandbox'/> + <flag name='reboot-timeout'/> + <flag name='seamless-migration'/> + <flag name='block-commit'/> + <flag name='vnc'/> + <flag name='drive-mirror'/> + <flag name='blockdev-snapshot-sync'/> + <flag name='qxl'/> + <flag name='VGA'/> + <flag name='cirrus-vga'/> + <flag name='vmware-svga'/> + <flag name='device-video-primary'/> + <flag name='usb-serial'/> + <flag name='nbd-server'/> + <flag name='virtio-rng'/> + <flag name='rng-random'/> + <flag name='rng-egd'/> + <flag name='megasas'/> + <flag name='tpm-passthrough'/> + <flag name='tpm-tis'/> + <flag name='pci-bridge'/> + <flag name='vfio-pci'/> + <flag name='mem-merge'/> + <flag name='drive-discard'/> + <flag name='mlock'/> + <flag name='device-del-event'/> + <flag name='dmi-to-pci-bridge'/> + <flag name='i440fx-pci-hole64-size'/> + <flag name='q35-pci-hole64-size'/> + <flag name='usb-storage'/> + <flag name='usb-storage.removable'/> + <flag name='ich9-intel-hda'/> + <flag name='kvm-pit-lost-tick-policy'/> + <flag name='boot-strict'/> + <flag name='pvpanic'/> + <flag name='spice-file-xfer-disable'/> + <flag name='usb-kbd'/> + <flag name='msg-timestamp'/> + <flag name='active-commit'/> + <flag name='change-backing-file'/> + <flag name='memory-backend-ram'/> + <flag name='numa'/> + <flag name='memory-backend-file'/> + <flag name='usb-audio'/> + <flag name='rtc-reset-reinjection'/> + <flag name='splash-timeout'/> + <flag name='iothread'/> + <flag name='migrate-rdma'/> + <flag name='ivshmem'/> + <flag name='drive-iotune-max'/> + <flag name='VGA.vgamem_mb'/> + <flag name='vmware-svga.vgamem_mb'/> + <flag name='qxl.vgamem_mb'/> + <flag name='pc-dimm'/> + <flag name='machine-vmport-opt'/> + <flag name='aes-key-wrap'/> + <flag name='dea-key-wrap'/> + <flag name='pci-serial'/> + <flag name='vhost-user-multiqueue'/> + <flag name='migration-event'/> + <flag name='ioh3420'/> + <flag name='x3130-upstream'/> + <flag name='xio3130-downstream'/> + <flag name='rtl8139'/> + <flag name='e1000'/> + <flag name='virtio-net'/> + <flag name='gic-version'/> + <flag name='incoming-defer'/> + <flag name='virtio-gpu'/> + <flag name='virtio-gpu.virgl'/> + <flag name='virtio-keyboard'/> + <flag name='virtio-mouse'/> + <flag name='virtio-tablet'/> + <flag name='virtio-input-host'/> + <flag name='chardev-file-append'/> + <flag name='ich9-disable-s3'/> + <flag name='ich9-disable-s4'/> + <flag name='vserport-change-event'/> + <flag name='virtio-balloon-pci.deflate-on-oom'/> + <flag name='mptsas1068'/> + <flag name='spice-gl'/> + <flag name='qxl.vram64_size_mb'/> + <flag name='chardev-logfile'/> + <flag name='debug-threads'/> + <flag name='secret'/> + <flag name='pxb'/> + <flag name='pxb-pcie'/> + <flag name='device-tray-moved-event'/> + <flag name='nec-usb-xhci-ports'/> + <flag name='virtio-scsi-pci.iothread'/> + <flag name='name-guest'/> + <flag name='qxl.max_outputs'/> + <flag name='spice-unix'/> + <flag name='drive-detect-zeroes'/> + <flag name='tls-creds-x509'/> + <flag name='intel-iommu'/> + <flag name='smm'/> + <flag name='virtio-pci-disable-legacy'/> + <flag name='query-hotpluggable-cpus'/> + <flag name='virtio-net.rx_queue_size'/> + <flag name='virtio-vga'/> + <flag name='drive-iotune-max-length'/> + <flag name='ivshmem-plain'/> + <flag name='ivshmem-doorbell'/> + <flag name='query-qmp-schema'/> + <flag name='gluster.debug_level'/> + <flag name='vhost-scsi'/> + <flag name='drive-iotune-group'/> + <flag name='query-cpu-model-expansion'/> + <flag name='virtio-net.host_mtu'/> + <flag name='spice-rendernode'/> + <flag name='nvdimm'/> + <flag name='pcie-root-port'/> + <flag name='query-cpu-definitions'/> + <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> + <flag name='cpu-cache'/> + <flag name='qemu-xhci'/> + <flag name='kernel-irqchip'/> + <flag name='kernel-irqchip.split'/> + <flag name='intel-iommu.intremap'/> + <flag name='intel-iommu.caching-mode'/> + <flag name='intel-iommu.eim'/> + <flag name='intel-iommu.device-iotlb'/> + <flag name='virtio.iommu_platform'/> + <flag name='virtio.ats'/> + <flag name='loadparm'/> + <flag name='vnc-multi-servers'/> + <flag name='virtio-net.tx_queue_size'/> + <flag name='chardev-reconnect'/> + <flag name='virtio-gpu.max_outputs'/> + <flag name='vxhs'/> + <flag name='virtio-blk.num-queues'/> + <flag name='vmcoreinfo'/> + <flag name='numa.dist'/> + <flag name='disk-share-rw'/> + <flag name='iscsi.password-secret'/> + <flag name='isa-serial'/> + <flag name='dump-completed'/> + <flag name='qcow2-luks'/> + <flag name='pcie-pci-bridge'/> + <flag name='seccomp-blacklist'/> + <flag name='query-cpus-fast'/> + <flag name='disk-write-cache'/> + <flag name='nbd-tls'/> + <flag name='tpm-crb'/> + <flag name='pr-manager-helper'/> + <flag name='qom-list-properties'/> + <flag name='memory-backend-file.discard-data'/> + <flag name='sdl-gl'/> + <flag name='screendump_device'/> + <flag name='hda-output'/> + <flag name='blockdev-del'/> + <flag name='vmgenid'/> + <flag name='vhost-vsock'/> + <flag name='chardev-fd-pass'/> + <flag name='tpm-emulator'/> + <flag name='mch'/> + <flag name='mch.extended-tseg-mbytes'/> + <flag name='usb-storage.werror'/> + <flag name='egl-headless'/> + <flag name='vfio-pci.display'/> + <flag name='blockdev'/> + <flag name='memory-backend-memfd'/> + <flag name='memory-backend-memfd.hugetlb'/> + <flag name='iothread.poll-max-ns'/> + <flag name='egl-headless.rendernode'/> + <flag name='incremental-backup'/> + </qemuCaps> + <devices> + <device alias='rng0'/> + <device alias='sound0-codec0'/> + <device alias='virtio-disk0'/> + <device alias='virtio-serial0'/> + <device alias='video0'/> + <device alias='serial0'/> + <device alias='sound0'/> + <device alias='channel1'/> + <device alias='channel0'/> + <device alias='usb'/> + </devices> + <libDir path='/var/lib/libvirt/qemu/domain-4-copy'/> + <channelTargetDir path='/var/lib/libvirt/qemu/channel/target/domain-4-copy'/> + <memoryBackingDir path='/var/lib/libvirt/qemu/ram/4-copy'/> + <chardevStdioLogd/> + <allowReboot value='yes'/> + <nodename index='0'/> + <fdset index='0'/> + <blockjobs active='no'/> + <agentTimeout>-2</agentTimeout> + <domain type='kvm' id='4'> + <name>copy</name> + <uuid>0439a4a8-db56-4933-9183-d8681d7b0746</uuid> + <memory unit='KiB'>1024000</memory> + <currentMemory unit='KiB'>1024000</currentMemory> + <vcpu placement='static'>1</vcpu> + <throttlegroups> + <throttlegroup> + <total_iops_sec>200</total_iops_sec> + <total_iops_sec_max>200</total_iops_sec_max> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + <group_name>limit0</group_name> + </throttlegroup> + <throttlegroup> + <total_iops_sec>250</total_iops_sec> + <total_iops_sec_max>250</total_iops_sec_max> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + <group_name>limit1</group_name> + </throttlegroup> + <throttlegroup> + <total_iops_sec>300</total_iops_sec> + <total_iops_sec_max>300</total_iops_sec_max> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + <group_name>limit2</group_name> + </throttlegroup> + <throttlegroup> + <total_iops_sec>400</total_iops_sec> + <total_iops_sec_max>400</total_iops_sec_max> + <total_iops_sec_max_length>1</total_iops_sec_max_length> + <group_name>limit012</group_name> + </throttlegroup> + </throttlegroups> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64' machine='pc-i440fx-2.9'>hvm</type> + <boot dev='hd'/> + <bootmenu enable='yes'/> + </os> + <features> + <acpi/> + <apic/> + <vmport state='off'/> + </features> + <clock offset='utc'> + <timer name='rtc' tickpolicy='catchup'/> + <timer name='pit' tickpolicy='delay'/> + <timer name='hpet' present='no'/> + </clock> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <pm> + <suspend-to-mem enabled='no'/> + <suspend-to-disk enabled='no'/> + </pm> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/pull4.qcow2' index='3'> + <privateData> + <nodenames> + <nodename type='storage' name='libvirt-3-storage'/> + <nodename type='format' name='libvirt-3-format'/> + </nodenames> + </privateData> + </source> + <target dev='vda' bus='virtio'/> + <throttlefilters> + <throttlefilter group='limit0'/> + <throttlefilter group='limit012'/> + </throttlefilters> + <alias name='virtio-disk1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/> + <privateData> + <qom name='/machine/peripheral/virtio-disk1/virtio-backend'/> + <nodenames> + <nodename type='throttle-filter' name='libvirt-4-filter' group='limit0'/> + <nodename type='throttle-filter' name='libvirt-5-filter' group='limit012'/> + </nodenames> + </privateData> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/pull5.qcow2' index='4'> + <privateData> + <nodenames> + <nodename type='storage' name='libvirt-4-storage'/> + <nodename type='format' name='libvirt-4-format'/> + </nodenames> + </privateData> + </source> + <target dev='vda' bus='virtio'/> + <iotune> + <total_bytes_sec>10000000</total_bytes_sec> + <read_iops_sec>400000</read_iops_sec> + <write_iops_sec>100000</write_iops_sec> + </iotune> + <alias name='virtio-disk2'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0c' function='0x0'/> + <privateData> + <qom name='/machine/peripheral/virtio-disk2/virtio-backend'/> + </privateData> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci.0'/> + </controller> + <serial type='pty'> + <source path='/dev/pts/34'/> + <target type='isa-serial' port='0'> + <model name='isa-serial'/> + </target> + <alias name='serial0'/> + </serial> + <console type='pty' tty='/dev/pts/34'> + <source path='/dev/pts/34'/> + <target type='serial' port='0'/> + <alias name='serial0'/> + </console> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'> + <listen type='address' address='127.0.0.1' fromConfig='1' autoGenerated='no'/> + <image compression='off'/> + </graphics> + <audio id='1' type='spice'/> + <video> + <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/> + <alias name='video0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + <memballoon model='none'/> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <label>unconfined_u:unconfined_r:svirt_t:s0:c550,c786</label> + <imagelabel>unconfined_u:object_r:svirt_image_t:s0:c550,c786</imagelabel> + </seclabel> + <seclabel type='dynamic' model='dac' relabel='yes'> + <label>+0:+0</label> + <imagelabel>+0:+0</imagelabel> + </seclabel> + </domain> +</domstatus> diff --git a/tests/qemuxmlactivetest.c b/tests/qemuxmlactivetest.c index 8cf44090ce..b132b91623 100644 --- a/tests/qemuxmlactivetest.c +++ b/tests/qemuxmlactivetest.c @@ -247,6 +247,7 @@ mymain(void) DO_TEST_STATUS("blockjob-blockdev"); DO_TEST_STATUS("backup-pull"); + DO_TEST_STATUS("throttlefilter"); DO_TEST_STATUS("memory-backing-dir"); -- 2.39.5 (Apple Git-154)

From: Chun Feng Wu <danielwuwy@163.com> Test throttle group APIs * Extract common methods for both "testDomainSetThrottleGroup" and "testDomainSetBlockIoTune": testDomainValidateBlockIoTune, testDomainSetBlockIoTuneFields, testDomainCheckBlockIoTuneMutualExclusion, testDomainCheckBlockIoTuneMax * Test "Set": testDomainSetThrottleGroup * Test "Get": testDomainGetThrottleGroup * Test "Del": testDomainDelThrottleGroup Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Cleanup Test "Get": testDomainGetThrottleGroup * Update the version to 11.1.0. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/test/test_driver.c | 367 +++++++++++++++++++++++++++-------------- 1 file changed, 245 insertions(+), 122 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 6f18b2b2c8..72eab99184 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -3783,25 +3783,8 @@ testDomainGetInterfaceParameters(virDomainPtr dom, #define TEST_BLOCK_IOTUNE_MAX 1000000000000000LL static int -testDomainSetBlockIoTune(virDomainPtr dom, - const char *path, - virTypedParameterPtr params, - int nparams, - unsigned int flags) +testDomainValidateBlockIoTune(virTypedParameterPtr params, int nparams) { - virDomainObj *vm = NULL; - virDomainDef *def = NULL; - virDomainBlockIoTuneInfo info = {0}; - virDomainDiskDef *conf_disk = NULL; - virTypedParameterPtr eventParams = NULL; - int eventNparams = 0; - int eventMaxparams = 0; - size_t i; - int ret = -1; - - virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | - VIR_DOMAIN_AFFECT_CONFIG, -1); - if (virTypedParamsValidate(params, nparams, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC, VIR_TYPED_PARAM_ULLONG, @@ -3846,34 +3829,29 @@ testDomainSetBlockIoTune(virDomainPtr dom, NULL) < 0) return -1; - if (!(vm = testDomObjFromDomain(dom))) - return -1; - - if (!(def = virDomainObjGetOneDef(vm, flags))) - goto cleanup; - - if (!(conf_disk = virDomainDiskByName(def, path, true))) { - virReportError(VIR_ERR_INVALID_ARG, - _("missing persistent configuration for disk '%1$s'"), - path); - goto cleanup; - } + return 0; +} - info = conf_disk->blkdeviotune; - info.group_name = g_strdup(conf_disk->blkdeviotune.group_name); - if (virTypedParamsAddString(&eventParams, &eventNparams, &eventMaxparams, - VIR_DOMAIN_TUNABLE_BLKDEV_DISK, path) < 0) - goto cleanup; +static int +testDomainSetBlockIoTuneFields(virDomainBlockIoTuneInfo *info, + virTypedParameterPtr params, + int nparams, + virTypedParameterPtr *eventParams, + int *eventNparams, + int *eventMaxparams) +{ + size_t i; + int ret = -1; -#define SET_IOTUNE_FIELD(FIELD, STR, TUNABLE_STR) \ - if (STREQ(param->field, STR)) { \ - info.FIELD = param->value.ul; \ - if (virTypedParamsAddULLong(&eventParams, &eventNparams, \ - &eventMaxparams, \ - TUNABLE_STR, \ +#define SET_IOTUNE_FIELD(FIELD, CONST) \ + if (STREQ(param->field, VIR_DOMAIN_BLOCK_IOTUNE_##CONST)) { \ + info->FIELD = param->value.ul; \ + if (virTypedParamsAddULLong(eventParams, eventNparams, \ + eventMaxparams, \ + VIR_DOMAIN_TUNABLE_BLKDEV_##CONST, \ param->value.ul) < 0) \ - goto cleanup; \ + goto endjob; \ continue; \ } @@ -3884,119 +3862,99 @@ testDomainSetBlockIoTune(virDomainPtr dom, virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("block I/O throttle limit value must be no more than %1$llu"), TEST_BLOCK_IOTUNE_MAX); - goto cleanup; + goto endjob; } - SET_IOTUNE_FIELD(total_bytes_sec, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC, - VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC); - SET_IOTUNE_FIELD(read_bytes_sec, - VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC, - VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC); - SET_IOTUNE_FIELD(write_bytes_sec, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC, - VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC); - SET_IOTUNE_FIELD(total_iops_sec, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC, - VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC); - SET_IOTUNE_FIELD(read_iops_sec, - VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC, - VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC); - SET_IOTUNE_FIELD(write_iops_sec, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC, - VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC); - - SET_IOTUNE_FIELD(total_bytes_sec_max, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX, - VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC_MAX); - SET_IOTUNE_FIELD(read_bytes_sec_max, - VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX, - VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC_MAX); - SET_IOTUNE_FIELD(write_bytes_sec_max, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX, - VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC_MAX); - SET_IOTUNE_FIELD(total_iops_sec_max, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX, - VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC_MAX); - SET_IOTUNE_FIELD(read_iops_sec_max, - VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX, - VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC_MAX); - SET_IOTUNE_FIELD(write_iops_sec_max, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX, - VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC_MAX); - SET_IOTUNE_FIELD(size_iops_sec, - VIR_DOMAIN_BLOCK_IOTUNE_SIZE_IOPS_SEC, - VIR_DOMAIN_TUNABLE_BLKDEV_SIZE_IOPS_SEC); - + SET_IOTUNE_FIELD(total_bytes_sec, TOTAL_BYTES_SEC); + SET_IOTUNE_FIELD(read_bytes_sec, READ_BYTES_SEC); + SET_IOTUNE_FIELD(write_bytes_sec, WRITE_BYTES_SEC); + SET_IOTUNE_FIELD(total_iops_sec, TOTAL_IOPS_SEC); + SET_IOTUNE_FIELD(read_iops_sec, READ_IOPS_SEC); + SET_IOTUNE_FIELD(write_iops_sec, WRITE_IOPS_SEC); + + SET_IOTUNE_FIELD(total_bytes_sec_max, TOTAL_BYTES_SEC_MAX); + SET_IOTUNE_FIELD(read_bytes_sec_max, READ_BYTES_SEC_MAX); + SET_IOTUNE_FIELD(write_bytes_sec_max, WRITE_BYTES_SEC_MAX); + SET_IOTUNE_FIELD(total_iops_sec_max, TOTAL_IOPS_SEC_MAX); + SET_IOTUNE_FIELD(read_iops_sec_max, READ_IOPS_SEC_MAX); + SET_IOTUNE_FIELD(write_iops_sec_max, WRITE_IOPS_SEC_MAX); + SET_IOTUNE_FIELD(size_iops_sec, SIZE_IOPS_SEC); + + /* NB: Cannot use macro since this is a value.s not a value.ul */ if (STREQ(param->field, VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME)) { - VIR_FREE(info.group_name); - info.group_name = g_strdup(param->value.s); - if (virTypedParamsAddString(&eventParams, - &eventNparams, - &eventMaxparams, + VIR_FREE(info->group_name); + info->group_name = g_strdup(param->value.s); + if (virTypedParamsAddString(eventParams, eventNparams, + eventMaxparams, VIR_DOMAIN_TUNABLE_BLKDEV_GROUP_NAME, param->value.s) < 0) - goto cleanup; + goto endjob; continue; } - SET_IOTUNE_FIELD(total_bytes_sec_max_length, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX_LENGTH, - VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC_MAX_LENGTH); - SET_IOTUNE_FIELD(read_bytes_sec_max_length, - VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX_LENGTH, - VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC_MAX_LENGTH); - SET_IOTUNE_FIELD(write_bytes_sec_max_length, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX_LENGTH, - VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC_MAX_LENGTH); - SET_IOTUNE_FIELD(total_iops_sec_max_length, - VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX_LENGTH, - VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC_MAX_LENGTH); - SET_IOTUNE_FIELD(read_iops_sec_max_length, - VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX_LENGTH, - VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC_MAX_LENGTH); - SET_IOTUNE_FIELD(write_iops_sec_max_length, - VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX_LENGTH, - VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(total_bytes_sec_max_length, TOTAL_BYTES_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(read_bytes_sec_max_length, READ_BYTES_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(write_bytes_sec_max_length, WRITE_BYTES_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(total_iops_sec_max_length, TOTAL_IOPS_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(read_iops_sec_max_length, READ_IOPS_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(write_iops_sec_max_length, WRITE_IOPS_SEC_MAX_LENGTH); } + #undef SET_IOTUNE_FIELD - if ((info.total_bytes_sec && info.read_bytes_sec) || - (info.total_bytes_sec && info.write_bytes_sec)) { + ret = 0; + endjob: + return ret; +} + + +static int +testDomainCheckBlockIoTuneMutualExclusion(virDomainBlockIoTuneInfo *info) +{ + if ((info->total_bytes_sec && info->read_bytes_sec) || + (info->total_bytes_sec && info->write_bytes_sec)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of bytes_sec cannot be set at the same time")); - goto cleanup; + return -1; } - if ((info.total_iops_sec && info.read_iops_sec) || - (info.total_iops_sec && info.write_iops_sec)) { + if ((info->total_iops_sec && info->read_iops_sec) || + (info->total_iops_sec && info->write_iops_sec)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of iops_sec cannot be set at the same time")); - goto cleanup; + return -1; } - if ((info.total_bytes_sec_max && info.read_bytes_sec_max) || - (info.total_bytes_sec_max && info.write_bytes_sec_max)) { + if ((info->total_bytes_sec_max && info->read_bytes_sec_max) || + (info->total_bytes_sec_max && info->write_bytes_sec_max)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of bytes_sec_max cannot be set at the same time")); - goto cleanup; + return -1; } - if ((info.total_iops_sec_max && info.read_iops_sec_max) || - (info.total_iops_sec_max && info.write_iops_sec_max)) { + if ((info->total_iops_sec_max && info->read_iops_sec_max) || + (info->total_iops_sec_max && info->write_iops_sec_max)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("total and read/write of iops_sec_max cannot be set at the same time")); - goto cleanup; + return -1; } + return 0; +} + + +static int +testDomainCheckBlockIoTuneMax(virDomainBlockIoTuneInfo *info) +{ + int ret = -1; #define TEST_BLOCK_IOTUNE_MAX_CHECK(FIELD, FIELD_MAX) \ do { \ - if (info.FIELD > info.FIELD_MAX) { \ + if (info->FIELD > info->FIELD_MAX) { \ virReportError(VIR_ERR_INVALID_ARG, \ _("%1$s cannot be set higher than %2$s"), \ #FIELD, #FIELD_MAX); \ - goto cleanup; \ + goto endjob; \ } \ } while (0); @@ -4009,6 +3967,69 @@ testDomainSetBlockIoTune(virDomainPtr dom, #undef TEST_BLOCK_IOTUNE_MAX_CHECK + ret = 0; + endjob: + return ret; +} + + +static int +testDomainSetBlockIoTune(virDomainPtr dom, + const char *path, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virDomainObj *vm = NULL; + virDomainDef *def = NULL; + virDomainBlockIoTuneInfo info = {0}; + virDomainDiskDef *conf_disk = NULL; + virTypedParameterPtr eventParams = NULL; + int eventNparams = 0; + int eventMaxparams = 0; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (testDomainValidateBlockIoTune(params, nparams) < 0) + return -1; + + if (!(vm = testDomObjFromDomain(dom))) + return -1; + + if (!(def = virDomainObjGetOneDef(vm, flags))) + goto cleanup; + + if (!(conf_disk = virDomainDiskByName(def, path, true))) { + virReportError(VIR_ERR_INVALID_ARG, + _("missing persistent configuration for disk '%1$s'"), + path); + goto cleanup; + } + + info = conf_disk->blkdeviotune; + info.group_name = g_strdup(conf_disk->blkdeviotune.group_name); + + if (virTypedParamsAddString(&eventParams, &eventNparams, &eventMaxparams, + VIR_DOMAIN_TUNABLE_BLKDEV_DISK, path) < 0) + goto cleanup; + + if (testDomainSetBlockIoTuneFields(&info, + params, + nparams, + &eventParams, + &eventNparams, + &eventMaxparams) < 0) + goto cleanup; + + if (testDomainCheckBlockIoTuneMutualExclusion(&info) < 0) + goto cleanup; + + + if (testDomainCheckBlockIoTuneMax(&info) < 0) + goto cleanup; + virDomainDiskSetBlockIOTune(conf_disk, &info); ret = 0; @@ -4119,6 +4140,106 @@ testDomainGetBlockIoTune(virDomainPtr dom, virDomainObjEndAPI(&vm); return ret; } + + +static int +testDomainSetThrottleGroup(virDomainPtr dom, + const char *group, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virDomainObj *vm = NULL; + virDomainDef *def = NULL; + virDomainThrottleGroupDef info = { 0 }; + virDomainThrottleGroupDef *cur_info = NULL; + virTypedParameterPtr eventParams = NULL; + int eventNparams = 0; + int eventMaxparams = 0; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + if (testDomainValidateBlockIoTune(params, nparams) < 0) + return -1; + + if (!(vm = testDomObjFromDomain(dom))) + return -1; + + if (!(def = virDomainObjGetOneDef(vm, flags))) + goto cleanup; + + if (virTypedParamsAddString(&eventParams, &eventNparams, &eventMaxparams, + VIR_DOMAIN_TUNABLE_BLKDEV_GROUP_NAME, group) < 0) + goto cleanup; + + if (testDomainSetBlockIoTuneFields(&info, + params, + nparams, + &eventParams, + &eventNparams, + &eventMaxparams) < 0) + goto cleanup; + + if (testDomainCheckBlockIoTuneMutualExclusion(&info) < 0) + goto cleanup; + + if (testDomainCheckBlockIoTuneMax(&info) < 0) + goto cleanup; + + cur_info = virDomainThrottleGroupByName(def, group); + if (cur_info != NULL) { + virDomainThrottleGroupUpdate(def, &info); + } else { + virDomainThrottleGroupAdd(def, &info); + } + ret = 0; + + cleanup: + VIR_FREE(info.group_name); + virDomainObjEndAPI(&vm); + if (eventNparams) + virTypedParamsFree(eventParams, eventNparams); + return ret; +} + + +static int +testDomainDelThrottleGroup(virDomainPtr dom, + const char *groupname, + unsigned int flags) +{ + virDomainObj *vm = NULL; + virDomainDef *def = NULL; + virDomainThrottleGroupDef groupDef = {0}; + virDomainThrottleGroupDef *reply = &groupDef; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG | + VIR_TYPED_PARAM_STRING_OKAY, -1); + + + if (!(vm = testDomObjFromDomain(dom))) + return -1; + + if (!(def = virDomainObjGetOneDef(vm, flags))) + goto cleanup; + + if (!(reply = virDomainThrottleGroupByName(def, groupname))) { + virReportError(VIR_ERR_INVALID_ARG, + _("throttle group '%1$s' was not found in the domain config"), + groupname); + goto cleanup; + } + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + #undef TEST_SET_PARAM @@ -11218,6 +11339,8 @@ static virHypervisorDriver testHypervisorDriver = { .domainGetInterfaceParameters = testDomainGetInterfaceParameters, /* 5.6.0 */ .domainSetBlockIoTune = testDomainSetBlockIoTune, /* 5.7.0 */ .domainGetBlockIoTune = testDomainGetBlockIoTune, /* 5.7.0 */ + .domainSetThrottleGroup = testDomainSetThrottleGroup, /* 11.1.0 */ + .domainDelThrottleGroup = testDomainDelThrottleGroup, /* 11.1.0 */ .domainSetBlkioParameters = testDomainSetBlkioParameters, /* 7.7.0 */ .domainGetBlkioParameters = testDomainGetBlkioParameters, /* 7.7.0 */ .connectListDefinedDomains = testConnectListDefinedDomains, /* 0.1.11 */ -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:19 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Test throttle group APIs
* Extract common methods for both "testDomainSetThrottleGroup" and "testDomainSetBlockIoTune": testDomainValidateBlockIoTune, testDomainSetBlockIoTuneFields, testDomainCheckBlockIoTuneMutualExclusion, testDomainCheckBlockIoTuneMax * Test "Set": testDomainSetThrottleGroup * Test "Get": testDomainGetThrottleGroup * Test "Del": testDomainDelThrottleGroup
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Cleanup Test "Get": testDomainGetThrottleGroup * Update the version to 11.1.0.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- src/test/test_driver.c | 367 +++++++++++++++++++++++++++-------------- 1 file changed, 245 insertions(+), 122 deletions(-)
For the sake of not prolonging the review any more I'm dropping this patch for now. Please submit it once we're done with the qemu driver impl.

On Wed, Feb 19, 2025 at 22:27:19 +0530, Harikumar Rajkumar wrote:
For the sake of not prolonging the review any more I'm dropping this patch for now. Please submit it once we're done with the qemu driver impl.
Could you kindly confirm if the patch review has been completed? If so, we would appreciate a summary of the review comments. Here is the summary of my understanding of the changes: * Cleanup of the get_throttle_group RPC and cleanup of dead code. * Removal of QEMU_CAPS_OBJECT_JSON in the QEMU driver implementation. * Test cases for when copy-on-read is enabled. * Version number update. Can we submit the new patch (v9), or will you provide a branch for us to address the changes? Thanks & regards, Harikumar R

On Fri, Mar 14, 2025 at 09:03:09 -0000, Harikumar Rajkumar wrote:
On Wed, Feb 19, 2025 at 22:27:19 +0530, Harikumar Rajkumar wrote:
For the sake of not prolonging the review any more I'm dropping this patch for now. Please submit it once we're done with the qemu driver impl.
Could you kindly confirm if the patch review has been completed? If so, we would appreciate a summary of the review comments
The review is not done and once it will be done I'll notify you. The more I have to reply to these requests for updates the less time and motivation I have to work on the actual review.
Here is the summary of my understanding of the changes: * Cleanup of the get_throttle_group RPC and cleanup of dead code. * Removal of QEMU_CAPS_OBJECT_JSON in the QEMU driver implementation. * Test cases for when copy-on-read is enabled. * Version number update.
Can we submit the new patch (v9), or will you provide a branch for us to address the changes?
As I've said I will be doing the changes myself for the most improtant parts because I don't want to spend any more time on this. I've also found a few bugs that I'll fix. For few patches or parts of patches that are not important and I don't care about you'll have to re-send them and find someone to review them. If this feature is so urgent for you or your employer you should consider contributing to also aleviate the maintenance burden of the code you are asking us to maintain. Without a long term commitment I need to make sure that the code is in a state that it will not cause problems for me or others who will be maintainig it.

Thanks for the update and for your efforts on the review. We understand your approach and we'll wait until it's completed.

From: Chun Feng Wu <danielwuwy@163.com> Define macro for iotune options, this macro is used by opts_blkdeviotune and later throttle group opts Signed-off-by: Chun Feng Wu <danielwuwy@163.com> --- tools/virsh-domain.c | 308 ++++++++++++++++++++++--------------------- 1 file changed, 156 insertions(+), 152 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f3da2f903f..9f522da544 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -1197,6 +1197,160 @@ static const vshCmdInfo info_blkdeviotune = { .desc = N_("Set or query disk I/O parameters such as block throttling."), }; +#define VSH_OPTS_IOTUNE() \ + {.name = "total_bytes_sec", \ + .type = VSH_OT_ALIAS, \ + .help = "total-bytes-sec" \ + }, \ + {.name = "total-bytes-sec", \ + .type = VSH_OT_INT, \ + .help = N_("total throughput limit, as scaled integer (default bytes)") \ + }, \ + {.name = "read_bytes_sec", \ + .type = VSH_OT_ALIAS, \ + .help = "read-bytes-sec" \ + }, \ + {.name = "read-bytes-sec", \ + .type = VSH_OT_INT, \ + .help = N_("read throughput limit, as scaled integer (default bytes)") \ + }, \ + {.name = "write_bytes_sec", \ + .type = VSH_OT_ALIAS, \ + .help = "write-bytes-sec" \ + }, \ + {.name = "write-bytes-sec", \ + .type = VSH_OT_INT, \ + .help = N_("write throughput limit, as scaled integer (default bytes)") \ + }, \ + {.name = "total_iops_sec", \ + .type = VSH_OT_ALIAS, \ + .help = "total-iops-sec" \ + }, \ + {.name = "total-iops-sec", \ + .type = VSH_OT_INT, \ + .help = N_("total I/O operations limit per second") \ + }, \ + {.name = "read_iops_sec", \ + .type = VSH_OT_ALIAS, \ + .help = "read-iops-sec" \ + }, \ + {.name = "read-iops-sec", \ + .type = VSH_OT_INT, \ + .help = N_("read I/O operations limit per second") \ + }, \ + {.name = "write_iops_sec", \ + .type = VSH_OT_ALIAS, \ + .help = "write-iops-sec" \ + }, \ + {.name = "write-iops-sec", \ + .type = VSH_OT_INT, \ + .help = N_("write I/O operations limit per second") \ + }, \ + {.name = "total_bytes_sec_max", \ + .type = VSH_OT_ALIAS, \ + .help = "total-bytes-sec-max" \ + }, \ + {.name = "total-bytes-sec-max", \ + .type = VSH_OT_INT, \ + .help = N_("total max, as scaled integer (default bytes)") \ + }, \ + {.name = "read_bytes_sec_max", \ + .type = VSH_OT_ALIAS, \ + .help = "read-bytes-sec-max" \ + }, \ + {.name = "read-bytes-sec-max", \ + .type = VSH_OT_INT, \ + .help = N_("read max, as scaled integer (default bytes)") \ + }, \ + {.name = "write_bytes_sec_max", \ + .type = VSH_OT_ALIAS, \ + .help = "write-bytes-sec-max" \ + }, \ + {.name = "write-bytes-sec-max", \ + .type = VSH_OT_INT, \ + .help = N_("write max, as scaled integer (default bytes)") \ + }, \ + {.name = "total_iops_sec_max", \ + .type = VSH_OT_ALIAS, \ + .help = "total-iops-sec-max" \ + }, \ + {.name = "total-iops-sec-max", \ + .type = VSH_OT_INT, \ + .help = N_("total I/O operations max") \ + }, \ + {.name = "read_iops_sec_max", \ + .type = VSH_OT_ALIAS, \ + .help = "read-iops-sec-max" \ + }, \ + {.name = "read-iops-sec-max", \ + .type = VSH_OT_INT, \ + .help = N_("read I/O operations max") \ + }, \ + {.name = "write_iops_sec_max", \ + .type = VSH_OT_ALIAS, \ + .help = "write-iops-sec-max" \ + }, \ + {.name = "write-iops-sec-max", \ + .type = VSH_OT_INT, \ + .help = N_("write I/O operations max") \ + }, \ + {.name = "size_iops_sec", \ + .type = VSH_OT_ALIAS, \ + .help = "size-iops-sec" \ + }, \ + {.name = "size-iops-sec", \ + .type = VSH_OT_INT, \ + .help = N_("I/O size in bytes") \ + }, \ + {.name = "total_bytes_sec_max_length", \ + .type = VSH_OT_ALIAS, \ + .help = "total-bytes-sec-max-length" \ + }, \ + {.name = "total-bytes-sec-max-length", \ + .type = VSH_OT_INT, \ + .help = N_("duration in seconds to allow total max bytes") \ + }, \ + {.name = "read_bytes_sec_max_length", \ + .type = VSH_OT_ALIAS, \ + .help = "read-bytes-sec-max-length" \ + }, \ + {.name = "read-bytes-sec-max-length", \ + .type = VSH_OT_INT, \ + .help = N_("duration in seconds to allow read max bytes") \ + }, \ + {.name = "write_bytes_sec_max_length", \ + .type = VSH_OT_ALIAS, \ + .help = "write-bytes-sec-max-length" \ + }, \ + {.name = "write-bytes-sec-max-length", \ + .type = VSH_OT_INT, \ + .help = N_("duration in seconds to allow write max bytes") \ + }, \ + {.name = "total_iops_sec_max_length", \ + .type = VSH_OT_ALIAS, \ + .help = "total-iops-sec-max-length" \ + }, \ + {.name = "total-iops-sec-max-length", \ + .type = VSH_OT_INT, \ + .help = N_("duration in seconds to allow total I/O operations max") \ + }, \ + {.name = "read_iops_sec_max_length", \ + .type = VSH_OT_ALIAS, \ + .help = "read-iops-sec-max-length" \ + }, \ + {.name = "read-iops-sec-max-length", \ + .type = VSH_OT_INT, \ + .help = N_("duration in seconds to allow read I/O operations max") \ + }, \ + {.name = "write_iops_sec_max_length", \ + .type = VSH_OT_ALIAS, \ + .help = "write-iops-sec-max-length" \ + }, \ + {.name = "write-iops-sec-max-length", \ + .type = VSH_OT_INT, \ + .help = N_("duration in seconds to allow write I/O operations max") \ + } \ + static const vshCmdOptDef opts_blkdeviotune[] = { VIRSH_COMMON_OPT_DOMAIN_FULL(0), {.name = "device", @@ -1206,110 +1360,6 @@ static const vshCmdOptDef opts_blkdeviotune[] = { .completer = virshDomainDiskTargetCompleter, .help = N_("block device") }, - {.name = "total_bytes_sec", - .type = VSH_OT_ALIAS, - .help = "total-bytes-sec" - }, - {.name = "total-bytes-sec", - .type = VSH_OT_INT, - .help = N_("total throughput limit, as scaled integer (default bytes)") - }, - {.name = "read_bytes_sec", - .type = VSH_OT_ALIAS, - .help = "read-bytes-sec" - }, - {.name = "read-bytes-sec", - .type = VSH_OT_INT, - .help = N_("read throughput limit, as scaled integer (default bytes)") - }, - {.name = "write_bytes_sec", - .type = VSH_OT_ALIAS, - .help = "write-bytes-sec" - }, - {.name = "write-bytes-sec", - .type = VSH_OT_INT, - .help = N_("write throughput limit, as scaled integer (default bytes)") - }, - {.name = "total_iops_sec", - .type = VSH_OT_ALIAS, - .help = "total-iops-sec" - }, - {.name = "total-iops-sec", - .type = VSH_OT_INT, - .help = N_("total I/O operations limit per second") - }, - {.name = "read_iops_sec", - .type = VSH_OT_ALIAS, - .help = "read-iops-sec" - }, - {.name = "read-iops-sec", - .type = VSH_OT_INT, - .help = N_("read I/O operations limit per second") - }, - {.name = "write_iops_sec", - .type = VSH_OT_ALIAS, - .help = "write-iops-sec" - }, - {.name = "write-iops-sec", - .type = VSH_OT_INT, - .help = N_("write I/O operations limit per second") - }, - {.name = "total_bytes_sec_max", - .type = VSH_OT_ALIAS, - .help = "total-bytes-sec-max" - }, - {.name = "total-bytes-sec-max", - .type = VSH_OT_INT, - .help = N_("total max, as scaled integer (default bytes)") - }, - {.name = "read_bytes_sec_max", - .type = VSH_OT_ALIAS, - .help = "read-bytes-sec-max" - }, - {.name = "read-bytes-sec-max", - .type = VSH_OT_INT, - .help = N_("read max, as scaled integer (default bytes)") - }, - {.name = "write_bytes_sec_max", - .type = VSH_OT_ALIAS, - .help = "write-bytes-sec-max" - }, - {.name = "write-bytes-sec-max", - .type = VSH_OT_INT, - .help = N_("write max, as scaled integer (default bytes)") - }, - {.name = "total_iops_sec_max", - .type = VSH_OT_ALIAS, - .help = "total-iops-sec-max" - }, - {.name = "total-iops-sec-max", - .type = VSH_OT_INT, - .help = N_("total I/O operations max") - }, - {.name = "read_iops_sec_max", - .type = VSH_OT_ALIAS, - .help = "read-iops-sec-max" - }, - {.name = "read-iops-sec-max", - .type = VSH_OT_INT, - .help = N_("read I/O operations max") - }, - {.name = "write_iops_sec_max", - .type = VSH_OT_ALIAS, - .help = "write-iops-sec-max" - }, - {.name = "write-iops-sec-max", - .type = VSH_OT_INT, - .help = N_("write I/O operations max") - }, - {.name = "size_iops_sec", - .type = VSH_OT_ALIAS, - .help = "size-iops-sec" - }, - {.name = "size-iops-sec", - .type = VSH_OT_INT, - .help = N_("I/O size in bytes") - }, {.name = "group_name", .type = VSH_OT_ALIAS, .help = "group-name" @@ -1319,59 +1369,13 @@ static const vshCmdOptDef opts_blkdeviotune[] = { .completer = virshCompleteEmpty, .help = N_("group name to share I/O quota between multiple drives") }, - {.name = "total_bytes_sec_max_length", - .type = VSH_OT_ALIAS, - .help = "total-bytes-sec-max-length" - }, - {.name = "total-bytes-sec-max-length", - .type = VSH_OT_INT, - .help = N_("duration in seconds to allow total max bytes") - }, - {.name = "read_bytes_sec_max_length", - .type = VSH_OT_ALIAS, - .help = "read-bytes-sec-max-length" - }, - {.name = "read-bytes-sec-max-length", - .type = VSH_OT_INT, - .help = N_("duration in seconds to allow read max bytes") - }, - {.name = "write_bytes_sec_max_length", - .type = VSH_OT_ALIAS, - .help = "write-bytes-sec-max-length" - }, - {.name = "write-bytes-sec-max-length", - .type = VSH_OT_INT, - .help = N_("duration in seconds to allow write max bytes") - }, - {.name = "total_iops_sec_max_length", - .type = VSH_OT_ALIAS, - .help = "total-iops-sec-max-length" - }, - {.name = "total-iops-sec-max-length", - .type = VSH_OT_INT, - .help = N_("duration in seconds to allow total I/O operations max") - }, - {.name = "read_iops_sec_max_length", - .type = VSH_OT_ALIAS, - .help = "read-iops-sec-max-length" - }, - {.name = "read-iops-sec-max-length", - .type = VSH_OT_INT, - .help = N_("duration in seconds to allow read I/O operations max") - }, - {.name = "write_iops_sec_max_length", - .type = VSH_OT_ALIAS, - .help = "write-iops-sec-max-length" - }, - {.name = "write-iops-sec-max-length", - .type = VSH_OT_INT, - .help = N_("duration in seconds to allow write I/O operations max") - }, + VSH_OPTS_IOTUNE(), VIRSH_COMMON_OPT_DOMAIN_CONFIG, VIRSH_COMMON_OPT_DOMAIN_LIVE, VIRSH_COMMON_OPT_DOMAIN_CURRENT, {.name = NULL} }; +#undef VSH_OPTS_IOTUNE static bool cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd) -- 2.39.5 (Apple Git-154)

From: Chun Feng Wu <danielwuwy@163.com> Implement new throttle cmds * Add new virsh cmds: domthrottlegroupset, domthrottlegrouplist, domthrottlegroupinfo, domthrottlegroupdel * Add doc for new cmds at docs/manpages/virsh.rst * Add cmd helper "virshDomainThrottleGroupCompleter", which is used by domthrottlegroupset, domthrottlegroupinfo, domthrottlegroupdel Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * Update of code documentation comments. * Reimplement Get throttle group from XML. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- docs/manpages/virsh.rst | 134 +++++++++++ tools/virsh-completer-domain.c | 55 +++++ tools/virsh-completer-domain.h | 11 + tools/virsh-domain.c | 405 ++++++++++++++++++++++++++++++++- 4 files changed, 604 insertions(+), 1 deletion(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 0eb1d6ea93..8726870cf9 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1128,6 +1128,140 @@ given, but *--current* is exclusive. For querying only one of *--live*, is different depending on hypervisor. +domthrottlegroupset +------------------- + +**Syntax:** + +:: + + domthrottlegroupset domain group-name [[--config] [--live] | [--current]] + [[total-bytes-sec] | [read-bytes-sec] [write-bytes-sec]] + [[total-iops-sec] | [read-iops-sec] [write-iops-sec]] + [[total-bytes-sec-max] | [read-bytes-sec-max] [write-bytes-sec-max]] + [[total-iops-sec-max] | [read-iops-sec-max] [write-iops-sec-max]] + [[total-bytes-sec-max-length] | + [read-bytes-sec-max-length] [write-bytes-sec-max-length]] + [[total-iops-sec-max-length] | + [read-iops-sec-max-length] [write-iops-sec-max-length]] + [size-iops-sec] + +Add or update a throttle group against specific *domain*. +*group-name* specifies a unique throttle group name, which defines limit, and +will be referenced by drives. + +If no limit is specified, default them as all zeros, which will fail, +Otherwise, set limits with these flags: +*--total-bytes-sec* specifies total throughput limit as a scaled integer, the +default being bytes per second if no suffix is specified. +*--read-bytes-sec* specifies read throughput limit as a scaled integer, the +default being bytes per second if no suffix is specified. +*--write-bytes-sec* specifies write throughput limit as a scaled integer, the +default being bytes per second if no suffix is specified. +*--total-iops-sec* specifies total I/O operations limit per second. +*--read-iops-sec* specifies read I/O operations limit per second. +*--write-iops-sec* specifies write I/O operations limit per second. +*--total-bytes-sec-max* specifies maximum total throughput limit as a scaled +integer, the default being bytes per second if no suffix is specified +*--read-bytes-sec-max* specifies maximum read throughput limit as a scaled +integer, the default being bytes per second if no suffix is specified. +*--write-bytes-sec-max* specifies maximum write throughput limit as a scaled +integer, the default being bytes per second if no suffix is specified. +*--total-iops-sec-max* specifies maximum total I/O operations limit per second. +*--read-iops-sec-max* specifies maximum read I/O operations limit per second. +*--write-iops-sec-max* specifies maximum write I/O operations limit per second. +*--total-bytes-sec-max-length* specifies duration in seconds to allow maximum +total throughput limit. +*--read-bytes-sec-max-length* specifies duration in seconds to allow maximum +read throughput limit. +*--write-bytes-sec-max-length* specifies duration in seconds to allow maximum +write throughput limit. +*--total-iops-sec-max-length* specifies duration in seconds to allow maximum +total I/O operations limit. +*--read-iops-sec-max-length* specifies duration in seconds to allow maximum +read I/O operations limit. +*--write-iops-sec-max-length* specifies duration in seconds to allow maximum +write I/O operations limit. +*--size-iops-sec* specifies size I/O operations limit per second. + +Bytes and iops values are independent, but setting only one value (such +as --read-bytes-sec) resets the other two in that category to unlimited. +An explicit 0 also clears any limit. A non-zero value for a given total +cannot be mixed with non-zero values for read or write. + +It is up to the hypervisor to determine how to handle the length values. +For the QEMU hypervisor, if an I/O limit value or maximum value is set, +then the default value of 1 second will be displayed. Supplying a 0 will +reset the value back to the default. + +If *--live* is specified, affect a running guest. +If *--config* is specified, affect the next start of a persistent guest. +If *--current* is specified, it is equivalent to either *--live* or +*--config*, depending on the current state of the guest. +When setting the disk io parameters both *--live* and *--config* +are specified, both live configuration and config are updated while setting +the description, but *--current* is exclusive. If no flag is specified, behavior +is different depending on hypervisor. + + +domthrottlegroupdel +------------------- + +**Syntax:** + +:: + + domthrottlegroupdel domain group-name [[--config] [--live] | [--current]] + +Delete a Throttlegroup from the domain using the specified *group-name*. +If an Throttlegroup is currently referenced by a disk resource, then the attempt +to remove the Throttlegroup will fail. +If the *group-name* does not exist an error will occur. + +If *--live* is specified, affect a running guest. If the guest is not +running an error is returned. +If *--config* is specified, affect the next start of a persistent guest. +If *--current* is specified, it is equivalent to either *--live* or +*--config*, depending on the current state of the guest. + + +domthrottlegroupinfo +-------------------- + +**Syntax:** + +:: + + domthrottlegroupinfo domain group-name [[--config] [--live] | [--current]] + +Display domain Throttlegroup information including I/O limits setting. + +If *--live* is specified, get the Throttlegroup data from the running guest. If +the guest is not running, an error is returned. +If *--config* is specified, get the Throttlegroup data from the next start of +a persistent guest. +If *--current* is specified or *--live* and *--config* are not specified, +then get the Throttlegroup data based on the current guest state, which can +either be live or offline. +If both *--live* and *--config* are specified, the *--config* option takes +precedence on getting the current description. + + +domthrottlegrouplist +-------------------- + +**Syntax:** + +:: + + domthrottlegrouplist domain [--inactive] + +Print a table showing names of all throttle groups +associated with *domain*. If *--inactive* is specified, query the +Throttlegroup data that will be used on the next boot, rather than those +currently in use by a running domain. + + blkiotune --------- diff --git a/tools/virsh-completer-domain.c b/tools/virsh-completer-domain.c index 61362224a3..3b0df15c13 100644 --- a/tools/virsh-completer-domain.c +++ b/tools/virsh-completer-domain.c @@ -248,6 +248,61 @@ virshDomainMigrateDisksCompleter(vshControl *ctl, } +int +virshGetThrottleGroupNames(xmlXPathContext *ctxt, + xmlNodePtr **groups, + char ***groupNames) +{ + int ngroups; + size_t i; + + ngroups = virXPathNodeSet("./throttlegroups/throttlegroup", ctxt, groups); + if (ngroups < 0) + return -1; + + *groupNames = g_new0(char *, ngroups + 1); + + for (i = 0; i < ngroups; i++) { + ctxt->node = (*groups)[i]; + if (!((*groupNames)[i] = virXPathString("string(./group_name)", ctxt))) { + g_strfreev(*groupNames); + *groupNames = NULL; + return -1; + } + } + + return ngroups; +} + + +char ** +virshDomainThrottleGroupCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags) +{ + virshControl *priv = ctl->privData; + g_autoptr(xmlDoc) xmldoc = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *groups = NULL; + g_auto(GStrv) groupNames = NULL; + int ngroups; + + virCheckFlags(0, NULL); + + if (!priv->conn || virConnectIsAlive(priv->conn) <= 0) + return NULL; + + if (virshDomainGetXML(ctl, cmd, 0, &xmldoc, &ctxt) < 0) + return NULL; + + ngroups = virshGetThrottleGroupNames(ctxt, &groups, &groupNames); + if (ngroups < 0) + return NULL; + + return g_steal_pointer(&groupNames); +} + + char ** virshDomainUndefineStorageDisksCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-completer-domain.h b/tools/virsh-completer-domain.h index 27cf963912..680b3fc018 100644 --- a/tools/virsh-completer-domain.h +++ b/tools/virsh-completer-domain.h @@ -21,6 +21,7 @@ #pragma once #include "vsh.h" +#include <libxml/xpath.h> char ** virshDomainNameCompleter(vshControl *ctl, @@ -41,6 +42,16 @@ virshDomainDiskTargetCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags); +int +virshGetThrottleGroupNames(xmlXPathContext *ctxt, + xmlNodePtr **groups, + char ***groupNames); + +char ** +virshDomainThrottleGroupCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags); + char ** virshDomainInterfaceStateCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 9f522da544..b8f61340cc 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -1375,7 +1375,7 @@ static const vshCmdOptDef opts_blkdeviotune[] = { VIRSH_COMMON_OPT_DOMAIN_CURRENT, {.name = NULL} }; -#undef VSH_OPTS_IOTUNE + static bool cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd) @@ -1513,6 +1513,385 @@ cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd) goto cleanup; } + +/* + * "throttlegrouplist" command + */ +static const vshCmdInfo info_throttlegrouplist = { + .help = N_("list all domain throttlegroups."), + .desc = N_("Get the summary of throttle groups for a domain."), +}; + + +static const vshCmdOptDef opts_throttlegrouplist[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "inactive", + .type = VSH_OT_BOOL, + .help = N_("get inactive rather than running configuration") + }, + {.name = NULL} +}; + + +static bool +cmdThrottleGroupList(vshControl *ctl, + const vshCmd *cmd) +{ + unsigned int flags = 0; + size_t i; + g_autoptr(xmlDoc) xml = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *groups = NULL; + g_auto(GStrv) groupNames = NULL; + ssize_t ngroups; + g_autoptr(vshTable) table = NULL; + + if (vshCommandOptBool(cmd, "inactive")) + flags |= VIR_DOMAIN_XML_INACTIVE; + + if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0) + return false; + + table = vshTableNew(_("Name"), NULL); + + if (!table) + return false; + + ngroups = virshGetThrottleGroupNames(ctxt, &groups, &groupNames); + for (i = 0; i < ngroups; i++) { + if (vshTableRowAppend(table, groupNames[i], NULL) < 0) + return false; + } + + vshTablePrintToStdout(table, ctl); + + return true; +} + + +/* + * "throttlegroupset" command + */ +static const vshCmdInfo info_throttlegroupset = { + .help = N_("Add or update a throttling group."), + .desc = N_("Add or updte a throttling group."), +}; + + +static const vshCmdOptDef opts_throttlegroupset[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "group-name", + .type = VSH_OT_STRING, + .positional = true, + .required = true, + .completer = virshDomainThrottleGroupCompleter, + .help = N_("throttle group name") + }, + VSH_OPTS_IOTUNE(), + VIRSH_COMMON_OPT_DOMAIN_CONFIG, + VIRSH_COMMON_OPT_DOMAIN_LIVE, + VIRSH_COMMON_OPT_DOMAIN_CURRENT, + {.name = NULL} +}; +#undef VSH_OPTS_IOTUNE + + +static bool +cmdThrottleGroupSet(vshControl *ctl, + const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + const char *group_name = NULL; + unsigned long long value; + int nparams = 0; + int maxparams = 0; + virTypedParameterPtr params = NULL; + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + int rv = 0; + bool current = vshCommandOptBool(cmd, "current"); + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool ret = false; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + +#define VSH_SET_THROTTLE_GROUP_SCALED(PARAM, CONST) \ + if ((rv = vshCommandOptScaledInt(ctl, cmd, #PARAM, &value, \ + 1, ULLONG_MAX)) < 0) { \ + goto interror; \ + } else if (rv > 0) { \ + if (virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \ + value) < 0) \ + goto save_error; \ + } + + VSH_SET_THROTTLE_GROUP_SCALED(total-bytes-sec, TOTAL_BYTES_SEC); + VSH_SET_THROTTLE_GROUP_SCALED(read-bytes-sec, READ_BYTES_SEC); + VSH_SET_THROTTLE_GROUP_SCALED(write-bytes-sec, WRITE_BYTES_SEC); + VSH_SET_THROTTLE_GROUP_SCALED(total-bytes-sec-max, TOTAL_BYTES_SEC_MAX); + VSH_SET_THROTTLE_GROUP_SCALED(read-bytes-sec-max, READ_BYTES_SEC_MAX); + VSH_SET_THROTTLE_GROUP_SCALED(write-bytes-sec-max, WRITE_BYTES_SEC_MAX); +#undef VSH_SET_THROTTLE_GROUP_SCALED + +#define VSH_SET_THROTTLE_GROUP(PARAM, CONST) \ + if ((rv = vshCommandOptULongLong(ctl, cmd, #PARAM, &value)) < 0) { \ + goto interror; \ + } else if (rv > 0) { \ + if (virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \ + value) < 0) \ + goto save_error; \ + } + + VSH_SET_THROTTLE_GROUP(total-iops-sec, TOTAL_IOPS_SEC); + VSH_SET_THROTTLE_GROUP(read-iops-sec, READ_IOPS_SEC); + VSH_SET_THROTTLE_GROUP(write-iops-sec, WRITE_IOPS_SEC); + VSH_SET_THROTTLE_GROUP(total-iops-sec-max, TOTAL_IOPS_SEC_MAX); + VSH_SET_THROTTLE_GROUP(read-iops-sec-max, READ_IOPS_SEC_MAX); + VSH_SET_THROTTLE_GROUP(write-iops-sec-max, WRITE_IOPS_SEC_MAX); + VSH_SET_THROTTLE_GROUP(size-iops-sec, SIZE_IOPS_SEC); + + VSH_SET_THROTTLE_GROUP(total-bytes-sec-max-length, TOTAL_BYTES_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(read-bytes-sec-max-length, READ_BYTES_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(write-bytes-sec-max-length, WRITE_BYTES_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(total-iops-sec-max-length, TOTAL_IOPS_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(read-iops-sec-max-length, READ_IOPS_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(write-iops-sec-max-length, WRITE_IOPS_SEC_MAX_LENGTH); +#undef VSH_SET_THROTTLE_GROUP + + if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) { + goto cleanup; + } + + if (group_name) { + if (virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME, + group_name) < 0) + goto save_error; + } + + if (virDomainSetThrottleGroup(dom, group_name, params, nparams, flags) < 0) + goto error; + vshPrintExtra(ctl, "%s", _("Throttle group set successfully\n")); + + ret = true; + + cleanup: + virTypedParamsFree(params, nparams); + return ret; + + save_error: + vshSaveLibvirtError(); + error: + vshError(ctl, "%s", _("Unable to set throttle group")); + goto cleanup; + + interror: + vshError(ctl, "%s", _("Unable to parse integer parameter")); + goto cleanup; +} + + +/* + * "throttlegroupdel" command + */ +static const vshCmdInfo info_throttlegroupdel = { + .help = N_("Delete a throttling group."), + .desc = N_("Delete a throttling group."), +}; + + +static const vshCmdOptDef opts_throttlegroupdel[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "group-name", + .type = VSH_OT_STRING, + .positional = true, + .required = true, + .completer = virshDomainThrottleGroupCompleter, + .help = N_("throttle group name") + }, + VIRSH_COMMON_OPT_DOMAIN_CONFIG, + VIRSH_COMMON_OPT_DOMAIN_LIVE, + VIRSH_COMMON_OPT_DOMAIN_CURRENT, + {.name = NULL} +}; + + +static bool +cmdThrottleGroupDel(vshControl *ctl, + const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + const char *group_name = NULL; + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool current = vshCommandOptBool(cmd, "current"); + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) { + return false; + } + + if (virDomainDelThrottleGroup(dom, group_name, flags) < 0) + return false; + vshPrintExtra(ctl, "%s", _("Throttle group deleted successfully\n")); + + return true; +} + + +/* + * "throttlegroupinfo" command + */ +static const vshCmdInfo info_throttlegroupinfo = { + .help = N_("Get a throttling group."), + .desc = N_("Get a throttling group."), +}; + + +static const vshCmdOptDef opts_throttlegroupinfo[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "group-name", + .type = VSH_OT_STRING, + .positional = true, + .required = true, + .completer = virshDomainThrottleGroupCompleter, + .help = N_("throttle group name") + }, + VIRSH_COMMON_OPT_DOMAIN_CONFIG, + VIRSH_COMMON_OPT_DOMAIN_LIVE, + VIRSH_COMMON_OPT_DOMAIN_CURRENT, + {.name = NULL} +}; + + +#define PARSE_THROTTLER_GROUP(val) \ + if (virXPathULongLong("string(./" #val ")", \ + ctxt, &throttleGroup->val) == -2) { \ + virReportError(VIR_ERR_XML_ERROR, \ + _("throttle group field '%1$s' must be an integer"), #val); \ + goto cleanup; \ + } \ + if ((virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + #val, throttleGroup->val) < 0)) { \ + vshError(ctl, "%s", _("Unable to get throttle group parameters")); \ + goto cleanup; \ + } + +static bool +cmdThrottleGroupInfo(vshControl *ctl, + const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + const char *group_name = NULL; + int nparams = 0; + virTypedParameterPtr params = NULL; + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + size_t i; + bool current = vshCommandOptBool(cmd, "current"); + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool ret = false; + + g_autoptr(xmlDoc) xml = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *node = NULL; + int n = 0; + int maxparams = 0; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) { + goto cleanup; + } + + if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0) + goto cleanup; + + if ((n = virXPathNodeSet("/domain/throttlegroups/throttlegroup", ctxt, &node)) < 0) + goto cleanup; + + if (n == 0) + return 0; + + for (i = 0; i < n; i++) { + g_autoptr(virDomainThrottleGroupDef) throttleGroup = g_new0(virDomainThrottleGroupDef, 1); + + VIR_XPATH_NODE_AUTORESTORE(ctxt) + ctxt->node = node[i]; + + if (STREQ(group_name, virXPathString("string(./group_name)", ctxt)) == 0) { + continue; + } + + PARSE_THROTTLER_GROUP(total_bytes_sec); + PARSE_THROTTLER_GROUP(read_bytes_sec); + PARSE_THROTTLER_GROUP(write_bytes_sec); + PARSE_THROTTLER_GROUP(total_iops_sec); + PARSE_THROTTLER_GROUP(read_iops_sec); + PARSE_THROTTLER_GROUP(write_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max); + PARSE_THROTTLER_GROUP(read_bytes_sec_max); + PARSE_THROTTLER_GROUP(write_bytes_sec_max); + PARSE_THROTTLER_GROUP(total_iops_sec_max); + PARSE_THROTTLER_GROUP(read_iops_sec_max); + PARSE_THROTTLER_GROUP(write_iops_sec_max); + + PARSE_THROTTLER_GROUP(size_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(read_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(write_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(total_iops_sec_max_length); + PARSE_THROTTLER_GROUP(read_iops_sec_max_length); + PARSE_THROTTLER_GROUP(write_iops_sec_max_length); + } + + for (i = 0; i < nparams; i++) { + g_autofree char *str = vshGetTypedParamValue(ctl, ¶ms[i]); + vshPrint(ctl, "%-15s: %s\n", params[i].field, str); + } + + ret = true; + + cleanup: + virTypedParamsFree(params, nparams); + return ret; +} +#undef PARSE_THROTTLEGROUP + /* * "blkiotune" command */ @@ -13436,6 +13815,30 @@ const vshCmdDef domManagementCmds[] = { .info = &info_blkdeviotune, .flags = 0 }, + {.name = "domthrottlegroupset", + .handler = cmdThrottleGroupSet, + .opts = opts_throttlegroupset, + .info = &info_throttlegroupset, + .flags = 0 + }, + {.name = "domthrottlegroupdel", + .handler = cmdThrottleGroupDel, + .opts = opts_throttlegroupdel, + .info = &info_throttlegroupdel, + .flags = 0 + }, + {.name = "domthrottlegroupinfo", + .handler = cmdThrottleGroupInfo, + .opts = opts_throttlegroupinfo, + .info = &info_throttlegroupinfo, + .flags = 0 + }, + {.name = "domthrottlegrouplist", + .handler = cmdThrottleGroupList, + .opts = opts_throttlegrouplist, + .info = &info_throttlegrouplist, + .flags = 0 + }, {.name = "blkiotune", .handler = cmdBlkiotune, .opts = opts_blkiotune, -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:21 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Implement new throttle cmds
* Add new virsh cmds: domthrottlegroupset, domthrottlegrouplist, domthrottlegroupinfo, domthrottlegroupdel * Add doc for new cmds at docs/manpages/virsh.rst * Add cmd helper "virshDomainThrottleGroupCompleter", which is used by domthrottlegroupset, domthrottlegroupinfo, domthrottlegroupdel
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* Update of code documentation comments. * Reimplement Get throttle group from XML.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- docs/manpages/virsh.rst | 134 +++++++++++ tools/virsh-completer-domain.c | 55 +++++ tools/virsh-completer-domain.h | 11 + tools/virsh-domain.c | 405 ++++++++++++++++++++++++++++++++- 4 files changed, 604 insertions(+), 1 deletion(-)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 0eb1d6ea93..8726870cf9 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1128,6 +1128,140 @@ given, but *--current* is exclusive. For querying only one of *--live*, is different depending on hypervisor.
+domthrottlegroupset +------------------- + +**Syntax:** + +:: + + domthrottlegroupset domain group-name [[--config] [--live] | [--current]] + [[total-bytes-sec] | [read-bytes-sec] [write-bytes-sec]] + [[total-iops-sec] | [read-iops-sec] [write-iops-sec]] + [[total-bytes-sec-max] | [read-bytes-sec-max] [write-bytes-sec-max]] + [[total-iops-sec-max] | [read-iops-sec-max] [write-iops-sec-max]] + [[total-bytes-sec-max-length] | + [read-bytes-sec-max-length] [write-bytes-sec-max-length]] + [[total-iops-sec-max-length] | + [read-iops-sec-max-length] [write-iops-sec-max-length]] + [size-iops-sec] + +Add or update a throttle group against specific *domain*. +*group-name* specifies a unique throttle group name, which defines limit, and +will be referenced by drives. + +If no limit is specified, default them as all zeros, which will fail, +Otherwise, set limits with these flags: +*--total-bytes-sec* specifies total throughput limit as a scaled integer, the +default being bytes per second if no suffix is specified. +*--read-bytes-sec* specifies read throughput limit as a scaled integer, the +default being bytes per second if no suffix is specified. +*--write-bytes-sec* specifies write throughput limit as a scaled integer, the +default being bytes per second if no suffix is specified. +*--total-iops-sec* specifies total I/O operations limit per second. +*--read-iops-sec* specifies read I/O operations limit per second. +*--write-iops-sec* specifies write I/O operations limit per second. +*--total-bytes-sec-max* specifies maximum total throughput limit as a scaled +integer, the default being bytes per second if no suffix is specified +*--read-bytes-sec-max* specifies maximum read throughput limit as a scaled +integer, the default being bytes per second if no suffix is specified. +*--write-bytes-sec-max* specifies maximum write throughput limit as a scaled +integer, the default being bytes per second if no suffix is specified. +*--total-iops-sec-max* specifies maximum total I/O operations limit per second. +*--read-iops-sec-max* specifies maximum read I/O operations limit per second. +*--write-iops-sec-max* specifies maximum write I/O operations limit per second. +*--total-bytes-sec-max-length* specifies duration in seconds to allow maximum +total throughput limit. +*--read-bytes-sec-max-length* specifies duration in seconds to allow maximum +read throughput limit. +*--write-bytes-sec-max-length* specifies duration in seconds to allow maximum +write throughput limit. +*--total-iops-sec-max-length* specifies duration in seconds to allow maximum +total I/O operations limit. +*--read-iops-sec-max-length* specifies duration in seconds to allow maximum +read I/O operations limit. +*--write-iops-sec-max-length* specifies duration in seconds to allow maximum +write I/O operations limit. +*--size-iops-sec* specifies size I/O operations limit per second. + +Bytes and iops values are independent, but setting only one value (such +as --read-bytes-sec) resets the other two in that category to unlimited. +An explicit 0 also clears any limit. A non-zero value for a given total +cannot be mixed with non-zero values for read or write. + +It is up to the hypervisor to determine how to handle the length values. +For the QEMU hypervisor, if an I/O limit value or maximum value is set, +then the default value of 1 second will be displayed. Supplying a 0 will +reset the value back to the default. + +If *--live* is specified, affect a running guest. +If *--config* is specified, affect the next start of a persistent guest. +If *--current* is specified, it is equivalent to either *--live* or +*--config*, depending on the current state of the guest. +When setting the disk io parameters both *--live* and *--config* +are specified, both live configuration and config are updated while setting +the description, but *--current* is exclusive. If no flag is specified, behavior +is different depending on hypervisor. + + +domthrottlegroupdel +------------------- + +**Syntax:** + +:: + + domthrottlegroupdel domain group-name [[--config] [--live] | [--current]] + +Delete a Throttlegroup from the domain using the specified *group-name*. +If an Throttlegroup is currently referenced by a disk resource, then the attempt +to remove the Throttlegroup will fail. +If the *group-name* does not exist an error will occur. + +If *--live* is specified, affect a running guest. If the guest is not +running an error is returned. +If *--config* is specified, affect the next start of a persistent guest. +If *--current* is specified, it is equivalent to either *--live* or +*--config*, depending on the current state of the guest. + + +domthrottlegroupinfo +-------------------- + +**Syntax:** + +:: + + domthrottlegroupinfo domain group-name [[--config] [--live] | [--current]] + +Display domain Throttlegroup information including I/O limits setting. + +If *--live* is specified, get the Throttlegroup data from the running guest. If +the guest is not running, an error is returned. +If *--config* is specified, get the Throttlegroup data from the next start of +a persistent guest. +If *--current* is specified or *--live* and *--config* are not specified, +then get the Throttlegroup data based on the current guest state, which can +either be live or offline. +If both *--live* and *--config* are specified, the *--config* option takes +precedence on getting the current description. + + +domthrottlegrouplist +-------------------- + +**Syntax:** + +:: + + domthrottlegrouplist domain [--inactive] + +Print a table showing names of all throttle groups +associated with *domain*. If *--inactive* is specified, query the +Throttlegroup data that will be used on the next boot, rather than those +currently in use by a running domain. + + blkiotune ---------
diff --git a/tools/virsh-completer-domain.c b/tools/virsh-completer-domain.c index 61362224a3..3b0df15c13 100644 --- a/tools/virsh-completer-domain.c +++ b/tools/virsh-completer-domain.c @@ -248,6 +248,61 @@ virshDomainMigrateDisksCompleter(vshControl *ctl, }
+int +virshGetThrottleGroupNames(xmlXPathContext *ctxt, + xmlNodePtr **groups, + char ***groupNames) +{ + int ngroups; + size_t i; + + ngroups = virXPathNodeSet("./throttlegroups/throttlegroup", ctxt, groups); + if (ngroups < 0) + return -1; + + *groupNames = g_new0(char *, ngroups + 1); + + for (i = 0; i < ngroups; i++) { + ctxt->node = (*groups)[i]; + if (!((*groupNames)[i] = virXPathString("string(./group_name)", ctxt))) { + g_strfreev(*groupNames); + *groupNames = NULL; + return -1; + } + } + + return ngroups; +} + + +char ** +virshDomainThrottleGroupCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags) +{ + virshControl *priv = ctl->privData; + g_autoptr(xmlDoc) xmldoc = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *groups = NULL; + g_auto(GStrv) groupNames = NULL; + int ngroups; + + virCheckFlags(0, NULL); + + if (!priv->conn || virConnectIsAlive(priv->conn) <= 0) + return NULL; + + if (virshDomainGetXML(ctl, cmd, 0, &xmldoc, &ctxt) < 0) + return NULL; + + ngroups = virshGetThrottleGroupNames(ctxt, &groups, &groupNames); + if (ngroups < 0) + return NULL; + + return g_steal_pointer(&groupNames); +} + + char ** virshDomainUndefineStorageDisksCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-completer-domain.h b/tools/virsh-completer-domain.h index 27cf963912..680b3fc018 100644 --- a/tools/virsh-completer-domain.h +++ b/tools/virsh-completer-domain.h @@ -21,6 +21,7 @@ #pragma once
#include "vsh.h" +#include <libxml/xpath.h>
char ** virshDomainNameCompleter(vshControl *ctl, @@ -41,6 +42,16 @@ virshDomainDiskTargetCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags);
+int +virshGetThrottleGroupNames(xmlXPathContext *ctxt, + xmlNodePtr **groups, + char ***groupNames); + +char ** +virshDomainThrottleGroupCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags); + char ** virshDomainInterfaceStateCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 9f522da544..b8f61340cc 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -1375,7 +1375,7 @@ static const vshCmdOptDef opts_blkdeviotune[] = { VIRSH_COMMON_OPT_DOMAIN_CURRENT, {.name = NULL} }; -#undef VSH_OPTS_IOTUNE +
static bool cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd) @@ -1513,6 +1513,385 @@ cmdBlkdeviotune(vshControl *ctl, const vshCmd *cmd) goto cleanup; }
+ +/* + * "throttlegrouplist" command + */ +static const vshCmdInfo info_throttlegrouplist = { + .help = N_("list all domain throttlegroups."), + .desc = N_("Get the summary of throttle groups for a domain."), +}; + + +static const vshCmdOptDef opts_throttlegrouplist[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "inactive", + .type = VSH_OT_BOOL, + .help = N_("get inactive rather than running configuration") + }, + {.name = NULL} +}; + + +static bool +cmdThrottleGroupList(vshControl *ctl, + const vshCmd *cmd) +{ + unsigned int flags = 0; + size_t i; + g_autoptr(xmlDoc) xml = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *groups = NULL; + g_auto(GStrv) groupNames = NULL; + ssize_t ngroups; + g_autoptr(vshTable) table = NULL; + + if (vshCommandOptBool(cmd, "inactive")) + flags |= VIR_DOMAIN_XML_INACTIVE; + + if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0) + return false; + + table = vshTableNew(_("Name"), NULL); + + if (!table) + return false; + + ngroups = virshGetThrottleGroupNames(ctxt, &groups, &groupNames); + for (i = 0; i < ngroups; i++) { + if (vshTableRowAppend(table, groupNames[i], NULL) < 0) + return false; + } + + vshTablePrintToStdout(table, ctl); + + return true; +} + + +/* + * "throttlegroupset" command + */ +static const vshCmdInfo info_throttlegroupset = { + .help = N_("Add or update a throttling group."), + .desc = N_("Add or updte a throttling group."), +}; + + +static const vshCmdOptDef opts_throttlegroupset[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "group-name", + .type = VSH_OT_STRING, + .positional = true, + .required = true, + .completer = virshDomainThrottleGroupCompleter, + .help = N_("throttle group name") + }, + VSH_OPTS_IOTUNE(), + VIRSH_COMMON_OPT_DOMAIN_CONFIG, + VIRSH_COMMON_OPT_DOMAIN_LIVE, + VIRSH_COMMON_OPT_DOMAIN_CURRENT, + {.name = NULL} +}; +#undef VSH_OPTS_IOTUNE + + +static bool +cmdThrottleGroupSet(vshControl *ctl, + const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + const char *group_name = NULL; + unsigned long long value; + int nparams = 0; + int maxparams = 0; + virTypedParameterPtr params = NULL; + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + int rv = 0; + bool current = vshCommandOptBool(cmd, "current"); + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool ret = false; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + +#define VSH_SET_THROTTLE_GROUP_SCALED(PARAM, CONST) \ + if ((rv = vshCommandOptScaledInt(ctl, cmd, #PARAM, &value, \ + 1, ULLONG_MAX)) < 0) { \ + goto interror; \ + } else if (rv > 0) { \ + if (virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \ + value) < 0) \ + goto save_error; \ + } + + VSH_SET_THROTTLE_GROUP_SCALED(total-bytes-sec, TOTAL_BYTES_SEC); + VSH_SET_THROTTLE_GROUP_SCALED(read-bytes-sec, READ_BYTES_SEC); + VSH_SET_THROTTLE_GROUP_SCALED(write-bytes-sec, WRITE_BYTES_SEC); + VSH_SET_THROTTLE_GROUP_SCALED(total-bytes-sec-max, TOTAL_BYTES_SEC_MAX); + VSH_SET_THROTTLE_GROUP_SCALED(read-bytes-sec-max, READ_BYTES_SEC_MAX); + VSH_SET_THROTTLE_GROUP_SCALED(write-bytes-sec-max, WRITE_BYTES_SEC_MAX); +#undef VSH_SET_THROTTLE_GROUP_SCALED + +#define VSH_SET_THROTTLE_GROUP(PARAM, CONST) \ + if ((rv = vshCommandOptULongLong(ctl, cmd, #PARAM, &value)) < 0) { \ + goto interror; \ + } else if (rv > 0) { \ + if (virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + VIR_DOMAIN_BLOCK_IOTUNE_##CONST, \ + value) < 0) \ + goto save_error; \ + } + + VSH_SET_THROTTLE_GROUP(total-iops-sec, TOTAL_IOPS_SEC); + VSH_SET_THROTTLE_GROUP(read-iops-sec, READ_IOPS_SEC); + VSH_SET_THROTTLE_GROUP(write-iops-sec, WRITE_IOPS_SEC); + VSH_SET_THROTTLE_GROUP(total-iops-sec-max, TOTAL_IOPS_SEC_MAX); + VSH_SET_THROTTLE_GROUP(read-iops-sec-max, READ_IOPS_SEC_MAX); + VSH_SET_THROTTLE_GROUP(write-iops-sec-max, WRITE_IOPS_SEC_MAX); + VSH_SET_THROTTLE_GROUP(size-iops-sec, SIZE_IOPS_SEC); + + VSH_SET_THROTTLE_GROUP(total-bytes-sec-max-length, TOTAL_BYTES_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(read-bytes-sec-max-length, READ_BYTES_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(write-bytes-sec-max-length, WRITE_BYTES_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(total-iops-sec-max-length, TOTAL_IOPS_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(read-iops-sec-max-length, READ_IOPS_SEC_MAX_LENGTH); + VSH_SET_THROTTLE_GROUP(write-iops-sec-max-length, WRITE_IOPS_SEC_MAX_LENGTH); +#undef VSH_SET_THROTTLE_GROUP + + if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) { + goto cleanup; + } + + if (group_name) { + if (virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME, + group_name) < 0) + goto save_error; + } + + if (virDomainSetThrottleGroup(dom, group_name, params, nparams, flags) < 0) + goto error; + vshPrintExtra(ctl, "%s", _("Throttle group set successfully\n")); + + ret = true; + + cleanup: + virTypedParamsFree(params, nparams); + return ret; + + save_error: + vshSaveLibvirtError(); + error: + vshError(ctl, "%s", _("Unable to set throttle group")); + goto cleanup; + + interror: + vshError(ctl, "%s", _("Unable to parse integer parameter")); + goto cleanup; +} + + +/* + * "throttlegroupdel" command + */ +static const vshCmdInfo info_throttlegroupdel = { + .help = N_("Delete a throttling group."), + .desc = N_("Delete a throttling group."), +}; + + +static const vshCmdOptDef opts_throttlegroupdel[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "group-name", + .type = VSH_OT_STRING, + .positional = true, + .required = true, + .completer = virshDomainThrottleGroupCompleter, + .help = N_("throttle group name") + }, + VIRSH_COMMON_OPT_DOMAIN_CONFIG, + VIRSH_COMMON_OPT_DOMAIN_LIVE, + VIRSH_COMMON_OPT_DOMAIN_CURRENT, + {.name = NULL} +}; + + +static bool +cmdThrottleGroupDel(vshControl *ctl, + const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + const char *group_name = NULL; + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool current = vshCommandOptBool(cmd, "current"); + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) { + return false; + }
Inconsistent coding style
+ + if (virDomainDelThrottleGroup(dom, group_name, flags) < 0) + return false; + vshPrintExtra(ctl, "%s", _("Throttle group deleted successfully\n")); + + return true; +} + + +/* + * "throttlegroupinfo" command
This doesn't match the command name
+ */ +static const vshCmdInfo info_throttlegroupinfo = { + .help = N_("Get a throttling group."), + .desc = N_("Get a throttling group."), +}; + + +static const vshCmdOptDef opts_throttlegroupinfo[] = {
The struct name doesn't match the actual command
+ VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "group-name", + .type = VSH_OT_STRING, + .positional = true, + .required = true, + .completer = virshDomainThrottleGroupCompleter, + .help = N_("throttle group name") + }, + VIRSH_COMMON_OPT_DOMAIN_CONFIG, + VIRSH_COMMON_OPT_DOMAIN_LIVE, + VIRSH_COMMON_OPT_DOMAIN_CURRENT, + {.name = NULL} +}; + + +#define PARSE_THROTTLER_GROUP(val) \ + if (virXPathULongLong("string(./" #val ")", \ + ctxt, &throttleGroup->val) == -2) { \ + virReportError(VIR_ERR_XML_ERROR, \ + _("throttle group field '%1$s' must be an integer"), #val); \ + goto cleanup; \ + } \ + if ((virTypedParamsAddULLong(¶ms, &nparams, &maxparams, \ + #val, throttleGroup->val) < 0)) { \ + vshError(ctl, "%s", _("Unable to get throttle group parameters")); \ + goto cleanup; \ + } + +static bool +cmdThrottleGroupInfo(vshControl *ctl, + const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + const char *group_name = NULL; + int nparams = 0; + virTypedParameterPtr params = NULL; + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + size_t i; + bool current = vshCommandOptBool(cmd, "current"); + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool ret = false; + + g_autoptr(xmlDoc) xml = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autofree xmlNodePtr *node = NULL; + int n = 0; + int maxparams = 0; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE;
'flags' is effectively passed to virDomainGetXMLDesc, which doesn't take any of the AFFECT_ flags. While VIR_DOMAIN_AFFECT_CONFIG accidentally maps to VIR_DOMAIN_XML_INACTIVE; VIR_DOMAIN_AFFECT_LIVE maps to VIR_DOMAIN_XML_SECURE. --live doesn't make sense for this
+ + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(ctl, cmd, "group-name", &group_name) < 0) { + goto cleanup; + }
Inconsistent coding style
+ + if (virshDomainGetXML(ctl, cmd, flags, &xml, &ctxt) < 0) + goto cleanup; + + if ((n = virXPathNodeSet("/domain/throttlegroups/throttlegroup", ctxt, &node)) < 0) + goto cleanup; + + if (n == 0) + return 0; + + for (i = 0; i < n; i++) { + g_autoptr(virDomainThrottleGroupDef) throttleGroup = g_new0(virDomainThrottleGroupDef, 1); + + VIR_XPATH_NODE_AUTORESTORE(ctxt) + ctxt->node = node[i]; + + if (STREQ(group_name, virXPathString("string(./group_name)", ctxt)) == 0) {
virXPathString allocates a buffer for the string and returns it; this is a memleak as it's not freed after use ...
+ continue; + } + + PARSE_THROTTLER_GROUP(total_bytes_sec); + PARSE_THROTTLER_GROUP(read_bytes_sec); + PARSE_THROTTLER_GROUP(write_bytes_sec); + PARSE_THROTTLER_GROUP(total_iops_sec); + PARSE_THROTTLER_GROUP(read_iops_sec); + PARSE_THROTTLER_GROUP(write_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max); + PARSE_THROTTLER_GROUP(read_bytes_sec_max); + PARSE_THROTTLER_GROUP(write_bytes_sec_max); + PARSE_THROTTLER_GROUP(total_iops_sec_max); + PARSE_THROTTLER_GROUP(read_iops_sec_max); + PARSE_THROTTLER_GROUP(write_iops_sec_max); + + PARSE_THROTTLER_GROUP(size_iops_sec); + + PARSE_THROTTLER_GROUP(total_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(read_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(write_bytes_sec_max_length); + PARSE_THROTTLER_GROUP(total_iops_sec_max_length); + PARSE_THROTTLER_GROUP(read_iops_sec_max_length); + PARSE_THROTTLER_GROUP(write_iops_sec_max_length);
I don't quite understand the point of parsing this from XML and putting it into typed parameters ...
+ } + + for (i = 0; i < nparams; i++) { + g_autofree char *str = vshGetTypedParamValue(ctl, ¶ms[i]); + vshPrint(ctl, "%-15s: %s\n", params[i].field, str);
... just to print it back from the parameter.
+ } + + ret = true; + + cleanup: + virTypedParamsFree(params, nparams); + return ret; +} +#undef PARSE_THROTTLEGROUP + /* * "blkiotune" command */

From: Chun Feng Wu <danielwuwy@163.com> Update "attach_disk" to support new option: throttle-groups to form filter chain in QEMU for specific disk Signed-off-by: Chun Feng Wu <danielwuwy@163.com> * apply suggested coding style changes. Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- docs/manpages/virsh.rst | 3 ++- tools/virsh-completer-domain.c | 27 +++++++++++++++++++++++++++ tools/virsh-completer-domain.h | 5 +++++ tools/virsh-domain.c | 25 ++++++++++++++++++++++++- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 8726870cf9..7d5a3ff74a 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -5028,7 +5028,7 @@ attach-disk [--source-protocol protocol] [--source-host-name hostname:port] [--source-host-transport transport] [--source-host-socket socket] [--serial serial] [--wwn wwn] [--rawio] [--address address] - [--multifunction] [--print-xml] + [--multifunction] [--print-xml] [--throttle-groups groups] Attach a new disk device to the domain. *source* is path for the files and devices unless *--source-protocol* @@ -5068,6 +5068,7 @@ ide:controller.bus.unit, usb:bus.port, sata:controller.bus.unit or ccw:cssid.ssid.devno. Virtio-ccw devices must have their cssid set to 0xfe. *multifunction* indicates specified pci address is a multifunction pci device address. +*throttle-groups* is comma separated list of throttle groups to be applied. There is also support for using a network disk. As specified, the user can provide a *--source-protocol* in which case the *source* parameter will diff --git a/tools/virsh-completer-domain.c b/tools/virsh-completer-domain.c index 3b0df15c13..b96180be9e 100644 --- a/tools/virsh-completer-domain.c +++ b/tools/virsh-completer-domain.c @@ -303,6 +303,33 @@ virshDomainThrottleGroupCompleter(vshControl *ctl, } +static char ** +virshDomainThrottleGroupListCompleter(vshControl *ctl, + const vshCmd *cmd, + const char *argname) +{ + const char *curval = NULL; + g_auto(GStrv) groups = virshDomainThrottleGroupCompleter(ctl, cmd, 0); + + if (vshCommandOptStringQuiet(ctl, cmd, argname, &curval) < 0) + return NULL; + + if (!groups) + return NULL; + + return virshCommaStringListComplete(curval, (const char **) groups); +} + + +char ** +virshDomainThrottleGroupsCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int completeflags G_GNUC_UNUSED) +{ + return virshDomainThrottleGroupListCompleter(ctl, cmd, "throttle-groups"); +} + + char ** virshDomainUndefineStorageDisksCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-completer-domain.h b/tools/virsh-completer-domain.h index 680b3fc018..23b432d05c 100644 --- a/tools/virsh-completer-domain.h +++ b/tools/virsh-completer-domain.h @@ -52,6 +52,11 @@ virshDomainThrottleGroupCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags); +char ** +virshDomainThrottleGroupsCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags); + char ** virshDomainInterfaceStateCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index b8f61340cc..e156199eb1 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -522,6 +522,11 @@ static const vshCmdOptDef opts_attach_disk[] = { .type = VSH_OT_STRING, .help = N_("host socket for source of disk device") }, + {.name = "throttle-groups", + .type = VSH_OT_STRING, + .completer = virshDomainThrottleGroupsCompleter, + .help = N_("comma separated list of throttle groups to be applied") + }, VIRSH_COMMON_OPT_DOMAIN_PERSISTENT, VIRSH_COMMON_OPT_DOMAIN_CONFIG, VIRSH_COMMON_OPT_DOMAIN_LIVE, @@ -611,6 +616,8 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) const char *host_name = NULL; const char *host_transport = NULL; const char *host_socket = NULL; + const char *throttle_groups_str = NULL; + g_auto(GStrv) throttle_groups = NULL; int ret; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; const char *stype = NULL; @@ -665,9 +672,14 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) vshCommandOptString(ctl, cmd, "source-protocol", &source_protocol) < 0 || vshCommandOptString(ctl, cmd, "source-host-name", &host_name) < 0 || vshCommandOptString(ctl, cmd, "source-host-transport", &host_transport) < 0 || - vshCommandOptString(ctl, cmd, "source-host-socket", &host_socket) < 0) + vshCommandOptString(ctl, cmd, "source-host-socket", &host_socket) < 0 || + vshCommandOptString(ctl, cmd, "throttle-groups", &throttle_groups_str) < 0) return false; + if (throttle_groups_str) { + throttle_groups = g_strsplit(throttle_groups_str, ",", 0); + } + if (stype && (type = virshAttachDiskSourceTypeFromString(stype)) < 0) { vshError(ctl, _("Unknown source type: '%1$s'"), stype); @@ -714,6 +726,17 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) virXMLFormatElement(&diskChildBuf, "driver", &driverAttrBuf, NULL); + if (throttle_groups) { + g_auto(virBuffer) throttleChildBuf = VIR_BUFFER_INITIALIZER; + char **iter; + for (iter = throttle_groups; *iter != NULL; iter++) { + g_auto(virBuffer) throttleAttrBuf = VIR_BUFFER_INITIALIZER; + virBufferAsprintf(&throttleAttrBuf, " group='%s'", *iter); + virXMLFormatElement(&throttleChildBuf, "throttlefilter", &throttleAttrBuf, NULL); + } + virXMLFormatElement(&diskChildBuf, "throttlefilters", NULL, &throttleChildBuf); + } + switch ((enum virshAttachDiskSourceType) type) { case VIRSH_ATTACH_DISK_SOURCE_TYPE_FILE: virBufferEscapeString(&sourceAttrBuf, " file='%s'", source); -- 2.39.5 (Apple Git-154)

On Wed, Feb 19, 2025 at 22:27:22 +0530, Harikumar Rajkumar wrote:
From: Chun Feng Wu <danielwuwy@163.com>
Update "attach_disk" to support new option: throttle-groups to form filter chain in QEMU for specific disk
Signed-off-by: Chun Feng Wu <danielwuwy@163.com>
* apply suggested coding style changes.
Signed-off-by: Harikumar Rajkumar <harirajkumar230@gmail.com> --- docs/manpages/virsh.rst | 3 ++- tools/virsh-completer-domain.c | 27 +++++++++++++++++++++++++++ tools/virsh-completer-domain.h | 5 +++++ tools/virsh-domain.c | 25 ++++++++++++++++++++++++- 4 files changed, 58 insertions(+), 2 deletions(-)
[...]
index 3b0df15c13..b96180be9e 100644 --- a/tools/virsh-completer-domain.c +++ b/tools/virsh-completer-domain.c @@ -303,6 +303,33 @@ virshDomainThrottleGroupCompleter(vshControl *ctl, }
+static char ** +virshDomainThrottleGroupListCompleter(vshControl *ctl, + const vshCmd *cmd, + const char *argname) +{ + const char *curval = NULL; + g_auto(GStrv) groups = virshDomainThrottleGroupCompleter(ctl, cmd, 0); + + if (vshCommandOptStringQuiet(ctl, cmd, argname, &curval) < 0) + return NULL; + + if (!groups) + return NULL; + + return virshCommaStringListComplete(curval, (const char **) groups); +} + + +char ** +virshDomainThrottleGroupsCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int completeflags G_GNUC_UNUSED) +{ + return virshDomainThrottleGroupListCompleter(ctl, cmd, "throttle-groups"); +}
This wrapper is pointless and can be folded into the above function.
+ + char ** virshDomainUndefineStorageDisksCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-completer-domain.h b/tools/virsh-completer-domain.h index 680b3fc018..23b432d05c 100644 --- a/tools/virsh-completer-domain.h +++ b/tools/virsh-completer-domain.h @@ -52,6 +52,11 @@ virshDomainThrottleGroupCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags);
+char ** +virshDomainThrottleGroupsCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags); + char ** virshDomainInterfaceStateCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index b8f61340cc..e156199eb1 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -522,6 +522,11 @@ static const vshCmdOptDef opts_attach_disk[] = { .type = VSH_OT_STRING, .help = N_("host socket for source of disk device") }, + {.name = "throttle-groups", + .type = VSH_OT_STRING, + .completer = virshDomainThrottleGroupsCompleter, + .help = N_("comma separated list of throttle groups to be applied") + }, VIRSH_COMMON_OPT_DOMAIN_PERSISTENT, VIRSH_COMMON_OPT_DOMAIN_CONFIG, VIRSH_COMMON_OPT_DOMAIN_LIVE, @@ -611,6 +616,8 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) const char *host_name = NULL; const char *host_transport = NULL; const char *host_socket = NULL; + const char *throttle_groups_str = NULL; + g_auto(GStrv) throttle_groups = NULL; int ret; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; const char *stype = NULL; @@ -665,9 +672,14 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) vshCommandOptString(ctl, cmd, "source-protocol", &source_protocol) < 0 || vshCommandOptString(ctl, cmd, "source-host-name", &host_name) < 0 || vshCommandOptString(ctl, cmd, "source-host-transport", &host_transport) < 0 || - vshCommandOptString(ctl, cmd, "source-host-socket", &host_socket) < 0) + vshCommandOptString(ctl, cmd, "source-host-socket", &host_socket) < 0 || + vshCommandOptString(ctl, cmd, "throttle-groups", &throttle_groups_str) < 0) return false;
+ if (throttle_groups_str) { + throttle_groups = g_strsplit(throttle_groups_str, ",", 0); + } + if (stype && (type = virshAttachDiskSourceTypeFromString(stype)) < 0) { vshError(ctl, _("Unknown source type: '%1$s'"), stype); @@ -714,6 +726,17 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd)
virXMLFormatElement(&diskChildBuf, "driver", &driverAttrBuf, NULL);
+ if (throttle_groups) { + g_auto(virBuffer) throttleChildBuf = VIR_BUFFER_INITIALIZER;
This ought to use VIR_BUFFER_INIT_CHILD(&diskCHildBuf) otherwise the resulting XML will be malformated. (visible with --print-xml)
+ char **iter; + for (iter = throttle_groups; *iter != NULL; iter++) { + g_auto(virBuffer) throttleAttrBuf = VIR_BUFFER_INITIALIZER;
+ virBufferAsprintf(&throttleAttrBuf, " group='%s'", *iter); + virXMLFormatElement(&throttleChildBuf, "throttlefilter", &throttleAttrBuf, NULL); + } + virXMLFormatElement(&diskChildBuf, "throttlefilters", NULL, &throttleChildBuf); + }
This whole block should go to the same spot as where it's formatted in the domain XML; behind 'target'.
+ switch ((enum virshAttachDiskSourceType) type) { case VIRSH_ATTACH_DISK_SOURCE_TYPE_FILE: virBufferEscapeString(&sourceAttrBuf, " file='%s'", source); -- 2.39.5 (Apple Git-154)

Hi Team, Could you kindly confirm if the new version of the code is currently under review? Thanks and Regards, Harikumar R

Hi Team,
Could you kindly confirm if the new version of the code is currently under review?
Thanks and Regards, Harikumar R
I just wanted to follow up on my previous message regarding the new version of the code. Could you kindly confirm if it’s currently under review? I would appreciate any updates when you have a moment. Thank you! Best regards, Harikumar R

On Mon, Mar 03, 2025 at 15:23:15 -0000, Harikumar Rajkumar wrote:
Hi Team,
Could you kindly confirm if the new version of the code is currently under review?
Thanks and Regards, Harikumar R
I just wanted to follow up on my previous message regarding the new version of the code. Could you kindly confirm if it’s currently under review? I would appreciate any updates when you have a moment.
Sorry I was busy. I'll pick this up. Please do not send any new versions until I'm done; I'll be updating version info and ohter small things. If something larger turns up I'll give you a branch with the small things updated.

On Mon, Mar 03, 2025 at 15:23:15 -0000, Harikumar Rajkumar wrote:
Sorry I was busy. I'll pick this up.
Please do not send any new versions until I'm done; I'll be updating version info and ohter small things. If something larger turns up I'll give you a branch with the small things updated.
Sure. Thanks

Hi Team, Could we please get an ETA on when the review will be completed? We would greatly appreciate it if the review could be completed as soon as possible. Apologies for the urgency, but we have a dependency on these changes. Thank you for your understanding! Best regards, Harikumar R

On Wed, Mar 12, 2025 at 08:15:12 -0000, Harikumar Rajkumar wrote:
Hi Team, Could we please get an ETA on when the review will be completed?
No, not really. In the patches I went through I updated the docs to mention 11.2.0. I hope I will not have to update it further. It depends on how the rest will look but the first patches required modification besides fixing version numbers.
We would greatly appreciate it if the review could be completed as soon as possible. Apologies for the urgency, but we have a dependency on these changes.
Please understand that this is a niche feature that entails a lot of code. Based on your interaction with upstream it is also very likely, once merged, will be inherited for us to maintain. As review and maintenance is something that most projects struggle with the best way to incentivze an upstream to merge a niche complex feature is to contribute also manitenance and review resources to the projec to offset the maintenance burden of the code you are proposing to merge.
Thank you for your understanding!
Likewise I'd like to thank you for understanding the two paragraphs above.

Thank you for the update. I understand that the review process is complex, and the feature potentially requiring ongoing maintenance. I appreciate the effort and work that has been invested so far.

On Wed, Feb 19, 2025 at 22:27:04 +0530, Harikumar Rajkumar wrote:
*** Support block disk along with throttle filters ***
Chun Feng Wu (17): schema: Add new domain elements to support multiple throttle groups schema: Add new domain elements to support multiple throttle filters config: Introduce ThrottleGroup and corresponding XML parsing config: Introduce ThrottleFilter and corresponding XML parsing qemu: monitor: Add support for ThrottleGroup operations tests: Test qemuMonitorJSONGetThrottleGroup and qemuMonitorJSONUpdateThrottleGroup remote: New APIs for ThrottleGroup lifecycle management qemu: Refactor qemuDomainSetBlockIoTune to extract common methods qemu: Implement qemu driver for throttle API qemu: helper: throttle filter nodename and preparation processing qemu: block: Support block disk along with throttle filters config: validate: Verify iotune, throttle group and filter qemuxmlconftest: Add 'throttlefilter' tests test_driver: Test throttle group lifecycle APIs virsh: Refactor iotune options for re-use virsh: Add support for throttle group operations virsh: Add option "throttle-groups" to "attach_disk"
Harikumar Rajkumar (1): qemustatusxml2xmldata: Add 'throttlefilter' tests
While testing I've noticed that the new filters didn't properly work after 'virDomainBlockCopy'. As this happens also for other filters (currently copy-on-read) it's arguably a pre-existing bug. The following series, and specifically patch: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/message/ZLUL... Will need to be applied first.
participants (2)
-
Harikumar Rajkumar
-
Peter Krempa