[PATCH v6 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 R (1): tests: Test qemuxmlactivetestThrottleGroup docs/formatdomain.rst | 47 ++ docs/manpages/virsh.rst | 135 +++- include/libvirt/libvirt-domain.h | 21 + src/conf/domain_conf.c | 405 +++++++++++ src/conf/domain_conf.h | 45 ++ src/conf/domain_validate.c | 119 ++- src/conf/schemas/domaincommon.rng | 293 ++++---- src/conf/virconftypes.h | 4 + src/driver-hypervisor.h | 22 + src/libvirt-domain.c | 174 +++++ src/libvirt_private.syms | 8 + src/libvirt_public.syms | 7 + 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 | 73 +- src/qemu/qemu_driver.c | 619 +++++++++++++--- src/qemu/qemu_hotplug.c | 33 + src/qemu/qemu_monitor.c | 34 + src/qemu/qemu_monitor.h | 14 + src/qemu/qemu_monitor_json.c | 134 ++++ src/qemu/qemu_monitor_json.h | 14 + src/remote/remote_daemon_dispatch.c | 44 ++ src/remote/remote_driver.c | 40 ++ src/remote/remote_protocol.x | 48 +- src/remote_protocol-structs | 28 + src/test/test_driver.c | 452 ++++++++---- 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 | 680 ++++++++++++++---- 41 files changed, 4656 insertions(+), 525 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> --- 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 60bee8bd4f..3f9cc90502 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -1964,6 +1964,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 10.7.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 b3fdbf7ffb..e2b0832b5c 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"/> @@ -6833,6 +6834,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 @@ -7800,134 +7818,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)

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> --- 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 3f9cc90502..84aa5d85ac 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -2772,6 +2772,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> ... @@ -3274,6 +3283,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 10.7.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 e2b0832b5c..bc57bb812e 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"/> @@ -6851,6 +6854,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)

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> --- src/conf/domain_conf.c | 293 ++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 27 ++++ src/conf/virconftypes.h | 2 + 3 files changed, 322 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4ad8289b89..e5a002876f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3790,6 +3790,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) { @@ -4077,6 +4103,8 @@ void virDomainDefFree(virDomainDef *def) virDomainIOThreadIDDefArrayFree(def->iothreadids, def->niothreadids); + virDomainThrottleGroupDefArrayFree(def->throttlegroups, def->nthrottlegroups); + g_free(def->defaultIOThread); virBitmapFree(def->cputune.emulatorpin); @@ -7822,6 +7850,120 @@ virDomainDiskDefIotuneParse(virDomainDiskDef *def, #undef PARSE_IOTUNE +#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) + 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, xmlNodePtr cur, @@ -19163,6 +19305,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; @@ -22438,6 +22583,95 @@ virDomainIOThreadIDDel(virDomainDef *def, } +/** + * virDomainThrottleGroupDefCopy: + * @src: throttle group to be copiped from + * @dst: throttle group to be copiped to + * + * copy throttle group content from @src to @dst + */ +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 + * + * search existing throttle group within domain definition + * by group_name and then overwrite it with @info, + * it's to update existing throttle group + */ +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 + * + * search existing throttle group within domain definition + * by group_name and then delete it with @name, + * it's to delete existing throttle group + */ +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, @@ -27624,6 +27858,63 @@ virDomainDefIOThreadsFormat(virBuffer *buf, } +#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(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); + ssize_t n; + + for (n = 0; n < def->nthrottlegroups; n++) { + virDomainThrottleGroupFormat(&childrenBuf, def->throttlegroups[n]); + } + + virXMLFormatElement(buf, "throttlegroups", NULL, &childrenBuf); +} + + static void virDomainIOMMUDefFormat(virBuffer *buf, const virDomainIOMMUDef *iommu) @@ -28316,6 +28607,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 a187ab4083..2f40d304b4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3067,6 +3067,9 @@ struct _virDomainDef { virDomainDefaultIOThreadDef *defaultIOThread; + size_t nthrottlegroups; + virDomainThrottleGroupDef **throttlegroups; + virDomainCputune cputune; virDomainResctrlDef **resctrls; @@ -4581,3 +4584,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)

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> --- src/conf/domain_conf.c | 105 ++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 18 +++++++ src/conf/virconftypes.h | 2 + 3 files changed, 125 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e5a002876f..a4de5c01c3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3816,6 +3816,16 @@ virDomainThrottleGroupDefArrayFree(virDomainThrottleGroupDef **def, } +void +virDomainThrottleFilterDefFree(virDomainThrottleFilterDef *def) +{ + if (!def) + return; + g_free(def->group_name); + g_free(def->nodename); +} + + void virDomainResourceDefFree(virDomainResourceDef *resource) { @@ -7964,6 +7974,53 @@ virDomainDefThrottleGroupsParse(virDomainDef *def, } +static virDomainThrottleFilterDef * +virDomainDiskThrottleFilterDefParse(xmlNodePtr node) +{ + g_autoptr(virDomainThrottleFilterDef) filter = g_new0(virDomainThrottleFilterDef, 1); + + filter->group_name = virXMLPropString(node, "group"); + + if (!filter->group_name) + return NULL; + + 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, @@ -8454,6 +8511,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); @@ -22672,6 +22732,34 @@ 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; + + if (!def->throttlefilters || def->nthrottlefilters == 0) + return NULL; + + 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, @@ -23322,6 +23410,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) @@ -23608,6 +23711,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 2f40d304b4..f1e85ab42f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -520,6 +520,13 @@ 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 */ +}; + + /* Stores the virtual disk configuration */ struct _virDomainDiskDef { virStorageSource *src; /* non-NULL. XXX Allow NULL for empty cdrom? */ @@ -552,6 +559,9 @@ struct _virDomainDiskDef { virDomainBlockIoTuneInfo blkdeviotune; + size_t nthrottlefilters; + virDomainThrottleFilterDef **throttlefilters; + char *driverName; char *serial; @@ -4608,3 +4618,11 @@ virDomainThrottleGroupByName(const virDomainDef *def, void virDomainThrottleGroupDefCopy(const virDomainThrottleGroupDef *src, virDomainThrottleGroupDef *dst); + +void +virDomainThrottleFilterDefFree(virDomainThrottleFilterDef *def); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainThrottleFilterDef, virDomainThrottleFilterDefFree); + +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)

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> --- src/qemu/qemu_monitor.c | 34 +++++++++ src/qemu/qemu_monitor.h | 14 ++++ src/qemu/qemu_monitor_json.c | 134 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 14 ++++ 4 files changed, 196 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 73f37d26eb..6f3e04b888 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3043,6 +3043,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 89a59dfd27..f608ff7179 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1071,6 +1071,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 1b4288b744..6cfe01cbca 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4676,6 +4676,140 @@ int qemuMonitorJSONGetBlockIoThrottle(qemuMonitor *mon, return qemuMonitorJSONBlockIoThrottleInfo(devices, qdevid, reply); } + +int +qemuMonitorMakeThrottleGroupLimits(virJSONValue *limits, + const virDomainThrottleGroupDef *group) +{ + if (virJSONValueObjectAdd(&limits, + "P:bps-total", group->total_bytes_sec, + "P:bps-read", group->read_bytes_sec, + "P:bps-write", group->write_bytes_sec, + "P:iops-total", group->total_iops_sec, + "P:iops-read", group->read_iops_sec, + "P:iops-write", group->write_iops_sec, + "P:bps-total-max", group->total_bytes_sec_max, + "P:bps-read-max", group->read_bytes_sec_max, + "P:bps-write-max", group->write_bytes_sec_max, + "P:iops-total-max", group->total_iops_sec_max, + "P:iops-read-max", group->read_iops_sec_max, + "P:iops-write-max", group->write_iops_sec_max, + "P:iops-size", group->size_iops_sec, + /* avoid error from QEMU: "the burst length cannot be 0" for throttlelimits + * when setting max-length + */ + "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(); + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name = g_strdup_printf("throttle-%s", qomid); + + if (qemuMonitorMakeThrottleGroupLimits(limits, info) < 0) + return -1; + + if (!(cmd = qemuMonitorJSONMakeCommand("qom-set", + "s:property", "limits", + "s:path", prefixed_group_name, + "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 0214e9e9ff..cfb724ed37 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)

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> --- tests/qemumonitorjsontest.c | 86 +++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index f7fe0fb6f4..cf6fce3d77 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1884,6 +1884,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), + "limit1", &info) < 0) + return -1; + + return 0; +} + + static int testQemuMonitorJSONqemuMonitorJSONGetTargetArch(const void *opaque) { @@ -2981,6 +3066,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)

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> --- include/libvirt/libvirt-domain.h | 21 ++++ src/driver-hypervisor.h | 22 ++++ src/libvirt-domain.c | 174 ++++++++++++++++++++++++++++ src/libvirt_private.syms | 8 ++ src/libvirt_public.syms | 7 ++ src/remote/remote_daemon_dispatch.c | 44 +++++++ src/remote/remote_driver.c | 40 +++++++ src/remote/remote_protocol.x | 48 +++++++- src/remote_protocol-structs | 28 +++++ 9 files changed, 391 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index d4f1573954..f00479ce35 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -6573,4 +6573,25 @@ virDomainGraphicsReload(virDomainPtr domain, unsigned int type, unsigned int flags); + +int +virDomainSetThrottleGroup(virDomainPtr dom, + const char *group, + virTypedParameterPtr params, + int nparams, + unsigned int flags); + +int +virDomainGetThrottleGroup(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..b3b1912666 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1453,6 +1453,25 @@ 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 +(*virDrvDomainGetThrottleGroup)(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 +1745,7 @@ struct _virHypervisorDriver { virDrvDomainStartDirtyRateCalc domainStartDirtyRateCalc; virDrvDomainFDAssociate domainFDAssociate; virDrvDomainGraphicsReload domainGraphicsReload; + virDrvDomainSetThrottleGroup domainSetThrottleGroup; + virDrvDomainGetThrottleGroup domainGetThrottleGroup; + virDrvDomainDelThrottleGroup domainDelThrottleGroup; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 7c6b93963c..ce18d18854 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -14162,3 +14162,177 @@ 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 or a subset 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: 10.7.0 + */ +int +virDomainSetThrottleGroup(virDomainPtr dom, + const char *group, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "params=%p, nparams=%d, flags=0x%x", + params, 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; +} + + +/** + * virDomainGetThrottleGroup: + * @dom: pointer to domain object + * @group: throttle group name + * @params: pointer that will be filled with an array of typed parameters + * @nparams: pointer filled with number of elements in @params + * @flags: bitwise-OR of virDomainModificationImpact + * + * Get all block IO tunable parameters for specific throttle group. @group cannot be NULL. + * @nparams gives how many slots were filled with parameter information + * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG. + * Both flags may be set. + * + * + * Returns -1 in case of error, 0 in case of success. + * + * Since: 10.7.0 + */ +int +virDomainGetThrottleGroup(virDomainPtr dom, + const char *group, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "params=%p, nparams=%d, flags=0x%x", + params, (nparams) ? *nparams : -1, flags); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + virCheckNonNullArgGoto(group, error); + virCheckNonNullArgGoto(nparams, error); + virCheckNonNullArgGoto(params, error); + + conn = dom->conn; + + if (conn->driver->domainGetThrottleGroup) { + int ret; + ret = conn->driver->domainGetThrottleGroup(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: 10.7.0 + */ +int +virDomainDelThrottleGroup(virDomainPtr dom, + const char *group, + unsigned int flags) +{ + virConnectPtr conn; + + 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 c931003fad..071538c2da 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -677,6 +677,14 @@ virDomainTaintMessageTypeFromString; virDomainTaintMessageTypeToString; virDomainTaintTypeFromString; virDomainTaintTypeToString; +virDomainThrottleFilterDefFree; +virDomainThrottleFilterFind; +virDomainThrottleGroupAdd; +virDomainThrottleGroupByName; +virDomainThrottleGroupDefCopy; +virDomainThrottleGroupDefFree; +virDomainThrottleGroupDel; +virDomainThrottleGroupUpdate; virDomainTimerModeTypeFromString; virDomainTimerModeTypeToString; virDomainTimerNameTypeFromString; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 7a3492d9d7..f17b6feaef 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -948,4 +948,11 @@ LIBVIRT_10.2.0 { virDomainGraphicsReload; } LIBVIRT_10.1.0; +LIBVIRT_10.7.0 { + global: + virDomainSetThrottleGroup; + virDomainGetThrottleGroup; + virDomainDelThrottleGroup; +} LIBVIRT_10.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index e812f5c3e9..5d5f286203 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -3618,6 +3618,50 @@ remoteDispatchDomainGetBlockIoTune(virNetServer *server G_GNUC_UNUSED, return rv; } + +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; + virTypedParameterPtr params = NULL; + int nparams = 0; + virConnectPtr conn = remoteGetHypervisorConn(client); + + if (!conn) + goto cleanup; + + if (!(dom = get_nonnull_domain(conn, args->dom))) + goto cleanup; + + if (virDomainGetThrottleGroup(dom, args->group ? *args->group : NULL, + ¶ms, &nparams, args->flags) < 0) + goto cleanup; + + /* 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; +} + + /*-------------------------------------------------------------*/ static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e76d9e9ba4..c757156e24 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2583,6 +2583,43 @@ static int remoteDomainGetBlockIoTune(virDomainPtr domain, return 0; } + +static int +remoteDomainGetThrottleGroup(virDomainPtr domain, + const char *group, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + remote_domain_get_throttle_group_args args = {0}; + g_auto(remote_domain_get_throttle_group_ret) ret = {0}; + struct private_data *priv = domain->conn->privateData; + VIR_LOCK_GUARD lock = remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, domain); + args.group = group ? (char **)&group : NULL; + args.flags = flags; + + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_THROTTLE_GROUP, + (xdrproc_t) xdr_remote_domain_get_throttle_group_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_get_throttle_group_ret, + (char *) &ret) == -1) { + return -1; + } + + if (virTypedParamsDeserialize((struct _virTypedParameterRemote *) ret.params.params_val, + ret.params.params_len, + REMOTE_DOMAIN_THROTTLE_GROUP_PARAMETERS_MAX, + params, + nparams) < 0) + return -1; + + return 0; + +} + + static int remoteDomainGetCPUStats(virDomainPtr domain, virTypedParameterPtr params, unsigned int nparams, @@ -7842,6 +7879,9 @@ static virHypervisorDriver hypervisor_driver = { .domainSetLaunchSecurityState = remoteDomainSetLaunchSecurityState, /* 8.0.0 */ .domainFDAssociate = remoteDomainFDAssociate, /* 9.0.0 */ .domainGraphicsReload = remoteDomainGraphicsReload, /* 10.2.0 */ + .domainSetThrottleGroup = remoteDomainSetThrottleGroup, /* 10.7.0 */ + .domainGetThrottleGroup = remoteDomainGetThrottleGroup, /* 10.7.0 */ + .domainDelThrottleGroup = remoteDomainDelThrottleGroup, /* 10.7.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 41c045ff78..02338b9f02 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,25 @@ 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 + */ + 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)

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> --- src/qemu/qemu_driver.c | 225 ++++++++++++++++++++++++++--------------- 1 file changed, 142 insertions(+), 83 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 09f7edda7d..ab2d40e85f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14990,35 +14990,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, @@ -15063,32 +15036,28 @@ 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; - - priv = vm->privateData; + return 0; +} - 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; + int ret = -1; #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; \ @@ -15128,10 +15097,10 @@ 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; @@ -15154,56 +15123,56 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, #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 +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) +{ + int ret = -1; #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"), \ @@ -15215,7 +15184,7 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, } \ goto endjob; \ } \ - 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); \ @@ -15233,6 +15202,96 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, #undef CHECK_MAX + ret = 0; + endjob: + return ret; +} + + +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)

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> --- src/qemu/qemu_driver.c | 388 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ab2d40e85f..7217e4d881 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20112,6 +20112,390 @@ qemuDomainGraphicsReload(virDomainPtr domain, return ret; } + +/** + * qemuDomainCheckThrottleGroupAllZero: + * @newiotune: Pointer to iotune, which contains detailed items + * + * Check if params within @newiotune contain all zero values, if yes, + * return failure since it's meaningless to set all zero values in @newiotune + * + * Returns -1 on failure, or 0 on success. + */ +static int +qemuDomainCheckThrottleGroupAllZero(virDomainBlockIoTuneInfo *newiotune) +{ + if (virDomainBlockIoTuneInfoHasAny(newiotune)) + return 0; + + VIR_FREE(newiotune->group_name); + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("creating a new group/updating existing with all parameters zero is not supported")); + return -1; +} + + +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; + + + 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; + /* this throttle group feature requires "QEMU_CAPS_OBJECT_JSON" + * when starting domain later, so check such flag here as well */ + 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 (qemuDomainCheckThrottleGroupAllZero(&info) < 0) + goto endjob; + + 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), + groupname, + &info); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto endjob; + virDomainThrottleGroupUpdate(def, &info); + } else { + /* prefix group name with "throttle-" in QOM */ + g_autofree char *prefixed_group_name = g_strdup_printf("throttle-%s", groupname); + + 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 (qemuDomainCheckThrottleGroupAllZero(&conf_info) < 0) + goto endjob; + + 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 +qemuDomainGetThrottleGroup(virDomainPtr dom, + const char *groupname, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virDomainObj *vm = NULL; + virDomainDef *def = NULL; + virDomainDef *persistentDef = NULL; + virDomainThrottleGroupDef groupDef = { 0 }; + virDomainThrottleGroupDef *reply = &groupDef; + int ret = -1; + int maxparams = 0; + int rc = 0; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainGetThrottleGroupEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (virDomainObjBeginJob(vm, VIR_JOB_QUERY) < 0) + goto cleanup; + + if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) + goto endjob; + + if (def) { + if (virDomainThrottleGroupByName(def, groupname) == NULL) { + virReportError(VIR_ERR_INVALID_ARG, + _("throttle group '%1$s' was not found in the domain config"), + groupname); + goto endjob; + } + qemuDomainObjEnterMonitor(vm); + rc = qemuMonitorGetThrottleGroup(qemuDomainGetMonitor(vm), groupname, reply); + qemuDomainObjExitMonitor(vm); + + if (rc < 0) + goto endjob; + } + + if (persistentDef) { + reply = virDomainThrottleGroupByName(persistentDef, groupname); + if (reply == NULL) { + virReportError(VIR_ERR_INVALID_ARG, + _("throttle group '%1$s' was not found in the domain config"), + groupname); + goto endjob; + } + reply->group_name = g_strdup(groupname); + } + + *nparams = 0; + +#define THROTTLE_GROUP_ASSIGN(name, var) \ + if (virTypedParamsAddULLong(params, \ + nparams, \ + &maxparams, \ + VIR_DOMAIN_BLOCK_IOTUNE_ ## name, \ + reply->var) < 0) \ + goto endjob; + + + if (virTypedParamsAddString(params, nparams, &maxparams, + VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME, + reply->group_name) < 0) + goto endjob; + + THROTTLE_GROUP_ASSIGN(TOTAL_BYTES_SEC, total_bytes_sec); + THROTTLE_GROUP_ASSIGN(READ_BYTES_SEC, read_bytes_sec); + THROTTLE_GROUP_ASSIGN(WRITE_BYTES_SEC, write_bytes_sec); + + THROTTLE_GROUP_ASSIGN(TOTAL_IOPS_SEC, total_iops_sec); + THROTTLE_GROUP_ASSIGN(READ_IOPS_SEC, read_iops_sec); + THROTTLE_GROUP_ASSIGN(WRITE_IOPS_SEC, write_iops_sec); + + THROTTLE_GROUP_ASSIGN(TOTAL_BYTES_SEC_MAX, total_bytes_sec_max); + THROTTLE_GROUP_ASSIGN(READ_BYTES_SEC_MAX, read_bytes_sec_max); + THROTTLE_GROUP_ASSIGN(WRITE_BYTES_SEC_MAX, write_bytes_sec_max); + + THROTTLE_GROUP_ASSIGN(TOTAL_IOPS_SEC_MAX, total_iops_sec_max); + THROTTLE_GROUP_ASSIGN(READ_IOPS_SEC_MAX, read_iops_sec_max); + THROTTLE_GROUP_ASSIGN(WRITE_IOPS_SEC_MAX, write_iops_sec_max); + + THROTTLE_GROUP_ASSIGN(SIZE_IOPS_SEC, size_iops_sec); + + THROTTLE_GROUP_ASSIGN(TOTAL_BYTES_SEC_MAX_LENGTH, total_bytes_sec_max_length); + THROTTLE_GROUP_ASSIGN(READ_BYTES_SEC_MAX_LENGTH, read_bytes_sec_max_length); + THROTTLE_GROUP_ASSIGN(WRITE_BYTES_SEC_MAX_LENGTH, write_bytes_sec_max_length); + + THROTTLE_GROUP_ASSIGN(TOTAL_IOPS_SEC_MAX_LENGTH, total_iops_sec_max_length); + THROTTLE_GROUP_ASSIGN(READ_IOPS_SEC_MAX_LENGTH, read_iops_sec_max_length); + THROTTLE_GROUP_ASSIGN(WRITE_IOPS_SEC_MAX_LENGTH, write_iops_sec_max_length); +#undef THROTTLE_GROUP_ASSIGN + + ret = 0; + + endjob: + virDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + 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) < 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, @@ -20362,6 +20746,10 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainSetLaunchSecurityState = qemuDomainSetLaunchSecurityState, /* 8.0.0 */ .domainFDAssociate = qemuDomainFDAssociate, /* 9.0.0 */ .domainGraphicsReload = qemuDomainGraphicsReload, /* 10.2.0 */ + .domainSetThrottleGroup = qemuDomainSetThrottleGroup, /* 10.7.0 */ + .domainGetThrottleGroup = qemuDomainGetThrottleGroup, /* 10.7.0 */ + .domainDelThrottleGroup = qemuDomainDelThrottleGroup, /* 10.7.0 */ + }; -- 2.39.5 (Apple Git-154)

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> --- 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 | 73 +++++++++++++++++++-- 5 files changed, 341 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 35dca8ee7b..fe5098d7b4 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 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 dcb9c4934e..a209452594 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -11004,6 +11004,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 { + /* get starting parentNodename, e.g. libvirt-1-format */ + 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 1fc4e2f33f..16bd9a22d0 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2235,6 +2235,34 @@ 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) { + /* first time to set nodename in filter */ + def->throttlefilters[i]->nodename = g_strdup(nodename); + } + } + + return 0; +} + + static int qemuDomainDiskPrivateParse(xmlXPathContextPtr ctxt, virDomainDiskDef *disk) @@ -2244,6 +2272,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 +2284,22 @@ 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++) { + virBufferEscapeString(buf, "<nodename type='throttle-filter' name='%s' ", disk->throttlefilters[i]->nodename); + virBufferEscapeString(buf, "group='%s'/>\n", disk->throttlefilters[i]->group_name); + } + } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</nodenames>\n"); } @@ -6293,7 +6332,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 +6346,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; @@ -9724,6 +9768,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)); + /* generate and set nodename into filter for later QEMU cmd preparation */ + qemuBlockThrottleFilterSetNodename(filter, g_strdup_printf("%s-filter", nodenameprefix)); +} + + int qemuDomainPrepareStorageSourceBlockdev(virDomainDiskDef *disk, virStorageSource *src, @@ -9747,6 +9807,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) @@ -9761,6 +9822,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> --- src/qemu/qemu_command.c | 99 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.c | 25 +++++++++++ 2 files changed, 124 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index a209452594..92363c64e9 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2022,6 +2022,99 @@ qemuBuildBlockStorageSourceAttachDataCommandline(virCommand *cmd, } +static 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; + + /* this throttle group feature requires "QEMU_CAPS_OBJECT_JSON" */ + 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, @@ -2079,6 +2172,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)) @@ -10440,6 +10536,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 3c18af6b0c..855ce9ca44 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,19 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, if (rc < 0) goto rollback; + /* Setup throttling filters + * add additional "blockdev-add"(throttle filter) between "blockdev-add" (qemuBlockStorageSourceChainAttach) and "device_add" (qemuDomainAttachExtensionDevice) + */ + if ((filterData = qemuBuildThrottleFiltersAttachPrepareBlockdev(disk))) { + if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0) + return -1; + /* QMP requests("blockdev-add" with "driver":"throttle") to QEMU */ + 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 +831,8 @@ qemuDomainAttachDiskGeneric(virDomainObj *vm, if (extensionDeviceAttached) ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &disk->info)); + qemuBlockThrottleFiltersDetach(priv->mon, filterData); + qemuBlockStorageSourceChainDetach(priv->mon, data); qemuDomainObjExitMonitor(vm); @@ -4653,6 +4669,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; @@ -4691,6 +4708,14 @@ qemuDomainRemoveDiskDevice(virQEMUDriver *driver, } } + qemuDomainObjEnterMonitor(vm); + /* QMP request("blockdev-del") to QEMU to delete throttle filter*/ + if ((filterData = qemuBuildThrottleFiltersDetachPrepareBlockdev(disk))) { + /* "qemuBlockThrottleFiltersDetach" is used in rollback within "qemuDomainAttachDiskGeneric" as well */ + qemuBlockThrottleFiltersDetach(priv->mon, filterData); + } + qemuDomainObjExitMonitor(vm); + qemuDomainObjEnterMonitor(vm); if (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> --- src/conf/domain_validate.c | 119 ++++++++++++++++++++++++++----------- src/qemu/qemu_driver.c | 6 ++ src/qemu/qemu_hotplug.c | 8 +++ 3 files changed, 99 insertions(+), 34 deletions(-) diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 1034bb57f5..03dfff93c0 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 @@ -969,6 +980,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; } @@ -1869,6 +1900,23 @@ 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]; + + /* Validate Throttle Group */ + if (virDomainDiskIoTuneValidate(*throttleGroup) < 0) + return -1; + } + + return 0; +} + + static int virDomainDefValidateInternal(const virDomainDef *def, virDomainXMLOption *xmlopt) @@ -1927,6 +1975,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 7217e4d881..78d1d83770 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14863,6 +14863,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 855ce9ca44..e341cac80c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -989,6 +989,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)

