From: Lei Li <lilei@xxxxxxxxxxxxxxxxxx> Implement the block I/O throttle setting and getting support to qemu driver. Signed-off-by: Lei Li <lilei@xxxxxxxxxxxxxxxxxx> Signed-off-by: Zhi Yong Wu <wuzhy@xxxxxxxxxxxxxxxxxx> Signed-off-by: Eric Blake <eblake@xxxxxxxxxx> --- src/qemu/qemu_command.c | 31 ++++ src/qemu/qemu_driver.c | 340 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 33 ++++ src/qemu/qemu_monitor.h | 8 + src/qemu/qemu_monitor_json.c | 176 ++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 + src/qemu/qemu_monitor_text.c | 151 +++++++++++++++++++- src/qemu/qemu_monitor_text.h | 8 + 8 files changed, 754 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 85b34bb..905afa6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1866,6 +1866,37 @@ qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED, } } + /*block I/O throttling*/ + if (disk->blkdeviotune.total_bytes_sec) { + virBufferAsprintf(&opt, ",bps=%llu", + disk->blkdeviotune.total_bytes_sec); + } + + if (disk->blkdeviotune.read_bytes_sec) { + virBufferAsprintf(&opt, ",bps_rd=%llu", + disk->blkdeviotune.read_bytes_sec); + } + + if (disk->blkdeviotune.write_bytes_sec) { + virBufferAsprintf(&opt, ",bps_wr=%llu", + disk->blkdeviotune.write_bytes_sec); + } + + if (disk->blkdeviotune.total_iops_sec) { + virBufferAsprintf(&opt, ",iops=%llu", + disk->blkdeviotune.total_iops_sec); + } + + if (disk->blkdeviotune.read_iops_sec) { + virBufferAsprintf(&opt, ",iops_rd=%llu", + disk->blkdeviotune.read_iops_sec); + } + + if (disk->blkdeviotune.write_iops_sec) { + virBufferAsprintf(&opt, ",iops_wr=%llu", + disk->blkdeviotune.write_iops_sec); + } + if (virBufferError(&opt)) { virReportOOMError(); goto error; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 94fbe94..0308a41 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -93,6 +93,8 @@ #define QEMU_NB_MEM_PARAM 3 +#define QEMU_NB_BLOCK_IO_TUNE_PARAM 6 + #if HAVE_LINUX_KVM_H # include <linux/kvm.h> #endif @@ -10763,6 +10765,342 @@ cleanup: return ret; } +static int +qemuDomainSetBlockIoTune(virDomainPtr dom, + const char *disk, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + qemuDomainObjPrivatePtr priv; + virDomainDefPtr persistentDef = NULL; + virDomainBlockIoTuneInfo info; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *device = NULL; + int ret = -1; + int i; + bool isActive; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + memset(&info, 0, sizeof(info)); + + qemuDriverLock(driver); + virUUIDFormat(dom->uuid, uuidstr); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + device = qemuDiskPathToAlias(vm, disk); + if (!device) { + goto cleanup; + } + + if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + isActive = virDomainObjIsActive(vm); + + if (flags == VIR_DOMAIN_AFFECT_CURRENT) { + if (isActive) + flags = VIR_DOMAIN_AFFECT_LIVE; + else + flags = VIR_DOMAIN_AFFECT_CONFIG; + } + + if (!isActive && (flags & VIR_DOMAIN_AFFECT_LIVE)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is not running")); + goto endjob; + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + if (!vm->persistent) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot change persistent config of a transient domain")); + goto endjob; + } + if (!(persistentDef = virDomainObjGetPersistentDef(driver->caps, vm))) + goto endjob; + } + + for (i = 0; i < nparams; i++) { + virTypedParameterPtr param = ¶ms[i]; + + if (param->type != VIR_TYPED_PARAM_ULLONG) { + qemuReportError(VIR_ERR_INVALID_ARG, + _("expected unsigned long long for parameter %s"), + param->field); + goto endjob; + } + + if (STREQ(param->field, VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC)) { + info.total_bytes_sec = param->value.ul; + } else if (STREQ(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC)) { + info.read_bytes_sec = param->value.ul; + } else if (STREQ(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC)) { + info.write_bytes_sec = param->value.ul; + } else if (STREQ(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC)) { + info.total_iops_sec = param->value.ul; + } else if (STREQ(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC)) { + info.read_iops_sec = param->value.ul; + } else if (STREQ(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC)) { + info.write_iops_sec = param->value.ul; + } else { + qemuReportError(VIR_ERR_INVALID_ARG, + _("Unrecognized parameter %s"), + param->field); + goto endjob; + } + } + + if ((info.total_bytes_sec && info.read_bytes_sec) || + (info.total_bytes_sec && info.write_bytes_sec)) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("total and read/write of bytes_sec cannot be set at the same time")); + goto endjob; + } + + if ((info.total_iops_sec && info.read_iops_sec) || + (info.total_iops_sec && info.write_iops_sec)) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("total and read/write of iops_sec cannot be set at the same time")); + goto endjob; + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + sa_assert(persistentDef); + int idx = virDomainDiskIndexByName(vm->def, disk, true); + if (i < 0) + goto endjob; + persistentDef->disks[idx]->blkdeviotune = info; + } + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSetBlockIoThrottle(priv->mon, device, &info); + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + ret = virDomainSaveConfig(driver->configDir, persistentDef); + if (ret < 0) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Write to config file failed")); + goto endjob; + } + } + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + +cleanup: + VIR_FREE(device); + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + +static int +qemuDomainGetBlockIoTune(virDomainPtr dom, + const char *disk, + virTypedParameterPtr params, + int *nparams, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + qemuDomainObjPrivatePtr priv; + virDomainDefPtr persistentDef = NULL; + virDomainBlockIoTuneInfo reply; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *device = NULL; + int ret = -1; + int i; + bool isActive; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG | + VIR_TYPED_PARAM_STRING_OKAY, -1); + + /* We don't return strings, and thus trivially support this flag. */ + flags &= ~VIR_TYPED_PARAM_STRING_OKAY; + + qemuDriverLock(driver); + virUUIDFormat(dom->uuid, uuidstr); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if ((*nparams) == 0) { + /* Current number of parameters supported by QEMU Block I/O Throttling */ + *nparams = QEMU_NB_BLOCK_IO_TUNE_PARAM; + ret = 0; + goto cleanup; + } + + device = qemuDiskPathToAlias(vm, disk); + + if (!device) { + goto cleanup; + } + + if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + isActive = virDomainObjIsActive(vm); + + if (flags == VIR_DOMAIN_AFFECT_CURRENT) { + if (isActive) + flags = VIR_DOMAIN_AFFECT_LIVE; + else + flags = VIR_DOMAIN_AFFECT_CONFIG; + } + + if ((flags & VIR_DOMAIN_AFFECT_LIVE) && (flags & VIR_DOMAIN_AFFECT_CONFIG)) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("Cannot query with --live and --config together")); + goto endjob; + } + + if (!isActive && (flags & VIR_DOMAIN_AFFECT_LIVE)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is not running")); + goto endjob; + } + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorGetBlockIoThrottle(priv->mon, device, &reply); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (ret < 0) + goto endjob; + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + if (!vm->persistent) { + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is transient")); + goto endjob; + } + if (!(persistentDef = virDomainObjGetPersistentDef(driver->caps, vm))) + goto endjob; + + int idx = virDomainDiskIndexByName(vm->def, disk, true); + if (idx < 0) + goto endjob; + reply = persistentDef->disks[idx]->blkdeviotune; + } + + for (i = 0; i < QEMU_NB_BLOCK_IO_TUNE_PARAM && i < *nparams; i++) { + virTypedParameterPtr param = ¶ms[i]; + param->value.ul = 0; + param->type = VIR_TYPED_PARAM_ULLONG; + + switch(i) { + case 0: + if (virStrcpyStatic(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_BYTES_SEC); + goto endjob; + } + param->value.ul = reply.total_bytes_sec; + break; + + case 1: + if (virStrcpyStatic(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BLOCK_IOTUNE_READ_BYTES_SEC); + goto endjob; + } + param->value.ul = reply.read_bytes_sec; + break; + + case 2: + if (virStrcpyStatic(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_BYTES_SEC); + goto endjob; + } + param->value.ul = reply.write_bytes_sec; + break; + + case 3: + if (virStrcpyStatic(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BLOCK_IOTUNE_TOTAL_IOPS_SEC); + goto endjob; + } + param->value.ul = reply.total_iops_sec; + break; + + case 4: + if (virStrcpyStatic(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BLOCK_IOTUNE_READ_IOPS_SEC); + goto endjob; + } + param->value.ul = reply.read_iops_sec; + break; + + case 5: + if (virStrcpyStatic(param->field, + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Field name '%s' too long"), + VIR_DOMAIN_BLOCK_IOTUNE_WRITE_IOPS_SEC); + goto endjob; + } + param->value.ul = reply.write_iops_sec; + break; + default: + break; + } + } + + if (*nparams > QEMU_NB_BLOCK_IO_TUNE_PARAM) + *nparams = QEMU_NB_BLOCK_IO_TUNE_PARAM; + ret = 0; + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + +cleanup: + VIR_FREE(device); + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} static virDriver qemuDriver = { .no = VIR_DRV_QEMU, @@ -10907,6 +11245,8 @@ static virDriver qemuDriver = { .domainGetBlockJobInfo = qemuDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ + .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */ + .domainGetBlockIoTune = qemuDomainGetBlockIoTune, /* 0.9.8 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 73e5ea9..660aa11 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2567,6 +2567,39 @@ int qemuMonitorBlockJob(qemuMonitorPtr mon, return ret; } +int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr info) +{ + int ret; + + VIR_DEBUG("mon=%p, device=%p, info=%p", mon, device, info); + + if (mon->json) { + ret = qemuMonitorJSONSetBlockIoThrottle(mon, device, info); + } else { + ret = qemuMonitorTextSetBlockIoThrottle(mon, device, info); + } + return ret; +} + +int qemuMonitorGetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr reply) +{ + int ret; + + VIR_DEBUG("mon=%p, device=%p, reply=%p", mon, device, reply); + + if (mon->json) { + ret = qemuMonitorJSONGetBlockIoThrottle(mon, device, reply); + } else { + ret = qemuMonitorTextGetBlockIoThrottle(mon, device, reply); + } + return ret; +} + + int qemuMonitorVMStatusToPausedReason(const char *status) { int st; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 883e0aa..0b14e0c 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -521,6 +521,14 @@ int qemuMonitorOpenGraphics(qemuMonitorPtr mon, const char *fdname, bool skipauth); +int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr info); + +int qemuMonitorGetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr reply); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 56a62db..fb2a1fd 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3276,3 +3276,179 @@ int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, virJSONValueFree(reply); return ret; } + + +static int +qemuMonitorJSONBlockIoThrottleInfo(virJSONValuePtr result, + const char *device, + virDomainBlockIoTuneInfoPtr reply) +{ + virJSONValuePtr io_throttle; + int ret = -1; + int i; + int found = 0; + + io_throttle = virJSONValueObjectGet(result, "return"); + + if (!io_throttle || io_throttle->type != VIR_JSON_TYPE_ARRAY) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _(" block_io_throttle reply was missing device list")); + goto cleanup; + } + + for (i = 0; i < virJSONValueArraySize(io_throttle); i++) { + virJSONValuePtr temp_dev = virJSONValueArrayGet(io_throttle, i); + virJSONValuePtr inserted; + const char *current_dev; + + if (!temp_dev || temp_dev->type != VIR_JSON_TYPE_OBJECT) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("block io throttle device entry was not in expected format")); + goto cleanup; + } + + if ((current_dev = virJSONValueObjectGetString(temp_dev, "device")) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("block io throttle device entry was not in expected format")); + goto cleanup; + } + + if(STRPREFIX(current_dev, QEMU_DRIVE_HOST_PREFIX)) + current_dev += strlen(QEMU_DRIVE_HOST_PREFIX); + + if (STREQ(current_dev, device)) + continue; + + found = 1; + if ((inserted = virJSONValueObjectGet(temp_dev, "inserted")) == NULL || + inserted->type != VIR_JSON_TYPE_OBJECT) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("block io throttle inserted entry was not in expected format")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(inserted, "bps", &reply->total_bytes_sec) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot read total_bytes_sec")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(inserted, "bps_rd", &reply->read_bytes_sec) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot read read_bytes_sec")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(inserted, "bps_wr", &reply->write_bytes_sec) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot read write_bytes_sec")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(inserted, "iops", &reply->total_iops_sec) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot read total_iops_sec")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(inserted, "iops_rd", &reply->read_iops_sec) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot read read_iops_sec")); + goto cleanup; + } + + if (virJSONValueObjectGetNumberUlong(inserted, "iops_wr", &reply->write_iops_sec) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot read write_iops_sec")); + goto cleanup; + } + break; + } + + if (!found) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find throttling info for device '%s'"), + device); + goto cleanup; + } + ret = 0; + +cleanup: + return ret; +} + +int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr info) +{ + int ret = -1; + virJSONValuePtr cmd = NULL; + virJSONValuePtr result = NULL; + + cmd = qemuMonitorJSONMakeCommand("block_set_io_throttle", + "s:device", device, + "U:bps", info->total_bytes_sec, + "U:bps_rd", info->read_bytes_sec, + "U:bps_wr", info->write_bytes_sec, + "U:iops", info->total_iops_sec, + "U:iops_rd", info->read_iops_sec, + "U:iops_wr", info->write_iops_sec, + NULL); + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &result); + + if (ret == 0 && virJSONValueObjectHasKey(result, "error")) { + if (qemuMonitorJSONHasError(result, "DeviceNotActive")) + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("No active operation on device: %s"), device); + else if (qemuMonitorJSONHasError(result, "NotSupported")) + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Operation is not supported for device: %s"), device); + else + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpected error")); + ret = -1; + } + + virJSONValueFree(cmd); + virJSONValueFree(result); + return ret; +} + +int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr reply) +{ + int ret = -1; + virJSONValuePtr cmd = NULL; + virJSONValuePtr result = NULL; + + cmd = qemuMonitorJSONMakeCommand("query-block", NULL); + if (!cmd) { + return -1; + } + + ret = qemuMonitorJSONCommand(mon, cmd, &result); + + if (ret == 0 && virJSONValueObjectHasKey(result, "error")) { + if (qemuMonitorJSONHasError(result, "DeviceNotActive")) + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("No active operation on device: %s"), device); + else if (qemuMonitorJSONHasError(result, "NotSupported")) + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Operation is not supported for device: %s"), device); + else + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpected error")); + ret = -1; + } + + if (ret == 0) + ret = qemuMonitorJSONBlockIoThrottleInfo(result, device, reply); + + virJSONValueFree(cmd); + virJSONValueFree(result); + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index f10d7d2..1b8625f 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -255,4 +255,12 @@ int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, const char *fdname, bool skipauth); +int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr info); + +int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr reply); + #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 5de4d24..c734892 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -812,7 +812,7 @@ int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon, if (!eol) eol = p + strlen(p); - p += devnamelen + 2; /*Skip to first label. */ + p += devnamelen + 2; /* Skip to first label. */ while (*p) { if (STRPREFIX(p, "removable=")) { @@ -3429,3 +3429,152 @@ cleanup: VIR_FREE(cmd); return ret; } + + +int qemuMonitorTextSetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr info) +{ + char *cmd = NULL; + char *result = NULL; + int ret = 0; + const char *cmd_name = NULL; + + /* For the not specified fields, 0 by default */ + cmd_name = "block_set_io_throttle"; + ret = virAsprintf(&cmd, "%s %s %llu %llu %llu %llu %llu %llu", cmd_name, + device, info->total_bytes_sec, info->read_bytes_sec, + info->write_bytes_sec, info->total_iops_sec, + info->read_iops_sec, info->write_iops_sec); + + if (ret < 0) { + virReportOOMError(); + return -1; + } + + if (qemuMonitorHMPCommand(mon, cmd, &result) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot run monitor command")); + ret = -1; + goto cleanup; + } + + if (qemuMonitorTextCommandNotFound(cmd_name, result)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Command '%s' is not found"), cmd_name); + ret = -1; + goto cleanup; + } + +cleanup: + VIR_FREE(cmd); + VIR_FREE(result); + return ret; +} + +static int +qemuMonitorTextParseBlockIoThrottle(const char *result, + const char *device, + virDomainBlockIoTuneInfoPtr reply) +{ + char *dummy = NULL; + int ret = -1; + const char *p, *eol; + int devnamelen = strlen(device); + + p = result; + + while (*p) { + if (STRPREFIX(p, QEMU_DRIVE_HOST_PREFIX)) + p += strlen(QEMU_DRIVE_HOST_PREFIX); + + if (STREQLEN(p, device, devnamelen) && + p[devnamelen] == ':' && p[devnamelen+1] == ' ') { + + eol = strchr(p, '\n'); + if (!eol) + eol = p + strlen(p); + + p += devnamelen + 2; /* Skip to first label. */ + + while (*p) { + if (STRPREFIX(p, "bps=")) { + p += strlen("bps="); + if (virStrToLong_ull(p, &dummy, 10, &reply->total_bytes_sec) == -1) + VIR_DEBUG("error reading total_bytes_sec: %s", p); + } else if (STRPREFIX(p, "bps_rd=")) { + p += strlen("bps_rd="); + if (virStrToLong_ull(p, &dummy, 10, &reply->read_bytes_sec) == -1) + VIR_DEBUG("error reading read_bytes_sec: %s", p); + } else if (STRPREFIX(p, "bps_wr=")) { + p += strlen("bps_wr="); + if (virStrToLong_ull(p, &dummy, 10, &reply->write_bytes_sec) == -1) + VIR_DEBUG("error reading write_bytes_sec: %s", p); + } else if (STRPREFIX(p, "iops=")) { + p += strlen("iops="); + if (virStrToLong_ull(p, &dummy, 10, &reply->total_iops_sec) == -1) + VIR_DEBUG("error reading total_iops_sec: %s", p); + } else if (STRPREFIX(p, "iops_rd=")) { + p += strlen("iops_rd="); + if (virStrToLong_ull(p, &dummy, 10, &reply->read_iops_sec) == -1) + VIR_DEBUG("error reading read_iops_sec: %s", p); + } else if (STRPREFIX(p, "iops_wr=")) { + p += strlen("iops_wr="); + if (virStrToLong_ull(p, &dummy, 10, &reply->write_iops_sec) == -1) + VIR_DEBUG("error reading write_iops_sec: %s", p); + } else { + VIR_DEBUG(" unknown block info %s", p); + } + + /* Skip to next label. */ + p = strchr (p, ' '); + if (!p || p >= eol) + break; + p++; + } + ret = 0; + goto cleanup; + } + + /* Skip to next line. */ + p = strchr (p, '\n'); + if (!p) + break; + p++; + } + + qemuReportError(VIR_ERR_INVALID_ARG, + _("No info for device '%s'"), device); + +cleanup: + return ret; +} + +int qemuMonitorTextGetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr reply) +{ + char *result = NULL; + int ret = 0; + const char *cmd_name = "info block"; + + if (qemuMonitorHMPCommand(mon, cmd_name, &result) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot run monitor command")); + ret = -1; + goto cleanup; + } + + if (qemuMonitorTextCommandNotFound(cmd_name, result)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("Command '%s' is not found"), cmd_name); + ret = -1; + goto cleanup; + } + + ret = qemuMonitorTextParseBlockIoThrottle(result, device, reply); + +cleanup: + VIR_FREE(result); + return ret; +} diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index f32fce0..c3e97e0 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -248,4 +248,12 @@ int qemuMonitorTextOpenGraphics(qemuMonitorPtr mon, const char *fdname, bool skipauth); +int qemuMonitorTextSetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr info); + +int qemuMonitorTextGetBlockIoThrottle(qemuMonitorPtr mon, + const char *device, + virDomainBlockIoTuneInfoPtr reply); + #endif /* QEMU_MONITOR_TEXT_H */ -- 1.7.7.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list