--- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list