From: Chun Feng Wu <danielwuwy@163.com> Test domain xml and status xml * 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 * 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> --- .../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 + 9 files changed, 1133 insertions(+) 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 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"); 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..da757f91f9 --- /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,"iops-total":4000}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit1","limits":{"bps-read":5000,"bps-write":5000,"iops-total":5000}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit2","limits":{"bps-read":6000,"bps-write":6000,"iops-total":6000}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit12","limits":{"bps-read":7000,"bps-write":7000,"iops-total":7000}}' \ +-object '{"qom-type":"throttle-group","id":"throttle-limit3","limits":{"bps-read":8000,"bps-write":8000,"iops-total":8000}}' \ +-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 f3c8d0ae34..10b29c7d62 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -2350,6 +2350,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)

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> --- src/test/test_driver.c | 452 ++++++++++++++++++++++++++++++----------- 1 file changed, 330 insertions(+), 122 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index f1cefb5c50..e9635c7c13 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,190 @@ 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 +testDomainGetThrottleGroup(virDomainPtr dom, + const char *groupname, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virDomainObj *vm = NULL; + virDomainDef *def = NULL; + virDomainThrottleGroupDef groupDef = {0}; + virDomainThrottleGroupDef *reply = &groupDef; + int ret = -1; + int maxparams = 0; + + 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; + } + reply->group_name = g_strdup(groupname); + +#define TEST_THROTTLE_GROUP_ASSIGN(name, var) \ + if (virTypedParamsAddULLong(params, \ + nparams, \ + &maxparams, \ + VIR_DOMAIN_BLOCK_IOTUNE_ ## name, \ + reply->var) < 0) \ + goto cleanup; + + if (virTypedParamsAddString(params, nparams, &maxparams, + "VIR_DOMAIN_BLOCK_IOTUNE_GROUP_NAME", + reply->group_name) < 0) + goto cleanup; + + TEST_THROTTLE_GROUP_ASSIGN(TOTAL_BYTES_SEC, total_bytes_sec); + TEST_THROTTLE_GROUP_ASSIGN(READ_BYTES_SEC, read_bytes_sec); + TEST_THROTTLE_GROUP_ASSIGN(WRITE_BYTES_SEC, write_bytes_sec); + TEST_THROTTLE_GROUP_ASSIGN(TOTAL_IOPS_SEC, total_iops_sec); + TEST_THROTTLE_GROUP_ASSIGN(READ_IOPS_SEC, read_iops_sec); + TEST_THROTTLE_GROUP_ASSIGN(WRITE_IOPS_SEC, write_iops_sec); + + TEST_THROTTLE_GROUP_ASSIGN(TOTAL_BYTES_SEC_MAX, total_bytes_sec_max); + TEST_THROTTLE_GROUP_ASSIGN(READ_BYTES_SEC_MAX, read_bytes_sec_max); + TEST_THROTTLE_GROUP_ASSIGN(WRITE_BYTES_SEC_MAX, write_bytes_sec_max); + + TEST_THROTTLE_GROUP_ASSIGN(TOTAL_IOPS_SEC_MAX, total_iops_sec_max); + TEST_THROTTLE_GROUP_ASSIGN(READ_IOPS_SEC_MAX, read_iops_sec_max); + TEST_THROTTLE_GROUP_ASSIGN(WRITE_IOPS_SEC_MAX, write_iops_sec_max); + + TEST_THROTTLE_GROUP_ASSIGN(SIZE_IOPS_SEC, size_iops_sec); + + TEST_THROTTLE_GROUP_ASSIGN(TOTAL_BYTES_SEC_MAX_LENGTH, total_bytes_sec_max_length); + TEST_THROTTLE_GROUP_ASSIGN(READ_BYTES_SEC_MAX_LENGTH, read_bytes_sec_max_length); + TEST_THROTTLE_GROUP_ASSIGN(WRITE_BYTES_SEC_MAX_LENGTH, write_bytes_sec_max_length); + + TEST_THROTTLE_GROUP_ASSIGN(TOTAL_IOPS_SEC_MAX_LENGTH, total_iops_sec_max_length); + TEST_THROTTLE_GROUP_ASSIGN(READ_IOPS_SEC_MAX_LENGTH, read_iops_sec_max_length); + TEST_THROTTLE_GROUP_ASSIGN(WRITE_IOPS_SEC_MAX_LENGTH, write_iops_sec_max_length); +#undef TEST_THROTTLE_GROUP_ASSIGN + + ret = 0; + + cleanup: + if (reply != NULL && reply->group_name != NULL) { + g_free(reply->group_name); + } + virDomainObjEndAPI(&vm); + 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 @@ -11210,6 +11415,9 @@ static virHypervisorDriver testHypervisorDriver = { .domainGetInterfaceParameters = testDomainGetInterfaceParameters, /* 5.6.0 */ .domainSetBlockIoTune = testDomainSetBlockIoTune, /* 5.7.0 */ .domainGetBlockIoTune = testDomainGetBlockIoTune, /* 5.7.0 */ + .domainSetThrottleGroup = testDomainSetThrottleGroup, /* 10.7.0 */ + .domainGetThrottleGroup = testDomainGetThrottleGroup, /* 10.7.0 */ + .domainDelThrottleGroup = testDomainDelThrottleGroup, /* 10.7.0 */ .domainSetBlkioParameters = testDomainSetBlkioParameters, /* 7.7.0 */ .domainGetBlkioParameters = testDomainGetBlkioParameters, /* 7.7.0 */ .connectListDefinedDomains = testConnectListDefinedDomains, /* 0.1.11 */ -- 2.39.5 (Apple Git-154)

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 e4923284af..6eea5f176b 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> --- docs/manpages/virsh.rst | 132 +++++++++++++ tools/virsh-completer-domain.c | 55 ++++++ tools/virsh-completer-domain.h | 11 ++ tools/virsh-domain.c | 349 ++++++++++++++++++++++++++++++++- 4 files changed, 546 insertions(+), 1 deletion(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 2e525d3fac..c1ed028844 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1122,6 +1122,138 @@ 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. +*domain* (see also ``domblklist`` for listing these names). + +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* flags may be +given, 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 such as via the +``attach-disk`` command, 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. + + +domthrottlegrouplist +-------------------- + +**Syntax:** + +:: + + domthrottlegrouplist domain [--inactive] + +Print a table showing names of all throttle groups +associated with *domain*. If *--inactive* is specified, query the +block devices 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 6eea5f176b..b7004979c6 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,329 @@ 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} +}; + + +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; + + 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 (virDomainGetThrottleGroup(dom, group_name, ¶ms, &nparams, flags) != 0) { + vshError(ctl, "%s", + _("Unable to get throttle group parameters")); + goto cleanup; + } + + 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; +} + + /* * "blkiotune" command */ @@ -13408,6 +13731,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)

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> --- 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 c1ed028844..bb0dda6cc6 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -5007,7 +5007,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* @@ -5047,6 +5047,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 b7004979c6..7f0cf81501 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_autofree char **throttle_groups = NULL; int ret; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; const char *stype = NULL; @@ -622,6 +629,7 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) g_auto(virBuffer) sourceAttrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) sourceChildBuf = VIR_BUFFER_INIT_CHILD(&diskChildBuf); g_auto(virBuffer) hostAttrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) throttleChildBuf = VIR_BUFFER_INITIALIZER; g_autofree char *xml = NULL; struct stat st; bool current = vshCommandOptBool(cmd, "current"); @@ -665,9 +673,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 +727,16 @@ cmdAttachDisk(vshControl *ctl, const vshCmd *cmd) virXMLFormatElement(&diskChildBuf, "driver", &driverAttrBuf, NULL); + if (throttle_groups) { + 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)

