Implement schedular tunables API using cgroups

* src/qemu_driver.c:  Add driver methods qemuGetSchedulerType,
  qemuGetSchedulerParameters, qemuSetSchedulerParameters
* src/lxc_driver.c: Fix to use unsigned long long consistently
  for schedular parameters
* src/cgroup.h, src/cgroup.c: Fix cpu_shares to take unsigned
  long long
* src/util.c, src/util.h, src/libvirt_private.syms: Add a
  virStrToDouble helper
* src/virsh.c: Fix handling of --set arg to schedinfo command
  to honour the designated data type of each schedular tunable
  as declared by the driver
This commit is contained in:
Daniel P. Berrange 2009-07-09 14:11:21 +01:00
parent 38f6f47be9
commit 55bc5090b9
8 changed files with 320 additions and 122 deletions

View File

@ -790,23 +790,23 @@ int virCgroupAllowDeviceMajor(virCgroupPtr group,
return rc;
}
int virCgroupSetCpuShares(virCgroupPtr group, unsigned long shares)
int virCgroupSetCpuShares(virCgroupPtr group, unsigned long long shares)
{
return virCgroupSetValueU64(group,
VIR_CGROUP_CONTROLLER_CPU,
"cpu.shares", (uint64_t)shares);
"cpu.shares", shares);
}
int virCgroupGetCpuShares(virCgroupPtr group, unsigned long *shares)
int virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares)
{
return virCgroupGetValueU64(group,
VIR_CGROUP_CONTROLLER_CPU,
"cpu.shares", (uint64_t *)shares);
"cpu.shares", shares);
}
int virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage)
{
return virCgroupGetValueU64(group,
VIR_CGROUP_CONTROLLER_CPUACCT,
"cpuacct.usage", (uint64_t *)usage);
"cpuacct.usage", usage);
}

View File

@ -39,8 +39,8 @@ int virCgroupAllowDeviceMajor(virCgroupPtr group,
char type,
int major);
int virCgroupSetCpuShares(virCgroupPtr group, unsigned long shares);
int virCgroupGetCpuShares(virCgroupPtr group, unsigned long *shares);
int virCgroupSetCpuShares(virCgroupPtr group, unsigned long long shares);
int virCgroupGetCpuShares(virCgroupPtr group, unsigned long long *shares);
int virCgroupGetCpuacctUsage(virCgroupPtr group, unsigned long long *usage);

View File

@ -348,6 +348,7 @@ virStrToLong_i;
virStrToLong_ll;
virStrToLong_ull;
virStrToLong_ui;
virStrToDouble;
virFileLinkPointsTo;
virFileResolveLink;
saferead;

View File

