---
src/qemu/qemu_driver.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 411 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 9ddbc0f..03b8b9b 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5942,6 +5942,415 @@ qemuGetSchedulerParameters(virDomainPtr dom,
VIR_DOMAIN_AFFECT_CURRENT);
}
+static int
+qemuGetVcpusBWConfig(struct qemud_driver *driver, virDomainObjPtr vm,
+ bool active, virDomainVcpuBWDefPtr vcpubw_list, int *nvcpu)
+{
+ int i;
+ virDomainVcpuBWDefPtr vcpubw = NULL;
+ virDomainDefPtr vmdef = NULL;
+
+ if (active) {
+ vmdef = virDomainObjGetPersistentDef(driver->caps, vm);
+ if (!vmdef) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("can't get persistentDef"));
+ return -1;
+ }
+ } else {
+ vmdef = vm->def;
+ }
+
+ for (i = 0; i < *nvcpu; i++) {
+ if (i >= vmdef->vcpus)
+ break;
+
+ vcpubw = virDomainVcpuBWFindByVcpu(vmdef->cputune.vcpubw,
+ vmdef->cputune.nvcpubw,
+ i);
+ vcpubw_list[i].vcpuid = i;
+ if (vcpubw) {
+ vcpubw_list[i].quota = vcpubw->quota;
+ vcpubw_list[i].period = vcpubw->period;
+ } else {
+ vcpubw_list[i].quota = 0;
+ vcpubw_list[i].period = 0;
+ }
+ }
+
+ *nvcpu = i;
+ return 0;
+}
+
+static int
+qemuGetVcpuBWLive(virCgroupPtr cgroup, virDomainVcpuBWDefPtr vcpubw)
+{
+ unsigned long long period;
+ long long quota;
+ int rc;
+
+ rc = virCgroupGetCpuCfsPeriod(cgroup, &period);
+ if (rc < 0) {
+ virReportSystemError(-rc, "%s",
+ _("unable to get cpu bandwidth period tunable"));
+ return -1;
+ }
+
+ rc = virCgroupGetCpuCfsQuota(cgroup, "a);
+ if (rc < 0) {
+ virReportSystemError(-rc, "%s",
+ _("unable to get cpu bandwidth tunable"));
+ return -1;
+ }
+
+ vcpubw->period = period;
+ vcpubw->quota = quota;
+
+ return 0;
+}
+
+static int
+qemuGetVcpusBWLive(struct qemud_driver *driver, virDomainObjPtr vm,
+ virDomainVcpuBWDefPtr vcpubw_list, int *nvcpu)
+{
+ virCgroupPtr cgroup = NULL;
+ virCgroupPtr cgroup_vcpu = NULL;
+ qemuDomainObjPrivatePtr priv = NULL;
+ int rc;
+ int i;
+ int ret = -1;
+
+ if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("cgroup CPU controller is not
mounted"));
+ return -1;
+ }
+
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0)
{
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find cgroup for domain %s"),
vm->def->name);
+ return -1;
+ }
+
+ priv = vm->privateData;
+ if (priv->nvcpupids == 0 || priv->vcpupids[0] == vm->pid) {
+ /* We do not create sub dir for each vcpu */
+ for (i = 0; i < *nvcpu; i++) {
+ if (i >= vm->def->vcpus)
+ break;
+
+ vcpubw_list[i].vcpuid = i;
+ rc = qemuGetVcpuBWLive(cgroup, &vcpubw_list[i]);
+ if (rc < 0)
+ goto cleanup;
+ }
+ *nvcpu = i;
+ goto out;
+ }
+
+ for (i = 0; i < *nvcpu; i++) {
+ if (i >= vm->def->vcpus)
+ break;
+
+ rc = virCgroupForVcpu(cgroup, i, &cgroup_vcpu, 0);
+ if (!cgroup_vcpu) {
+ virReportSystemError(-rc,
+ _("Unable to find vcpu cgroup for %s(vcpu:"
+ " %d)"),
+ vm->def->name, i);
+ goto cleanup;
+ }
+
+ vcpubw_list[i].vcpuid = i;
+ rc = qemuGetVcpuBWLive(cgroup_vcpu, &vcpubw_list[i]);
+ if (rc < 0)
+ goto cleanup;
+ virCgroupFree(&cgroup_vcpu);
+ }
+ *nvcpu = i;
+
+out:
+ ret = 0;
+
+cleanup:
+ virCgroupFree(&cgroup_vcpu);
+ virCgroupFree(&cgroup);
+ return ret;
+}
+
+static int
+qemuGetVcpuBW(virDomainPtr dom, virDomainVcpuBWDefPtr vcpubw,
+ int *nvcpu, unsigned int flags)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ bool isActive;
+
+ virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
+ VIR_DOMAIN_AFFECT_CONFIG, -1);
+
+ if ((flags & (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) ==
+ (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("cannot query live and config together"));
+ return -1;
+ }
+
+ if (*nvcpu < 1) {
+ qemuReportError(VIR_ERR_INVALID_ARG,
+ "%s", _("Invalid vcpu count"));
+ return -1;
+ }
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (vm == NULL) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ 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_CONFIG) {
+ if (!vm->persistent) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("cannot query persistent config of a transient
domain"));
+ goto cleanup;
+ }
+
+ ret = qemuGetVcpusBWConfig(driver, vm, isActive, vcpubw, nvcpu);
+ if (ret < 0)
+ goto cleanup;
+ }
+
+ if (flags & VIR_DOMAIN_AFFECT_LIVE) {
+ if (!isActive) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain is not running"));
+ goto cleanup;
+ }
+
+ ret = qemuGetVcpusBWLive(driver, vm, vcpubw, nvcpu);
+ if (ret < 0)
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
+static int
+qemuSetVcpuBWConfig(virDomainDefPtr vmdef, virDomainVcpuBWDefPtr vcpubw,
+ int nvcpu)
+{
+ int i;
+
+ for (i = 0; i < nvcpu; i++) {
+ if (vcpubw[i].vcpuid < 0 || vcpubw[i].vcpuid >= vmdef->vcpus) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid vcpu
id"));
+ return -1;
+ }
+
+ if (vcpubw[i].period == 0 && vcpubw[i].quota == 0) {
+ if (virDomainVcpuBWDel(vmdef, vcpubw[i].vcpuid) < 0)
+ return -1;
+ } else {
+ if (virDomainVcpuBWAdd(vmdef, vcpubw[i].period,
+ vcpubw[i].quota, vcpubw[i].vcpuid) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+qemuSetVcpuBWLive(struct qemud_driver *driver, virDomainObjPtr vm,
+ virCgroupPtr cgroup, virDomainVcpuBWDefPtr vcpubw)
+{
+ if (qemuSetupCgroupVcpuBW(cgroup, vcpubw) < 0)
+ return -1;
+
+ if (qemuSetVcpuBWConfig(vm->def, vcpubw, 1) < 0)
+ return -1;
+
+ if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+qemuSetVcpusBWLive(struct qemud_driver *driver, virDomainObjPtr vm,
+ virCgroupPtr cgroup, virDomainVcpuBWDefPtr vcpubw,
+ int nvcpubw)
+{
+ int i;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virCgroupPtr cgroup_vcpu = NULL;
+ int rc;
+
+ /* check vcpu id first */
+ for (i = 0; i < nvcpubw; i++) {
+ if (vcpubw[i].vcpuid < 0 || vcpubw[i].vcpuid >= vm->def->vcpus) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid vcpu
id"));
+ return -1;
+ }
+ }
+
+ if (priv->nvcpupids == 0 || priv->vcpupids[0] == vm->pid) {
+ /* If we does not know VCPU<->PID mapping or all vcpu runs in the same
+ * thread, we can not control each vcpu. So just use the last config.
+ */
+ if (vcpubw[nvcpubw -1].period || vcpubw[nvcpubw - 1].quota) {
+ return qemuSetVcpuBWLive(driver, vm, cgroup, &vcpubw[nvcpubw - 1]);
+ }
+ return 0;
+ }
+
+ for (i = 0; i < nvcpubw; i++) {
+ if (vcpubw[i].period == 0 && vcpubw[i].quota == 0)
+ continue;
+
+ rc = virCgroupForVcpu(cgroup, vcpubw[i].vcpuid, &cgroup_vcpu, 0);
+ if (rc < 0) {
+ virReportSystemError(-rc,
+ _("Unable to find vcpu cgroup for %s(vcpu:"
+ " %d)"),
+ vm->def->name, i);
+ goto cleanup;
+ }
+
+ if (qemuSetVcpuBWLive(driver, vm, cgroup_vcpu, &vcpubw[i]) < 0)
+ goto cleanup;
+
+ virCgroupFree(&cgroup_vcpu);
+ }
+
+ return 0;
+
+cleanup:
+ virCgroupFree(&cgroup_vcpu);
+ return -1;
+}
+
+static int
+qemuSetVcpuBW(virDomainPtr dom, virDomainVcpuBWDefPtr vcpubw,
+ int nvcpu, unsigned int flags)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virCgroupPtr cgroup = NULL;
+ virDomainObjPtr vm = NULL;
+ int ret = -1;
+ bool isActive;
+ virDomainDefPtr vmdef = NULL;
+
+ virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
+ VIR_DOMAIN_AFFECT_CONFIG, -1);
+
+ if (nvcpu < 1) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid vcpu
count"));
+ return -1;
+ }
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+ if (vm == NULL) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ 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_CONFIG) && !vm->persistent) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("cannot change persistent config of a transient
domain"));
+ goto cleanup;
+ }
+
+ if (flags & VIR_DOMAIN_AFFECT_LIVE) {
+ if (!isActive) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("cgroup CPU controller is not
mounted"));
+ goto cleanup;
+ }
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=
0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find cgroup for domain %s"),
+ vm->def->name);
+ goto cleanup;
+ }
+ }
+
+ if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
+ /* Make a copy for updated domain. */
+ vmdef = virDomainObjCopyPersistentDef(driver->caps, vm);
+ if (!vmdef)
+ goto cleanup;
+
+ if (qemuSetVcpuBWConfig(vmdef, vcpubw, nvcpu) < 0)
+ goto cleanup;
+ }
+
+ if (flags & VIR_DOMAIN_AFFECT_LIVE) {
+ if (qemuSetVcpusBWLive(driver, vm, cgroup, vcpubw, nvcpu) < 0)
+ goto cleanup;
+ }
+
+ /* Finally, if no error until here, we can save config. */
+ if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
+ ret = virDomainSaveConfig(driver->configDir, vmdef);
+ if (ret < 0)
+ goto cleanup;
+ virDomainObjAssignDef(vm, vmdef, false);
+ vmdef = NULL;
+ }
+
+ ret = 0;
+
+cleanup:
+ virCgroupFree(&cgroup);
+ virDomainDefFree(vmdef);
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
/* This uses the 'info blockstats' monitor command which was
* integrated into both qemu & kvm in late 2007. If the command is
* not supported we detect this and return the appropriate error.
@@ -8477,6 +8886,8 @@ static virDriver qemuDriver = {
.domainGetSchedulerParametersFlags = qemuGetSchedulerParametersFlags, /* 0.9.2 */
.domainSetSchedulerParameters = qemuSetSchedulerParameters, /* 0.7.0 */
.domainSetSchedulerParametersFlags = qemuSetSchedulerParametersFlags, /* 0.9.2 */
+ .domainGetVcpuBW = qemuGetVcpuBW, /* 0.9.4 */
+ .domainSetVcpuBW = qemuSetVcpuBW, /* 0.9.4 */
.domainMigratePerform = qemudDomainMigratePerform, /* 0.5.0 */
.domainBlockStats = qemudDomainBlockStats, /* 0.4.1 */
.domainInterfaceStats = qemudDomainInterfaceStats, /* 0.4.1 */
--
1.7.1