* updated latest 11.0.0, qemu gitlab url, documents and change requests signed-off-by: Harikumar R <harirajkumar230@gmail.com> --- docs/formatdomain.rst | 4 ++-- src/conf/domain_conf.c | 31 +++++++++++++++++++------------ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 84aa5d85ac..276fa218c0 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -1967,7 +1967,7 @@ advertisements to the guest OS. (NB: Only qemu driver support) Disk Throttle Group Management ------------------------------ -:since:`Since 10.7.0` it is possible to create multiple named throttle groups +:since:`Since 11.0.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. @@ -3285,7 +3285,7 @@ paravirtualized driver is specified via the ``disk`` element. "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 10.7.0` + per-device throttle chain :since:`Since 11.0.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 diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a4de5c01c3..01b6d4fc9f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7859,7 +7859,8 @@ 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) { \ @@ -7952,8 +7953,10 @@ virDomainDefThrottleGroupsParse(virDomainDef *def, if ((n = virXPathNodeSet("./throttlegroups/throttlegroup", ctxt, &nodes)) < 0) return -1; - if (n) - def->throttlegroups = g_new0(virDomainThrottleGroupDef *, n); + if (n == 0) + return 0; + + def->throttlegroups = g_new0(virDomainThrottleGroupDef *, n); for (i = 0; i < n; i++) { g_autoptr(virDomainThrottleGroupDef) group = NULL; @@ -22648,7 +22651,9 @@ virDomainIOThreadIDDel(virDomainDef *def, * @src: throttle group to be copiped from * @dst: throttle group to be copiped to * - * copy throttle group content from @src to @dst + * 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, @@ -22684,9 +22689,8 @@ virDomainThrottleGroupAdd(virDomainDef *def, * @def: domain definition * @info: throttle group definition within domain * - * search existing throttle group within domain definition - * by group_name and then overwrite it with @info, - * it's to update existing throttle group + * 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, @@ -22713,9 +22717,7 @@ virDomainThrottleGroupUpdate(virDomainDef *def, * @def: domain definition * @name: throttle group name * - * search existing throttle group within domain definition - * by group_name and then delete it with @name, - * it's to delete existing throttle group + * Delete throttle group @name in @def */ void virDomainThrottleGroupDel(virDomainDef *def, @@ -27962,7 +27964,10 @@ 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", \ @@ -27990,6 +27995,8 @@ virDomainThrottleGroupFormat(virBuffer *buf, 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); @@ -28010,7 +28017,7 @@ virDomainDefThrottleGroupsFormat(virBuffer *buf, const virDomainDef *def) { g_auto(virBuffer) childrenBuf = VIR_BUFFER_INIT_CHILD(buf); - ssize_t n; + size_t n; for (n = 0; n < def->nthrottlegroups; n++) { virDomainThrottleGroupFormat(&childrenBuf, def->throttlegroups[n]); -- 2.39.5 (Apple Git-154)
participants (1)
-
Harikumar R