[libvirt] [PATCH 6/8] Implement schedular tunables API using cgroups

Daniel P. Berrange berrange at redhat.com
Wed Jul 22 15:23:45 UTC 2009


* 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

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 src/cgroup.c             |   10 +-
 src/cgroup.h             |    4 +-
 src/libvirt_private.syms |    1 +
 src/lxc_driver.c         |    9 ++-
 src/qemu_driver.c        |  151 +++++++++++++++++++++++++++++-
 src/util.c               |   20 ++++
 src/util.h               |    3 +
 src/virsh.c              |  232 +++++++++++++++++++++++++---------------------
 8 files changed, 314 insertions(+), 116 deletions(-)

diff --git a/src/cgroup.c b/src/cgroup.c
index d3d45d2..1a80a20 100644
--- a/src/cgroup.c
+++ b/src/cgroup.c
@@ -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);
 }
diff --git a/src/cgroup.h b/src/cgroup.h
index dbb444b..f452e2d 100644
--- a/src/cgroup.h
+++ b/src/cgroup.h
@@ -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);
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 0e9c9f7..22fb083 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -348,6 +348,7 @@ virStrToLong_i;
 virStrToLong_ll;
 virStrToLong_ull;
 virStrToLong_ui;
+virStrToDouble;
 virFileLinkPointsTo;
 virFileResolveLink;
 saferead;
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
index ff35845..843b066 100644
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -1355,9 +1355,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,
@@ -1381,7 +1386,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)
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 39ad47e..6c8370c 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -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 */
diff --git a/src/util.c b/src/util.c
index 5261714..ee64b28 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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
diff --git a/src/util.h b/src/util.h
index e905c38..e761e83 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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);
 
diff --git a/src/virsh.c b/src/virsh.c
index fff73a1..db3d8de 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -1181,111 +1181,118 @@ static const vshCmdOptDef opts_schedinfo[] = {
 };
 
 static int
-cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
+cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
+                   virSchedParameterPtr param)
 {
-    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 ret_val = FALSE;
-
-    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
-        return FALSE;
-
-    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
-        return FALSE;
-
-    /* Deprecated Xen-only options */
-    if(vshCommandOptBool(cmd, "weight")) {
-        weight = vshCommandOptInt(cmd, "weight", &weightfound);
-        if (!weightfound) {
+    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"));
-            goto cleanup;
+            return -1;
         } else {
-            nr_inputparams++;
+            param->value.ui = val;
         }
+        return 1;
     }
 
-    if(vshCommandOptBool(cmd, "cap")) {
-        cap = vshCommandOptInt(cmd, "cap", &capfound);
-        if (!capfound) {
+    /* 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"));
-            goto cleanup;
+            return -1;
         } else {
-            nr_inputparams++;
+            param->value.ui = val;
         }
+        return 1;
     }
 
-    if(vshCommandOptBool(cmd, "set")) {
-        set = vshCommandOptString(cmd, "set", &setfound);
-        if (!setfound) {
-            vshError(ctl, FALSE, "%s", _("Error getting param"));
-            goto cleanup;
+    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++;
 
-        param_name = vshMalloc(ctl, strlen(set) + 1);
-        if (param_name == NULL)
-            goto cleanup;
+        if (!match)
+            return 0;
 
-        if (sscanf(set, "%[^=]=%lli", param_name, &param_value) != 2) {
-            vshError(ctl, FALSE, "%s", _("Invalid value of param"));
-            goto cleanup;
+        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;
         }
-
-        nr_inputparams++;
-    }
-
-    params = vshMalloc(ctl, sizeof (virSchedParameter) * nr_inputparams);
-    if (params == NULL) {
-        goto cleanup;
+        return 1;
     }
 
-    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++;
-    }
+    return 0;
+}
 
-    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++;
-    }
+static int
+cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
+{
+    char *schedulertype;
+    virDomainPtr dom;
+    virSchedParameterPtr params = NULL;
+    int nparams = 0;
+    int update = 0;
+    int i, ret;
+    int ret_val = FALSE;
 
-    assert (inputparams == nr_inputparams);
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
 
-    /* Set SchedulerParameters */
-    if (inputparams > 0) {
-        ret = virDomainSetSchedulerParameters(dom, params, inputparams);
-        if (ret == -1) {
-            goto cleanup;
-        }
-    }
-    free(params);
-    params = NULL;
+    if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+        return FALSE;
 
     /* Print SchedulerType */
     schedulertype = virDomainGetSchedulerType(dom, &nparams);
@@ -1298,21 +1305,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:
@@ -1322,10 +1346,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);
@@ -1338,9 +1362,9 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd)
             }
         }
     }
+
  cleanup:
     free(params);
-    free(param_name);
     virDomainFree(dom);
     return ret_val;
 }
-- 
1.6.2.5




More information about the libvir-list mailing list