@ -1612,9 +1612,14 @@ static int lxcSetSchedulerParameters(virDomainPtr domain,
for (i = 0; i < nparams; i++) {
virSchedParameterPtr param = &params[i];
if (param->type != VIR_DOMAIN_SCHED_FIELD_ULLONG) {
lxcError(NULL, domain, VIR_ERR_INVALID_ARG,
_("invalid type for cpu_shares tunable, expected a 'ullong'"));
goto cleanup;
}
if (STREQ(param->field, "cpu_shares")) {
if (virCgroupSetCpuShares(group, params[i].value.ui) != 0)
if (virCgroupSetCpuShares(group, params[i].value.ul) != 0)
goto cleanup;
} else {
lxcError(NULL, domain, VIR_ERR_INVALID_ARG,
@ -1638,7 +1643,7 @@ static int lxcGetSchedulerParameters(virDomainPtr domain,
lxc_driver_t *driver = domain->conn->privateData;
virCgroupPtr group = NULL;
virDomainObjPtr vm = NULL;
unsigned long val;
unsigned long long val;
int ret = -1;
if (driver->cgroup == NULL)

View File

@ -5275,6 +5275,151 @@ cleanup:
return ret;
}
static char *qemuGetSchedulerType(virDomainPtr dom,
int *nparams)
{
struct qemud_driver *driver = dom->conn->privateData;
char *ret;
if (driver->cgroup == NULL) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
__FUNCTION__);
return NULL;
}
if (nparams)
*nparams = 1;
ret = strdup("posix");
if (!ret)
virReportOOMError(dom->conn);
return ret;
}
static int qemuSetSchedulerParameters(virDomainPtr dom,
virSchedParameterPtr params,
int nparams)
{
struct qemud_driver *driver = dom->conn->privateData;
int i;
virCgroupPtr group = NULL;
virDomainObjPtr vm = NULL;
int ret = -1;
if (driver->cgroup == NULL) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
__FUNCTION__);
return -1;
}
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
qemuDriverUnlock(driver);
if (vm == NULL) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
_("No such domain %s"), dom->uuid);
goto cleanup;
}
if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot find cgroup for domain %s"), vm->def->name);
goto cleanup;
}
for (i = 0; i < nparams; i++) {
virSchedParameterPtr param = &params[i];
if (STREQ(param->field, "cpu_shares")) {
int rc;
if (param->type != VIR_DOMAIN_SCHED_FIELD_ULLONG) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
_("invalid type for cpu_shares tunable, expected a 'ullong'"));
goto cleanup;
}
rc = virCgroupSetCpuShares(group, params[i].value.ul);
if (rc != 0) {
virReportSystemError(dom->conn, -rc, "%s",
_("unable to set cpu shares tunable"));
goto cleanup;
}
} else {
qemudReportError(dom->conn, domain, NULL, VIR_ERR_INVALID_ARG,
_("Invalid parameter `%s'"), param->field);
goto cleanup;
}
}
ret = 0;
cleanup:
virCgroupFree(&group);
if (vm)
virDomainObjUnlock(vm);
return ret;
}
static int qemuGetSchedulerParameters(virDomainPtr dom,
virSchedParameterPtr params,
int *nparams)
{
struct qemud_driver *driver = dom->conn->privateData;
virCgroupPtr group = NULL;
virDomainObjPtr vm = NULL;
unsigned long long val;
int ret = -1;
int rc;
if (driver->cgroup == NULL) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
__FUNCTION__);
return -1;
}
if ((*nparams) != 1) {
qemudReportError(dom->conn, domain, NULL, VIR_ERR_INVALID_ARG,
"%s", _("Invalid parameter count"));
return -1;
}
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
qemuDriverUnlock(driver);
if (vm == NULL) {
qemudReportError(dom->conn, domain, NULL, VIR_ERR_INTERNAL_ERROR,
_("No such domain %s"), dom->uuid);
goto cleanup;
}
if (virCgroupForDomain(driver->cgroup, vm->def->name, &group, 0) != 0) {
qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot find cgroup for domain %s"), vm->def->name);
goto cleanup;
}
rc = virCgroupGetCpuShares(group, &val);
if (rc != 0) {
virReportSystemError(dom->conn, -rc, "%s",
_("unable to get cpu shares tunable"));
goto cleanup;
}
params[0].value.ul = val;
strncpy(params[0].field, "cpu_shares", sizeof(params[0].field));
params[0].type = VIR_DOMAIN_SCHED_FIELD_ULLONG;
ret = 0;
cleanup:
virCgroupFree(&group);
if (vm)
virDomainObjUnlock(vm);
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.
@ -6248,9 +6393,9 @@ static virDriver qemuDriver = {
qemudDomainDetachDevice, /* domainDetachDevice */
qemudDomainGetAutostart, /* domainGetAutostart */
qemudDomainSetAutostart, /* domainSetAutostart */
NULL, /* domainGetSchedulerType */
NULL, /* domainGetSchedulerParameters */
NULL, /* domainSetSchedulerParameters */
qemuGetSchedulerType, /* domainGetSchedulerType */
qemuGetSchedulerParameters, /* domainGetSchedulerParameters */
qemuSetSchedulerParameters, /* domainSetSchedulerParameters */
NULL, /* domainMigratePrepare (v1) */
qemudDomainMigratePerform, /* domainMigratePerform */
NULL, /* domainMigrateFinish */

View File

@ -1479,6 +1479,26 @@ virStrToLong_ull(char const *s, char **end_ptr, int base, unsigned long long *re
return 0;
}
int
virStrToDouble(char const *s,
char **end_ptr,
double *result)
{
double val;
char *p;
int err;
errno = 0;
val = strtod(s, &p);
err = (errno || (!end_ptr && *p) || p == s);
if (end_ptr)
*end_ptr = p;
if (err)
return -1;
*result = val;
return 0;
}
/**
* virSkipSpaces:
* @str: pointer to the char pointer used

View File

@ -154,6 +154,9 @@ int virStrToLong_ull(char const *s,
char **end_ptr,
int base,
unsigned long long *result);
int virStrToDouble(char const *s,
char **end_ptr,
double *result);
int virMacAddrCompare (const char *mac1, const char *mac2);

View File

@ -1184,26 +1184,112 @@ static const vshCmdOptDef opts_schedinfo[] = {
{NULL, 0, 0, NULL}
};
static int
cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
virSchedParameterPtr param)
{
int found;
char *data;
/* Legacy 'weight' parameter */
if (STREQ(param->field, "weight") &&
param->type == VIR_DOMAIN_SCHED_FIELD_UINT &&
vshCommandOptBool(cmd, "weight")) {
int val;
val = vshCommandOptInt(cmd, "weight", &found);
if (!found) {
vshError(ctl, FALSE, "%s", _("Invalid value of weight"));
return -1;
} else {
param->value.ui = val;
}
return 1;
}
/* Legacy 'cap' parameter */
if (STREQ(param->field, "cap") &&
param->type == VIR_DOMAIN_SCHED_FIELD_UINT &&
vshCommandOptBool(cmd, "cap")) {
int val;
val = vshCommandOptInt(cmd, "cap", &found);
if (!found) {
vshError(ctl, FALSE, "%s", _("Invalid value of cap"));
return -1;
} else {
param->value.ui = val;
}
return 1;
}
if ((data = vshCommandOptString(cmd, "set", NULL))) {
char *val = strchr(data, '=');
int match = 0;
if (!val) {
vshError(ctl, FALSE, "%s", _("Invalid syntax for --set, expecting name=value"));
return -1;
}
*val = '\0';
match = STREQ(data, param->field);
*val = '=';
val++;
if (!match)
return 0;
switch (param->type) {
case VIR_DOMAIN_SCHED_FIELD_INT:
if (virStrToLong_i(val, NULL, 10, &param->value.i) < 0) {
vshError(ctl, FALSE, "%s",
_("Invalid value for parameter, expecting an int"));
return -1;
}
break;
case VIR_DOMAIN_SCHED_FIELD_UINT:
if (virStrToLong_ui(val, NULL, 10, &param->value.ui) < 0) {
vshError(ctl, FALSE, "%s",
_("Invalid value for parameter, expecting an unsigned int"));
return -1;
}
break;
case VIR_DOMAIN_SCHED_FIELD_LLONG:
if (virStrToLong_ll(val, NULL, 10, &param->value.l) < 0) {
vshError(ctl, FALSE, "%s",
_("Invalid value for parameter, expecting an long long"));
return -1;
}
break;
case VIR_DOMAIN_SCHED_FIELD_ULLONG:
if (virStrToLong_ull(val, NULL, 10, &param->value.ul) < 0) {
vshError(ctl, FALSE, "%s",
_("Invalid value for parameter, expecting an unsigned long long"));
return -1;
}
break;
case VIR_DOMAIN_SCHED_FIELD_DOUBLE:
if (virStrToDouble(val, NULL, &param->value.d) < 0) {
vshError(ctl, FALSE, "%s", _("Invalid value for parameter, expecting a double"));
return -1;
}
break;
case VIR_DOMAIN_SCHED_FIELD_BOOLEAN:
param->value.b = STREQ(val, "0") ? 0 : 1;
}
return 1;
}
return 0;
}
static int
cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
{
char *schedulertype;
char *set;
char *param_name = NULL;
long long int param_value = 0;
virDomainPtr dom;
virSchedParameterPtr params = NULL;
int i, ret;
int nparams = 0;
int nr_inputparams = 0;
int inputparams = 0;
int weightfound = 0;
int setfound = 0;
int weight = 0;
int capfound = 0;
int cap = 0;
char str_weight[] = "weight";
char str_cap[] = "cap";
int update = 0;
int i, ret;
int ret_val = FALSE;
if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
@ -1212,85 +1298,6 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
return FALSE;
/* Deprecated Xen-only options */
if(vshCommandOptBool(cmd, "weight")) {
weight = vshCommandOptInt(cmd, "weight", &weightfound);
if (!weightfound) {
vshError(ctl, FALSE, "%s", _("Invalid value of weight"));
goto cleanup;
} else {
nr_inputparams++;
}
}
if(vshCommandOptBool(cmd, "cap")) {
cap = vshCommandOptInt(cmd, "cap", &capfound);
if (!capfound) {
vshError(ctl, FALSE, "%s", _("Invalid value of cap"));
goto cleanup;
} else {
nr_inputparams++;
}
}
if(vshCommandOptBool(cmd, "set")) {
set = vshCommandOptString(cmd, "set", &setfound);
if (!setfound) {
vshError(ctl, FALSE, "%s", _("Error getting param"));
goto cleanup;
}
param_name = vshMalloc(ctl, strlen(set) + 1);
if (param_name == NULL)
goto cleanup;
if (sscanf(set, "%[^=]=%lli", param_name, &param_value) != 2) {
vshError(ctl, FALSE, "%s", _("Invalid value of param"));
goto cleanup;
}
nr_inputparams++;
}
params = vshMalloc(ctl, sizeof (virSchedParameter) * nr_inputparams);
if (params == NULL) {
goto cleanup;
}
if (weightfound) {
strncpy(params[inputparams].field,str_weight,sizeof(str_weight));
params[inputparams].type = VIR_DOMAIN_SCHED_FIELD_UINT;
params[inputparams].value.ui = weight;
inputparams++;
}
if (capfound) {
strncpy(params[inputparams].field,str_cap,sizeof(str_cap));
params[inputparams].type = VIR_DOMAIN_SCHED_FIELD_UINT;
params[inputparams].value.ui = cap;
inputparams++;
}
/* End Deprecated Xen-only options */
if (setfound) {
strncpy(params[inputparams].field,param_name,sizeof(params[0].field));
params[inputparams].type = VIR_DOMAIN_SCHED_FIELD_LLONG;
params[inputparams].value.l = param_value;
inputparams++;
}
assert (inputparams == nr_inputparams);
/* Set SchedulerParameters */
if (inputparams > 0) {
ret = virDomainSetSchedulerParameters(dom, params, inputparams);
if (ret == -1) {
goto cleanup;
}
}
free(params);
params = NULL;
/* Print SchedulerType */
schedulertype = virDomainGetSchedulerType(dom, &nparams);
if (schedulertype!= NULL){
@ -1302,21 +1309,38 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
/* Get SchedulerParameters */
params = vshMalloc(ctl, sizeof(virSchedParameter)* nparams);
if (params == NULL) {
goto cleanup;
}
for (i = 0; i < nparams; i++){
params[i].type = 0;
memset (params[i].field, 0, sizeof params[i].field);
}
ret = virDomainGetSchedulerParameters(dom, params, &nparams);
if (ret == -1) {
goto cleanup;
}
ret_val = TRUE;
if(nparams){
if (nparams) {
params = vshMalloc(ctl, sizeof(virSchedParameter)* nparams);
if (params == NULL)
goto cleanup;
memset(params, 0, sizeof(virSchedParameter)* nparams);
ret = virDomainGetSchedulerParameters(dom, params, &nparams);
if (ret == -1)
goto cleanup;
/* See if any params are being set */
for (i = 0; i < nparams; i++){
ret = cmdSchedInfoUpdate(ctl, cmd, &(params[i]));
if (ret == -1)
goto cleanup;
if (ret == 1)
update = 1;
}
/* Update parameters & refresh data */
if (update) {
ret = virDomainSetSchedulerParameters(dom, params, nparams);
if (ret == -1)
goto cleanup;
ret = virDomainGetSchedulerParameters(dom, params, &nparams);
if (ret == -1)
goto cleanup;
}
ret_val = TRUE;
for (i = 0; i < nparams; i++){
switch (params[i].type) {
case VIR_DOMAIN_SCHED_FIELD_INT:
@ -1326,10 +1350,10 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
printf("%-15s: %u\n", params[i].field, params[i].value.ui);
break;
case VIR_DOMAIN_SCHED_FIELD_LLONG:
printf("%-15s: %Ld\n", params[i].field, params[i].value.l);
printf("%-15s: %lld\n", params[i].field, params[i].value.l);
break;
case VIR_DOMAIN_SCHED_FIELD_ULLONG:
printf("%-15s: %Lu\n", params[i].field, params[i].value.ul);
printf("%-15s: %llu\n", params[i].field, params[i].value.ul);
break;
case VIR_DOMAIN_SCHED_FIELD_DOUBLE:
printf("%-15s: %f\n", params[i].field, params[i].value.d);
@ -1342,9 +1366,9 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
}
}
}
cleanup:
free(params);
free(param_name);
virDomainFree(dom);
return ret_val;
}