mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-03-07 17:28:15 +00:00
Implement virDomain{Set, Get}BlockIoTune for the qemu driver
Implement the block I/O throttle setting and getting support to qemu driver. Signed-off-by: Lei Li <lilei@linux.vnet.ibm.com> Signed-off-by: Zhi Yong Wu <wuzhy@linux.vnet.ibm.com> Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
6df7ccb10e
commit
eca96694a7
@ -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;
|
||||
|
@ -95,6 +95,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
|
||||
@ -11062,6 +11064,336 @@ 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;
|
||||
int idx = -1;
|
||||
|
||||
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;
|
||||
idx = virDomainDiskIndexByName(persistentDef, disk, true);
|
||||
if (idx < 0)
|
||||
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_LIVE) {
|
||||
priv = vm->privateData;
|
||||
qemuDomainObjEnterMonitorWithDriver(driver, vm);
|
||||
ret = qemuMonitorSetBlockIoThrottle(priv->mon, device, &info);
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto endjob;
|
||||
|
||||
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
|
||||
sa_assert(persistentDef && idx >= 0);
|
||||
persistentDef->disks[idx]->blkdeviotune = info;
|
||||
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 (!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,
|
||||
@ -11209,6 +11541,8 @@ static virDriver qemuDriver = {
|
||||
.domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */
|
||||
.isAlive = qemuIsAlive, /* 0.9.8 */
|
||||
.nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */
|
||||
.domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */
|
||||
.domainGetBlockIoTune = qemuDomainGetBlockIoTune, /* 0.9.8 */
|
||||
};
|
||||
|
||||
|
||||
|
@ -2582,6 +2582,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;
|
||||
|
@ -522,6 +522,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
|
||||
|
@ -3308,3 +3308,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;
|
||||
}
|
||||
|
@ -257,4 +257,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 */
|
||||
|
@ -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=")) {
|
||||
@ -3462,3 +3462,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;
|
||||
}
|
||||
|
@ -250,4 +250,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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user