On Thu, Nov 10, 2011 at 04:32:53AM +0800, Lei HH Li wrote: > This patch implement the blk io throttle setting and getting support to qemu > driver. > > > Signed-off-by: Zhi Yong Wu <wuzhy@xxxxxxxxxxxxxxxxxx> > Signed-off-by: Lei Li <lilei@xxxxxxxxxxxxxxxxxx> > --- > src/qemu/qemu_driver.c | 216 ++++++++++++++++++++++++++++++++++++++++++ > src/qemu/qemu_monitor.c | 36 +++++++ > src/qemu/qemu_monitor.h | 10 ++ > src/qemu/qemu_monitor_json.c | 191 +++++++++++++++++++++++++++++++++++++ > src/qemu/qemu_monitor_json.h | 10 ++ > src/qemu/qemu_monitor_text.c | 152 +++++++++++++++++++++++++++++ > src/qemu/qemu_monitor_text.h | 10 ++ > 7 files changed, 625 insertions(+), 0 deletions(-) > > diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c > index db2ac0d..7ca6719 100644 > --- a/src/qemu/qemu_driver.c > +++ b/src/qemu/qemu_driver.c > @@ -10759,6 +10759,220 @@ cleanup: > return ret; > } > > +static int > +qemuDomainSetBlockIoTune(virDomainPtr dom, > + const char *disk, > + virDomainBlockIoTuneInfoPtr info, > + unsigned int flags) > +{ > + struct qemud_driver *driver = dom->conn->privateData; > + virDomainObjPtr vm = NULL; > + qemuDomainObjPrivatePtr priv; > + virDomainDefPtr persistentDef = NULL; > + char uuidstr[VIR_UUID_STRING_BUFLEN]; > + const char *device = NULL; > + int ret = -1; > + bool isActive; > + > + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | > + VIR_DOMAIN_AFFECT_CONFIG, -1); > + > + 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 ((info->total_bytes_sec && info->read_bytes_sec) || > + (info->total_bytes_sec && info->write_bytes_sec)) { > + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("bps and bps_rd/bps_wr cannot be used at the same time")); These messages should use the user visible names for the fields (ie. read_bytes_sec). > + goto endjob; > + } > + > + if ((info->total_iops_sec && info->read_iops_sec) || > + (info->total_iops_sec && info->write_iops_sec)) { > + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("iops and iops_rd/iops_wr cannot be used at the same time")); Same here. > + 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_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; > + } > + > + if (flags & VIR_DOMAIN_AFFECT_LIVE) { > + priv = vm->privateData; > + qemuDomainObjEnterMonitorWithDriver(driver, vm); > + ret = qemuMonitorSetBlockIoThrottle(priv->mon, device, info, 1); > + qemuDomainObjExitMonitorWithDriver(driver, vm); > + } > + > + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { > + sa_assert(persistentDef); > + int i = virDomainDiskIndexByName(vm->def, disk, true); > + if (i < 0) > + goto endjob; > + persistentDef->disks[i]->blkdeviotune.total_bytes_sec = info->total_bytes_sec; > + persistentDef->disks[i]->blkdeviotune.read_bytes_sec = info->read_bytes_sec; > + persistentDef->disks[i]->blkdeviotune.write_bytes_sec = info->write_bytes_sec; > + persistentDef->disks[i]->blkdeviotune.total_iops_sec = info->total_iops_sec; > + persistentDef->disks[i]->blkdeviotune.read_iops_sec = info->read_iops_sec; > + persistentDef->disks[i]->blkdeviotune.write_iops_sec = info->write_iops_sec; > + persistentDef->disks[i]->blkdeviotune.mark = 1; > + } > + > + 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, > + virDomainBlockIoTuneInfoPtr reply, > + unsigned int flags) > +{ > + struct qemud_driver *driver = dom->conn->privateData; > + virDomainObjPtr vm = NULL; > + qemuDomainObjPrivatePtr priv; > + virDomainDefPtr persistentDef = NULL; > + char uuidstr[VIR_UUID_STRING_BUFLEN]; > + const char *device = NULL; > + int ret = -1; > + bool isActive; > + > + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | > + VIR_DOMAIN_AFFECT_CONFIG, -1); > + > + 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 ((flags & VIR_DOMAIN_AFFECT_LIVE) && (flags & VIR_DOMAIN_AFFECT_CONFIG)) { > + qemuReportError(VIR_ERR_INVALID_ARG, "%s", > + _("Cannot query with --live and --config together")); > + goto cleanup; > + } > + > + if (!isActive && (flags & VIR_DOMAIN_AFFECT_LIVE)) { > + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("domain is not running")); > + goto cleanup; > + } > + > + if (flags & VIR_DOMAIN_AFFECT_LIVE) { > + priv = vm->privateData; > + qemuDomainObjEnterMonitorWithDriver(driver, vm); > + ret = qemuMonitorGetBlockIoThrottle(priv->mon, device, reply, 0); > + qemuDomainObjExitMonitorWithDriver(driver, vm); > + } > + > + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { > + if (!vm->persistent) { > + qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("domain is transient")); > + goto cleanup; > + } > + if (!(persistentDef = virDomainObjGetPersistentDef(driver->caps, vm))) > + goto cleanup; > + > + sa_assert(persistentDef); > + int i = virDomainDiskIndexByName(vm->def, disk, true); > + if (i < 0) > + goto cleanup; > + reply->total_bytes_sec = persistentDef->disks[i]->blkdeviotune.total_bytes_sec; > + reply->read_bytes_sec = persistentDef->disks[i]->blkdeviotune.read_bytes_sec; > + reply->write_bytes_sec = persistentDef->disks[i]->blkdeviotune.write_bytes_sec; > + reply->total_iops_sec = persistentDef->disks[i]->blkdeviotune.total_iops_sec; > + reply->read_iops_sec = persistentDef->disks[i]->blkdeviotune.read_iops_sec; > + reply->write_iops_sec = persistentDef->disks[i]->blkdeviotune.write_iops_sec; > + ret = 0; > + } > + > + if (qemuDomainObjEndJob(driver, vm) == 0) { > + vm = NULL; > + goto cleanup; > + } > + > +cleanup: > + VIR_FREE(device); > + if (vm) > + virDomainObjUnlock(vm); > + qemuDriverUnlock(driver); > + return ret; > +} > > static virDriver qemuDriver = { > .no = VIR_DRV_QEMU, > @@ -10903,6 +11117,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..5f5bd08 100644 > --- a/src/qemu/qemu_monitor.c > +++ b/src/qemu/qemu_monitor.c > @@ -2567,6 +2567,42 @@ int qemuMonitorBlockJob(qemuMonitorPtr mon, > return ret; > } > > +int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr info, > + unsigned int flags) > +{ > + int ret; > + > + VIR_DEBUG("mon=%p, device=%p, info=%p, flags=%x", > + mon, device, info, flags); > + > + if (mon->json) { > + ret = qemuMonitorJSONSetBlockIoThrottle(mon, device, info, flags); > + } else { > + ret = qemuMonitorTextSetBlockIoThrottle(mon, device, info, flags); > + } > + return ret; > +} > + > +int qemuMonitorGetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr reply, > + unsigned int flags) > +{ > + int ret; > + > + VIR_DEBUG("mon=%p, device=%p, reply=%p, flags=%x", > + mon, device, reply, flags); > + > + if (mon->json) { > + ret = qemuMonitorJSONGetBlockIoThrottle(mon, device, reply, flags); > + } else { > + ret = qemuMonitorTextGetBlockIoThrottle(mon, device, reply, flags); > + } > + 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..bf00068 100644 > --- a/src/qemu/qemu_monitor.h > +++ b/src/qemu/qemu_monitor.h > @@ -521,6 +521,16 @@ int qemuMonitorOpenGraphics(qemuMonitorPtr mon, > const char *fdname, > bool skipauth); > > +int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr info, > + unsigned int flags); > + > +int qemuMonitorGetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr reply, > + unsigned int flags); > + > /** > * 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..7501c59 100644 > --- a/src/qemu/qemu_monitor_json.c > +++ b/src/qemu/qemu_monitor_json.c > @@ -3276,3 +3276,194 @@ 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, > + _("cannot read %s"), > + "total_bytes_sec"); The 2 lines just above should be merged together. The same applies for each field below. > + goto cleanup; > + } > + > + if (virJSONValueObjectGetNumberUlong(inserted, "bps_rd", &reply->read_bytes_sec) < 0) { > + qemuReportError(VIR_ERR_INTERNAL_ERROR, > + _("cannot read %s"), > + "read_bytes_sec"); > + goto cleanup; > + } > + > + if (virJSONValueObjectGetNumberUlong(inserted, "bps_wr", &reply->write_bytes_sec) < 0) { > + qemuReportError(VIR_ERR_INTERNAL_ERROR, > + _("cannot read %s"), > + "write_bytes_sec"); > + goto cleanup; > + } > + > + if (virJSONValueObjectGetNumberUlong(inserted, "iops", &reply->total_iops_sec) < 0) { > + qemuReportError(VIR_ERR_INTERNAL_ERROR, > + _("cannot read %s"), > + "total_iops_sec"); > + goto cleanup; > + } > + > + if (virJSONValueObjectGetNumberUlong(inserted, "iops_rd", &reply->read_iops_sec) < 0) { > + qemuReportError(VIR_ERR_INTERNAL_ERROR, > + _("cannot read %s"), > + "read_iops_sec"); > + goto cleanup; > + } > + > + if (virJSONValueObjectGetNumberUlong(inserted, "iops_wr", &reply->write_iops_sec) < 0) { > + qemuReportError(VIR_ERR_INTERNAL_ERROR, > + _("cannot read %s"), > + "write_iops_sec"); > + goto cleanup; > + } > + break; > + } > + > + if (!found) { > + qemuReportError(VIR_ERR_INTERNAL_ERROR, > + _("cannot found info for device '%s'"), > + device); > + goto cleanup; > + } > + ret = 0; > + > +cleanup: > + return ret; > +} > + > +int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr info, > + unsigned int flags) > +{ > + int ret = -1; > + virJSONValuePtr cmd = NULL; > + virJSONValuePtr result = NULL; > + > + if (flags) { > + 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); > + } Does this work properly if the user chose not to modify some of the fields? > + > + 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, > + unsigned int flags) > +{ > + int ret = -1; > + virJSONValuePtr cmd = NULL; > + virJSONValuePtr result = NULL; > + > + if (!flags) { > + 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 && !flags) > + 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..bf12dc5 100644 > --- a/src/qemu/qemu_monitor_json.h > +++ b/src/qemu/qemu_monitor_json.h > @@ -255,4 +255,14 @@ int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, > const char *fdname, > bool skipauth); > > +int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr info, > + unsigned int flags); > + > +int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr reply, > + unsigned int flags); > + > #endif /* QEMU_MONITOR_JSON_H */ > diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c > index 5de4d24..5c92e60 100644 > --- a/src/qemu/qemu_monitor_text.c > +++ b/src/qemu/qemu_monitor_text.c > @@ -3429,3 +3429,155 @@ cleanup: > VIR_FREE(cmd); > return ret; > } > + > +int qemuMonitorTextSetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr info, > + unsigned int flags) > +{ > + char *cmd = NULL; > + char *result = NULL; > + int ret = 0; > + > + if (flags) { > + ret = virAsprintf(&cmd, "block_set_io_throttle %s %llu %llu %llu %llu %llu %llu", > + 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); > + } Does this one work if the user chose not to specify all of the fields? > + > + 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; > + } > + > +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, > + unsigned int flags) > +{ > + char *cmd = NULL; > + char *result = NULL; > + int ret = 0; > + > + if (flags) { > + ret = virAsprintf(&cmd, "info block"); > + } > + > + 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 (strstr(result, "\ninfo ")) { > + qemuReportError(VIR_ERR_OPERATION_INVALID, > + "%s", > + _("info block not supported by this qemu")); > + ret = -1; > + goto cleanup; > + } Please use the function qemuMonitorTextCommandNotFound() instead of open-coding the above logic here. > + ret = qemuMonitorTextParseBlockIoThrottle(result, device, reply); > + > +cleanup: > + VIR_FREE(cmd); > + VIR_FREE(result); > + return ret; > +} > + > diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h > index f32fce0..1c47d39 100644 > --- a/src/qemu/qemu_monitor_text.h > +++ b/src/qemu/qemu_monitor_text.h > @@ -248,4 +248,14 @@ int qemuMonitorTextOpenGraphics(qemuMonitorPtr mon, > const char *fdname, > bool skipauth); > > +int qemuMonitorTextSetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr info, > + unsigned int flags); > + > +int qemuMonitorTextGetBlockIoThrottle(qemuMonitorPtr mon, > + const char *device, > + virDomainBlockIoTuneInfoPtr reply, > + unsigned int flags); > + > #endif /* QEMU_MONITOR_TEXT_H */ > -- > 1.7.1 > -- Adam Litke <agl@xxxxxxxxxx> IBM Linux Technology Center -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list