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)) {
|
if (virBufferError(&opt)) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -95,6 +95,8 @@
|
|||||||
|
|
||||||
#define QEMU_NB_MEM_PARAM 3
|
#define QEMU_NB_MEM_PARAM 3
|
||||||
|
|
||||||
|
#define QEMU_NB_BLOCK_IO_TUNE_PARAM 6
|
||||||
|
|
||||||
#if HAVE_LINUX_KVM_H
|
#if HAVE_LINUX_KVM_H
|
||||||
# include <linux/kvm.h>
|
# include <linux/kvm.h>
|
||||||
#endif
|
#endif
|
||||||
@ -11062,6 +11064,336 @@ cleanup:
|
|||||||
return ret;
|
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 = {
|
static virDriver qemuDriver = {
|
||||||
.no = VIR_DRV_QEMU,
|
.no = VIR_DRV_QEMU,
|
||||||
@ -11209,6 +11541,8 @@ static virDriver qemuDriver = {
|
|||||||
.domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */
|
.domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */
|
||||||
.isAlive = qemuIsAlive, /* 0.9.8 */
|
.isAlive = qemuIsAlive, /* 0.9.8 */
|
||||||
.nodeSuspendForDuration = nodeSuspendForDuration, /* 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;
|
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 qemuMonitorVMStatusToPausedReason(const char *status)
|
||||||
{
|
{
|
||||||
int st;
|
int st;
|
||||||
|
@ -522,6 +522,14 @@ int qemuMonitorOpenGraphics(qemuMonitorPtr mon,
|
|||||||
const char *fdname,
|
const char *fdname,
|
||||||
bool skipauth);
|
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
|
* When running two dd process and using <> redirection, we need a
|
||||||
* shell that will not truncate files. These two strings serve that
|
* shell that will not truncate files. These two strings serve that
|
||||||
|
@ -3308,3 +3308,179 @@ int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon,
|
|||||||
virJSONValueFree(reply);
|
virJSONValueFree(reply);
|
||||||
return ret;
|
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,
|
const char *fdname,
|
||||||
bool skipauth);
|
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 */
|
#endif /* QEMU_MONITOR_JSON_H */
|
||||||
|
@ -812,7 +812,7 @@ int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon,
|
|||||||
if (!eol)
|
if (!eol)
|
||||||
eol = p + strlen(p);
|
eol = p + strlen(p);
|
||||||
|
|
||||||
p += devnamelen + 2; /*Skip to first label. */
|
p += devnamelen + 2; /* Skip to first label. */
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
if (STRPREFIX(p, "removable=")) {
|
if (STRPREFIX(p, "removable=")) {
|
||||||
@ -3462,3 +3462,152 @@ cleanup:
|
|||||||
VIR_FREE(cmd);
|
VIR_FREE(cmd);
|
||||||
return ret;
|
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,
|
const char *fdname,
|
||||||
bool skipauth);
|
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 */
|
#endif /* QEMU_MONITOR_TEXT_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user