From 223438a245f34ca12f8159fa0b68132cb9c80f0f Mon Sep 17 00:00:00 2001 From: John Ferlan Date: Sun, 18 Sep 2016 12:02:50 -0400 Subject: [PATCH] qemu: Add length for bps/iops throttling parameters to driver Add support for a duration/length for the bps/iops and friends. Modify the API in order to add the "blkdeviotune." specific definitions for the iotune throttling duration/length options total_bytes_sec_max_length write_bytes_sec_max_length read_bytes_sec_max_length total_iops_sec_max_length write_iops_sec_max_length read_iops_sec_max_length --- include/libvirt/libvirt-domain.h | 54 +++++++++++++++++ src/conf/domain_conf.h | 6 ++ src/qemu/qemu_driver.c | 100 +++++++++++++++++++++++++++++-- src/qemu/qemu_monitor.c | 7 ++- src/qemu/qemu_monitor.h | 3 +- src/qemu/qemu_monitor_json.c | 25 +++++++- src/qemu/qemu_monitor_json.h | 3 +- tests/qemumonitorjsontest.c | 17 +++++- 8 files changed, 202 insertions(+), 13 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 9f6d292a65..5f50660748 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3846,6 +3846,60 @@ typedef void (*virConnectDomainEventJobCompletedCallback)(virConnectPtr conn, */ # define VIR_DOMAIN_TUNABLE_BLKDEV_SIZE_IOPS_SEC "blkdeviotune.size_iops_sec" +/** + * VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC_MAX_LENGTH: + * + * Macro represents the length in seconds allowed for a burst period + * for the blkdeviotune.total_bytes_sec_max, + * as VIR_TYPED_PARAM_ULLONG. + */ +# define VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_BYTES_SEC_MAX_LENGTH "blkdeviotune.total_bytes_sec_max_length" + +/** + * VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC_MAX_LENGTH: + * + * Macro represents the length in seconds allowed for a burst period + * for the blkdeviotune.read_bytes_sec_max + * as VIR_TYPED_PARAM_ULLONG. + */ +# define VIR_DOMAIN_TUNABLE_BLKDEV_READ_BYTES_SEC_MAX_LENGTH "blkdeviotune.read_bytes_sec_max_length" + +/** + * VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC_MAX_LENGTH: + * + * Macro represents the length in seconds allowed for a burst period + * for the blkdeviotune.write_bytes_sec_max + * as VIR_TYPED_PARAM_ULLONG. + */ +# define VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_BYTES_SEC_MAX_LENGTH "blkdeviotune.write_bytes_sec_max_length" + +/** + * VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC_MAX_LENGTH: + * + * Macro represents the length in seconds allowed for a burst period + * for the blkdeviotune.total_iops_sec_max + * as VIR_TYPED_PARAM_ULLONG. + */ +# define VIR_DOMAIN_TUNABLE_BLKDEV_TOTAL_IOPS_SEC_MAX_LENGTH "blkdeviotune.total_iops_sec_max_length" + +/** + * VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC_MAX_LENGTH: + * + * Macro represents the length in seconds allowed for a burst period + * for the blkdeviotune.read_iops_sec_max + * as VIR_TYPED_PARAM_ULLONG. + */ +# define VIR_DOMAIN_TUNABLE_BLKDEV_READ_IOPS_SEC_MAX_LENGTH "blkdeviotune.read_iops_sec_max_length" + +/** + * VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC_MAX_LENGTH: + * + * Macro represents the length in seconds allowed for a burst period + * for the blkdeviotune.write_iops_sec_max + * as VIR_TYPED_PARAM_ULLONG. + */ +# define VIR_DOMAIN_TUNABLE_BLKDEV_WRITE_IOPS_SEC_MAX_LENGTH "blkdeviotune.write_iops_sec_max_length" + /** * virConnectDomainEventTunableCallback: * @conn: connection object diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index dff28c08b3..24aa79cdc9 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -548,6 +548,12 @@ struct _virDomainBlockIoTuneInfo { unsigned long long read_iops_sec_max; unsigned long long write_iops_sec_max; unsigned long long size_iops_sec; + unsigned long long total_bytes_sec_max_length; + unsigned long long read_bytes_sec_max_length; + unsigned long long write_bytes_sec_max_length; + unsigned long long total_iops_sec_max_length; + unsigned long long read_iops_sec_max_length; + unsigned long long write_iops_sec_max_length; }; typedef virDomainBlockIoTuneInfo *virDomainBlockIoTuneInfoPtr; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 38dff42380..bacabd2d34 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -114,6 +114,7 @@ VIR_LOG_INIT("qemu.qemu_driver"); #define QEMU_NB_BLOCK_IO_TUNE_PARAM 6 #define QEMU_NB_BLOCK_IO_TUNE_PARAM_MAX 13 +#define QEMU_NB_BLOCK_IO_TUNE_PARAM_MAX_LENGTH 19 #define QEMU_NB_NUMA_PARAM 2 @@ -17302,7 +17303,9 @@ qemuDomainSetBlockIoTuneDefaults(virDomainBlockIoTuneInfoPtr newinfo, bool set_iops, bool set_bytes_max, bool set_iops_max, - bool set_size_iops) + bool set_size_iops, + bool set_bytes_max_length, + bool set_iops_max_length) { #define SET_IOTUNE_DEFAULTS(BOOL, FIELD) \ if (!BOOL) { \ @@ -17319,6 +17322,36 @@ qemuDomainSetBlockIoTuneDefaults(virDomainBlockIoTuneInfoPtr newinfo, if (!set_size_iops) newinfo->size_iops_sec = oldinfo->size_iops_sec; + + /* The length field is handled a bit differently. If not defined/set, + * QEMU will default these to 0 or 1 depending on whether something in + * the same family is set or not. + * + * Similar to other values, if nothing in the family is defined/set, + * then take whatever is in the oldinfo. + * + * To clear an existing limit, a 0 is provided; however, passing that + * 0 onto QEMU if there's a family value defined/set (or defaulted) + * will cause an error. So, to mimic that, if our oldinfo was set and + * our newinfo is clearing, then set max_length based on whether we + * have a value in the family set/defined. */ +#define SET_MAX_LENGTH(BOOL, FIELD) \ + if (!BOOL) \ + newinfo->FIELD##_max_length = oldinfo->FIELD##_max_length; \ + else if (BOOL && oldinfo->FIELD##_max_length && \ + !newinfo->FIELD##_max_length) \ + newinfo->FIELD##_max_length = (newinfo->FIELD || \ + newinfo->FIELD##_max) ? 1 : 0; + + SET_MAX_LENGTH(set_bytes_max_length, total_bytes_sec); + SET_MAX_LENGTH(set_bytes_max_length, read_bytes_sec); + SET_MAX_LENGTH(set_bytes_max_length, write_bytes_sec); + SET_MAX_LENGTH(set_iops_max_length, total_iops_sec); + SET_MAX_LENGTH(set_iops_max_length, read_iops_sec); + SET_MAX_LENGTH(set_iops_max_length, write_iops_sec); + +#undef SET_MAX_LENGTH + } @@ -17345,7 +17378,10 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, bool set_bytes_max = false; bool set_iops_max = false; bool set_size_iops = false; + bool set_bytes_max_length = false; + bool set_iops_max_length = false; bool supportMaxOptions = true; + bool supportMaxLengthOptions = true; virQEMUDriverConfigPtr cfg = NULL; virObjectEventPtr event = NULL; virTypedParameterPtr eventParams = NULL; @@ -17381,6 +17417,18 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, VIR_TYPED_PARAM_ULLONG, VIR_DOMAIN_BLOCK_IOTUNE_SIZE_IOPS_SEC, VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC_MAX_LENGTH, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC_MAX_LENGTH, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC_MAX_LENGTH, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC_MAX_LENGTH, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC_MAX_LENGTH, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC_MAX_LENGTH, + VIR_TYPED_PARAM_ULLONG, NULL) < 0) return -1; @@ -17448,6 +17496,19 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, SET_IOTUNE_FIELD(write_iops_sec_max, set_iops_max, WRITE_IOPS_SEC_MAX); SET_IOTUNE_FIELD(size_iops_sec, set_size_iops, SIZE_IOPS_SEC); + + SET_IOTUNE_FIELD(total_bytes_sec_max_length, set_bytes_max_length, + TOTAL_BYTES_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(read_bytes_sec_max_length, set_bytes_max_length, + READ_BYTES_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(write_bytes_sec_max_length, set_bytes_max_length, + WRITE_BYTES_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(total_iops_sec_max_length, set_iops_max_length, + TOTAL_IOPS_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(read_iops_sec_max_length, set_iops_max_length, + READ_IOPS_SEC_MAX_LENGTH); + SET_IOTUNE_FIELD(write_iops_sec_max_length, set_iops_max_length, + WRITE_IOPS_SEC_MAX_LENGTH); } #undef SET_IOTUNE_FIELD @@ -17487,6 +17548,9 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, if (def) { supportMaxOptions = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE_IOTUNE_MAX); + supportMaxLengthOptions = + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE_IOTUNE_MAX_LENGTH); + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE_IOTUNE)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("block I/O throttling not supported with this " @@ -17502,6 +17566,14 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, goto endjob; } + if (!supportMaxLengthOptions && + (set_iops_max_length || set_bytes_max_length)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("a block I/O throttling length parameter is not " + "supported with this QEMU binary")); + goto endjob; + } + if (!(disk = qemuDomainDiskByName(def, path))) goto endjob; @@ -17510,7 +17582,9 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, qemuDomainSetBlockIoTuneDefaults(&info, &disk->blkdeviotune, set_bytes, set_iops, set_bytes_max, - set_iops_max, set_size_iops); + set_iops_max, set_size_iops, + set_bytes_max_length, + set_iops_max_length); #define CHECK_MAX(val) \ do { \ @@ -17532,9 +17606,13 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, #undef CHECK_MAX + /* NB: Let's let QEMU decide how to handle issues with _length + * via the JSON error code from the block_set_io_throttle call */ + qemuDomainObjEnterMonitor(driver, vm); ret = qemuMonitorSetBlockIoThrottle(priv->mon, device, - &info, supportMaxOptions); + &info, supportMaxOptions, + supportMaxLengthOptions); if (qemuDomainObjExitMonitor(driver, vm) < 0) ret = -1; if (ret < 0) @@ -17561,7 +17639,9 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, } qemuDomainSetBlockIoTuneDefaults(&info, &conf_disk->blkdeviotune, set_bytes, set_iops, set_bytes_max, - set_iops_max, set_size_iops); + set_iops_max, set_size_iops, + set_bytes_max_length, + set_iops_max_length); conf_disk->blkdeviotune = info; ret = virDomainSaveConfig(cfg->configDir, driver->caps, persistentDef); if (ret < 0) @@ -17597,7 +17677,7 @@ qemuDomainGetBlockIoTune(virDomainPtr dom, virDomainBlockIoTuneInfo reply; char *device = NULL; int ret = -1; - int maxparams = QEMU_NB_BLOCK_IO_TUNE_PARAM_MAX; + int maxparams = QEMU_NB_BLOCK_IO_TUNE_PARAM_MAX_LENGTH; virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG | @@ -17633,6 +17713,9 @@ qemuDomainGetBlockIoTune(virDomainPtr dom, if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE_IOTUNE_MAX)) maxparams = QEMU_NB_BLOCK_IO_TUNE_PARAM; + else if (!virQEMUCapsGet(priv->qemuCaps, + QEMU_CAPS_DRIVE_IOTUNE_MAX_LENGTH)) + maxparams = QEMU_NB_BLOCK_IO_TUNE_PARAM_MAX; } if (*nparams == 0) { @@ -17696,6 +17779,13 @@ qemuDomainGetBlockIoTune(virDomainPtr dom, BLOCK_IOTUNE_ASSIGN(SIZE_IOPS_SEC, size_iops_sec); + BLOCK_IOTUNE_ASSIGN(TOTAL_BYTES_SEC_MAX_LENGTH, total_bytes_sec_max_length); + BLOCK_IOTUNE_ASSIGN(READ_BYTES_SEC_MAX_LENGTH, read_bytes_sec_max_length); + BLOCK_IOTUNE_ASSIGN(WRITE_BYTES_SEC_MAX_LENGTH, write_bytes_sec_max_length); + + BLOCK_IOTUNE_ASSIGN(TOTAL_IOPS_SEC_MAX_LENGTH, total_iops_sec_max_length); + BLOCK_IOTUNE_ASSIGN(READ_IOPS_SEC_MAX_LENGTH, read_iops_sec_max_length); + BLOCK_IOTUNE_ASSIGN(WRITE_IOPS_SEC_MAX_LENGTH, write_iops_sec_max_length); #undef BLOCK_IOTUNE_ASSIGN ret = 0; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index d9f66a2bfc..a5e14b2f13 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3427,14 +3427,17 @@ int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, const char *device, virDomainBlockIoTuneInfoPtr info, - bool supportMaxOptions) + bool supportMaxOptions, + bool supportMaxLengthOptions) { VIR_DEBUG("device=%p, info=%p", device, info); QEMU_CHECK_MONITOR(mon); if (mon->json) - return qemuMonitorJSONSetBlockIoThrottle(mon, device, info, supportMaxOptions); + return qemuMonitorJSONSetBlockIoThrottle(mon, device, info, + supportMaxOptions, + supportMaxLengthOptions); else return qemuMonitorTextSetBlockIoThrottle(mon, device, info); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index cb27412b6d..c3133c472b 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -876,7 +876,8 @@ int qemuMonitorOpenGraphics(qemuMonitorPtr mon, int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, const char *device, virDomainBlockIoTuneInfoPtr info, - bool supportMaxOptions); + bool supportMaxOptions, + bool supportMaxLengthOptions); int qemuMonitorGetBlockIoThrottle(qemuMonitorPtr mon, const char *device, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 6802643bfd..6c13832701 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4563,6 +4563,12 @@ qemuMonitorJSONBlockIoThrottleInfo(virJSONValuePtr result, GET_THROTTLE_STATS_OPTIONAL("iops_rd_max", read_iops_sec_max); GET_THROTTLE_STATS_OPTIONAL("iops_wr_max", write_iops_sec_max); GET_THROTTLE_STATS_OPTIONAL("iops_size", size_iops_sec); + GET_THROTTLE_STATS_OPTIONAL("bps_max_length", total_bytes_sec_max_length); + GET_THROTTLE_STATS_OPTIONAL("bps_rd_max_length", read_bytes_sec_max_length); + GET_THROTTLE_STATS_OPTIONAL("bps_wr_max_length", write_bytes_sec_max_length); + GET_THROTTLE_STATS_OPTIONAL("iops_max_length", total_iops_sec_max_length); + GET_THROTTLE_STATS_OPTIONAL("iops_rd_max_length", read_iops_sec_max_length); + GET_THROTTLE_STATS_OPTIONAL("iops_wr_max_length", write_iops_sec_max_length); break; } @@ -4584,7 +4590,8 @@ qemuMonitorJSONBlockIoThrottleInfo(virJSONValuePtr result, int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, const char *device, virDomainBlockIoTuneInfoPtr info, - bool supportMaxOptions) + bool supportMaxOptions, + bool supportMaxLengthOptions) { int ret = -1; virJSONValuePtr cmd = NULL; @@ -4593,7 +4600,8 @@ int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, /* The qemu capability check has already been made in * qemuDomainSetBlockIoTune. NB, once a NULL is found in * the sequence, qemuMonitorJSONMakeCommand will stop. So - * let's make use of that when !supportMaxOptions */ + * let's make use of that when !supportMaxOptions and + * similarly when !supportMaxLengthOptions */ cmd = qemuMonitorJSONMakeCommand("block_set_io_throttle", "s:device", device, "U:bps", info->total_bytes_sec, @@ -4610,6 +4618,19 @@ int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, "U:iops_rd_max", info->read_iops_sec_max, "U:iops_wr_max", info->write_iops_sec_max, "U:iops_size", info->size_iops_sec, + !supportMaxLengthOptions ? NULL : + "P:bps_max_length", + info->total_bytes_sec_max_length, + "P:bps_rd_max_length", + info->read_bytes_sec_max_length, + "P:bps_wr_max_length", + info->write_bytes_sec_max_length, + "P:iops_max_length", + info->total_iops_sec_max_length, + "P:iops_rd_max_length", + info->read_iops_sec_max_length, + "P:iops_wr_max_length", + info->write_iops_sec_max_length, NULL); if (!cmd) return -1; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 6a5eb3b8b5..77b2e02b35 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -327,7 +327,8 @@ int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, const char *device, virDomainBlockIoTuneInfoPtr info, - bool supportMaxOptions); + bool supportMaxOptions, + bool supportMaxLengthOptions); int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon, const char *device, diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 5e72a0f6be..d8fd9588a0 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -67,6 +67,12 @@ const char *queryBlockReply = " \"iops_rd_max\": 11," " \"iops_wr_max\": 12," " \"iops_size\": 13," +" \"bps_max_length\": 14," +" \"bps_rd_max_length\": 15," +" \"bps_wr_max_length\": 16," +" \"iops_max_length\": 17," +" \"iops_rd_max_length\": 18," +" \"iops_wr_max_length\": 19," " \"file\": \"/home/zippy/work/tmp/gentoo.qcow2\"," " \"encryption_key_missing\": false" " }," @@ -1996,7 +2002,7 @@ testQemuMonitorJSONqemuMonitorJSONSetBlockIoThrottle(const void *data) if (!test) return -1; - expectedInfo = (virDomainBlockIoTuneInfo) {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + expectedInfo = (virDomainBlockIoTuneInfo) {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; if (qemuMonitorTestAddItem(test, "query-block", queryBlockReply) < 0 || qemuMonitorTestAddItemParams(test, "block_set_io_throttle", @@ -2008,6 +2014,12 @@ testQemuMonitorJSONqemuMonitorJSONSetBlockIoThrottle(const void *data) "bps_wr_max", "9", "iops_max", "10", "iops_rd_max", "11", "iops_wr_max", "12", "iops_size", "13", + "bps_max_length", "14", + "bps_rd_max_length", "15", + "bps_wr_max_length", "16", + "iops_max_length", "17", + "iops_rd_max_length", "18", + "iops_wr_max_length", "19", NULL, NULL) < 0) goto cleanup; @@ -2022,7 +2034,8 @@ testQemuMonitorJSONqemuMonitorJSONSetBlockIoThrottle(const void *data) } if (qemuMonitorJSONSetBlockIoThrottle(qemuMonitorTestGetMonitor(test), - "drive-virtio-disk1", &info, true) < 0) + "drive-virtio-disk1", &info, true, + true) < 0) goto cleanup; ret = 0;