[libvirt] [PATCH] openvz: read vmguarpages/privvmpages to set memory tunables [v2]

Guido Günther agx at sigxcpu.org
Thu Apr 26 11:16:46 UTC 2012


---
This is a reworked version of the patch already sent, now adding
handling for "unlimited" (represented by LONG_MAX in openvz) as well as
a schema testcase. I also switched all value to unsigned long long to
avoid overflows.
Note that privvmpages is the amount of memory available to the
application. The total memory usage of the container might be higher due
to used kernel memory. This does probably warrant an extra tunable
later. O.k. to apply?
Cheers,
 -- Guido

 src/openvz/openvz_conf.c                        |  113 ++++++++++++
 src/openvz/openvz_driver.c                      |  208 +++++++++++++++++++++++
 tests/domainschemadata/domain-openvz-simple.xml |   27 +++
 3 files changed, 348 insertions(+)
 create mode 100644 tests/domainschemadata/domain-openvz-simple.xml

diff --git a/src/openvz/openvz_conf.c b/src/openvz/openvz_conf.c
index 5848ec4..a169ae6 100644
--- a/src/openvz/openvz_conf.c
+++ b/src/openvz/openvz_conf.c
@@ -129,6 +129,46 @@ int openvzExtractVersion(struct openvz_driver *driver)
 }
 
 
+/* Parse config values of the form barrier:limit into barrier and limit */
+static int
+openvzParseBarrierLimit(const char* value,
+                        unsigned long long *barrier,
+                        unsigned long long *limit)
+{
+    char *token;
+    char *saveptr = NULL;
+    char *str = strdup(value);
+
+    if (str == NULL) {
+        virReportOOMError();
+        goto error;
+    }
+
+    token = strtok_r(str, ":", &saveptr);
+    if (token == NULL) {
+        goto error;
+    } else {
+        if (barrier != NULL) {
+            if (virStrToLong_ull(token, NULL, 10, barrier))
+                goto error;
+        }
+    }
+    token = strtok_r(NULL, ":", &saveptr);
+    if (token == NULL) {
+        goto error;
+    } else {
+        if (limit != NULL) {
+            if (virStrToLong_ull(token, NULL, 10, limit))
+                goto error;
+        }
+    }
+    return 0;
+error:
+    VIR_FREE(str);
+    return -1;
+}
+
+
 static int openvzDefaultConsoleType(const char *ostype ATTRIBUTE_UNUSED)
 {
     return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_OPENVZ;
@@ -423,6 +463,78 @@ error:
 }
 
 
+static int
+openvzReadMemConf(virDomainDefPtr def, int veid)
+{
+    int ret;
+    char *temp = NULL;
+    unsigned long long barrier, limit;
+    const char *param;
+    unsigned long kb_per_pages;
+
+    kb_per_pages = sysconf(_SC_PAGESIZE) / 1024;
+    if (kb_per_pages == -1) {
+        openvzError(VIR_ERR_INTERNAL_ERROR,
+                    _("Can't determine page size"));
+        goto error;
+    }
+
+    /* Memory allocation guarantee */
+    param = "VMGUARPAGES";
+    ret = openvzReadVPSConfigParam(veid, param, &temp);
+    if (ret < 0) {
+        openvzError(VIR_ERR_INTERNAL_ERROR,
+                    _("Could not read '%s' from config for container %d"),
+                    param, veid);
+        goto error;
+    } else if (ret > 0) {
+        ret = openvzParseBarrierLimit(temp, &barrier, NULL);
+        if (ret < 0) {
+            openvzError(VIR_ERR_INTERNAL_ERROR,
+                        _("Could not parse  barrier of '%s' "
+                          "from config for container %d"), param, veid);
+            goto error;
+        }
+        if (barrier == LONG_MAX)
+            def->mem.min_guarantee = 0ull;
+        else
+            def->mem.min_guarantee = barrier * kb_per_pages;
+    }
+
+    /* Memory hard and soft limits */
+    param = "PRIVVMPAGES";
+    ret = openvzReadVPSConfigParam(veid, param, &temp);
+    if (ret < 0) {
+        openvzError(VIR_ERR_INTERNAL_ERROR,
+                    _("Could not read '%s' from config for container %d"),
+                    param, veid);
+        goto error;
+    } else if (ret > 0) {
+        ret = openvzParseBarrierLimit(temp, &barrier, &limit);
+        if (ret < 0) {
+            openvzError(VIR_ERR_INTERNAL_ERROR,
+                        _("Could not parse barrier and limit of '%s' "
+                          "from config for container %d"), param, veid);
+            goto error;
+        }
+        if (barrier == LONG_MAX)
+            def->mem.soft_limit = 0ull;
+        else
+            def->mem.soft_limit = barrier * kb_per_pages;
+
+        if (limit == LONG_MAX)
+            def->mem.hard_limit = 0ull;
+        else
+            def->mem.hard_limit = limit * kb_per_pages;
+    }
+
+    ret = 0;
+error:
+    VIR_FREE(temp);
+    return ret;
+}
+
+
 /* Free all memory associated with a openvz_driver structure */
 void
 openvzFreeDriver(struct openvz_driver *driver)
@@ -535,6 +647,7 @@ int openvzLoadDomains(struct openvz_driver *driver) {
 
         openvzReadNetworkConf(dom->def, veid);
         openvzReadFSConf(dom->def, veid);
+        openvzReadMemConf(dom->def, veid);
 
         virUUIDFormat(dom->def->uuid, uuidstr);
         if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0)
diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c
index e8b6915..555161e 100644
--- a/src/openvz/openvz_driver.c
+++ b/src/openvz/openvz_driver.c
@@ -54,6 +54,7 @@
 #include "nodeinfo.h"
 #include "memory.h"
 #include "virfile.h"
+#include "virtypedparam.h"
 #include "logging.h"
 #include "command.h"
 #include "viruri.h"
@@ -65,6 +66,8 @@
 #define CMDBUF_LEN 1488
 #define CMDOP_LEN 288
 
+#define OPENVZ_NB_MEM_PARAM 3
+
 static int openvzGetProcessInfo(unsigned long long *cpuTime, int vpsid);
 static int openvzGetMaxVCPUs(virConnectPtr conn, const char *type);
 static int openvzDomainGetMaxVcpus(virDomainPtr dom);
@@ -1631,6 +1634,209 @@ cleanup:
     return -1;
 }
 
+
+static int
+openvzDomainGetBarrierLimit(virDomainPtr domain,
+                            const char *param,
+                            unsigned long long *barrier,
+                            unsigned long long *limit)
+{
+    int status, ret = -1;
+    char *output = NULL;
+    virCommandPtr cmd = virCommandNewArgList(VZLIST, "--no-header", NULL);
+
+    virCommandSetOutputBuffer(cmd, &output);
+    virCommandAddArgFormat(cmd, "-o%s.b,%s.l", param, param);
+    virCommandAddArg(cmd, domain->name);
+    if (virCommandRun(cmd, &status)) {
+        openvzError(VIR_ERR_OPERATION_FAILED,
+                    _("Failed to get %s for %s: %d"), param, domain->name,
+                    status);
+        goto cleanup;
+    }
+
+    if (sscanf(output, "%llu %llu", barrier, limit) != 2) {
+        openvzError(VIR_ERR_INTERNAL_ERROR,
+                    _("Can't parse "VZLIST" output, got %s"), output);
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    VIR_FREE(output);
+    virCommandFree(cmd);
+    return ret;
+}
+
+
+static int
+openvzDomainSetBarrierLimit(virDomainPtr domain,
+                            const  char *param,
+                            unsigned long long barrier,
+                            unsigned long long limit)
+{
+    int status, ret = -1;
+    virCommandPtr cmd = virCommandNewArgList(VZCTL, "--quiet", "set", NULL);
+
+    /* LONG_MAX indicates unlimited so reject larger values */
+    if (barrier > LONG_MAX || limit > LONG_MAX) {
+        openvzError(VIR_ERR_OPERATION_FAILED,
+                    _("Failed to set %s for %s: value too large"), param,
+                    domain->name);
+        goto cleanup;
+    }
+
+    virCommandAddArg(cmd, domain->name);
+    virCommandAddArgFormat(cmd, "--%s", param);
+    virCommandAddArgFormat(cmd, "%llu:%llu", barrier, limit);
+    virCommandAddArg(cmd, "--save");
+    if (virCommandRun(cmd, &status)) {
+        openvzError(VIR_ERR_OPERATION_FAILED,
+                    _("Failed to set %s for %s: %d"), param, domain->name,
+                    status);
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    virCommandFree(cmd);
+    return ret;
+}
+
+
+static int
+openvzDomainGetMemoryParameters(virDomainPtr domain,
+                                virTypedParameterPtr params,
+                                int *nparams,
+                                unsigned int flags)
+{
+    int i, result = -1;
+    const char *name;
+    long kb_per_pages;
+    unsigned long long barrier, limit, val;
+
+    virCheckFlags(0, -1);
+
+    kb_per_pages = sysconf(_SC_PAGESIZE) / 1024;
+    if (kb_per_pages == -1) {
+        openvzError(VIR_ERR_INTERNAL_ERROR,
+                    _("Can't determine page size"));
+        goto cleanup;
+    }
+
+    if (*nparams == 0) {
+        *nparams = OPENVZ_NB_MEM_PARAM;
+        return 0;
+    }
+
+    for (i = 0; i <= *nparams; i++) {
+        virMemoryParameterPtr param = &params[i];
+
+        switch (i) {
+        case 0:
+            name = "privvmpages";
+            if (openvzDomainGetBarrierLimit(domain, name, &barrier, &limit) < 0)
+                goto cleanup;
+
+            val = (limit == LONG_MAX) ? 0ull : limit * kb_per_pages;
+            if (virTypedParameterAssign(param, VIR_DOMAIN_MEMORY_HARD_LIMIT,
+                                        VIR_TYPED_PARAM_ULLONG, val) < 0)
+                goto cleanup;
+            break;
+
+        case 1:
+            name = "privvmpages";
+            if (openvzDomainGetBarrierLimit(domain, name, &barrier, &limit) < 0)
+                goto cleanup;
+
+            val = (barrier == LONG_MAX) ? 0ull : barrier * kb_per_pages;
+            if (virTypedParameterAssign(param, VIR_DOMAIN_MEMORY_SOFT_LIMIT,
+                                        VIR_TYPED_PARAM_ULLONG, val) < 0)
+                goto cleanup;
+            break;
+
+        case 2:
+            name = "vmguarpages";
+            if (openvzDomainGetBarrierLimit(domain, name, &barrier, &limit) < 0)
+                goto cleanup;
+
+            val = (barrier == LONG_MAX) ? 0ull : barrier * kb_per_pages;
+            if (virTypedParameterAssign(param, VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
+                                        VIR_TYPED_PARAM_ULLONG, val) < 0)
+                goto cleanup;
+            break;
+        }
+    }
+
+    if (*nparams > OPENVZ_NB_MEM_PARAM)
+        *nparams = OPENVZ_NB_MEM_PARAM;
+    result = 0;
+
+cleanup:
+    return result;
+}
+
+
+static int
+openvzDomainSetMemoryParameters(virDomainPtr domain,
+                                virTypedParameterPtr params,
+                                int nparams,
+                                unsigned int flags)
+{
+    int i, result = -1;
+    long kb_per_pages;
+
+    kb_per_pages = sysconf(_SC_PAGESIZE) / 1024;
+    if (kb_per_pages == -1) {
+        openvzError(VIR_ERR_INTERNAL_ERROR,
+                    _("Can't determine page size"));
+        goto cleanup;
+    }
+
+    virCheckFlags(0, -1);
+    if (virTypedParameterArrayValidate(params, nparams,
+                                       VIR_DOMAIN_MEMORY_HARD_LIMIT,
+                                       VIR_TYPED_PARAM_ULLONG,
+                                       VIR_DOMAIN_MEMORY_SOFT_LIMIT,
+                                       VIR_TYPED_PARAM_ULLONG,
+                                       VIR_DOMAIN_MEMORY_MIN_GUARANTEE,
+                                       VIR_TYPED_PARAM_ULLONG,
+                                       NULL) < 0)
+        return -1;
+
+    for (i = 0; i < nparams; i++) {
+        virTypedParameterPtr param = &params[i];
+        unsigned long long barrier, limit;
+
+        if (STREQ(param->field, VIR_DOMAIN_MEMORY_HARD_LIMIT)) {
+            if (openvzDomainGetBarrierLimit(domain, "privvmpages",
+                                            &barrier, &limit) < 0)
+                goto cleanup;
+            limit = params[i].value.ul / kb_per_pages;
+            if (openvzDomainSetBarrierLimit(domain, "privvmpages",
+                                            barrier, limit) < 0)
+                goto cleanup;
+        } else if (STREQ(param->field, VIR_DOMAIN_MEMORY_SOFT_LIMIT)) {
+            if (openvzDomainGetBarrierLimit(domain, "privvmpages",
+                                            &barrier, &limit) < 0)
+                goto cleanup;
+            barrier = params[i].value.ul / kb_per_pages;
+            if (openvzDomainSetBarrierLimit(domain, "privvmpages",
+                                            barrier, limit) < 0)
+                goto cleanup;
+        } else if (STREQ(param->field, VIR_DOMAIN_MEMORY_MIN_GUARANTEE)) {
+            barrier = params[i].value.ul / kb_per_pages;
+            if (openvzDomainSetBarrierLimit(domain, "vmguarpages",
+                                            barrier, LONG_MAX) < 0)
+                goto cleanup;
+        }
+    }
+    result = 0;
+cleanup:
+    return result;
+}
+
+
 static int
 openvzGetVEStatus(virDomainObjPtr vm, int *status, int *reason)
 {
@@ -1752,6 +1958,8 @@ static virDriver openvzDriver = {
     .domainDestroy = openvzDomainShutdown, /* 0.3.1 */
     .domainDestroyFlags = openvzDomainShutdownFlags, /* 0.9.4 */
     .domainGetOSType = openvzGetOSType, /* 0.3.1 */
+    .domainGetMemoryParameters = openvzDomainGetMemoryParameters, /* 0.9.12 */
+    .domainSetMemoryParameters = openvzDomainSetMemoryParameters, /* 0.9.12 */
     .domainGetInfo = openvzDomainGetInfo, /* 0.3.1 */
     .domainGetState = openvzDomainGetState, /* 0.9.2 */
     .domainSetVcpus = openvzDomainSetVcpus, /* 0.4.6 */
diff --git a/tests/domainschemadata/domain-openvz-simple.xml b/tests/domainschemadata/domain-openvz-simple.xml
new file mode 100644
index 0000000..aba64a4
--- /dev/null
+++ b/tests/domainschemadata/domain-openvz-simple.xml
@@ -0,0 +1,27 @@
+<domain type='openvz'>
+  <name>100</name>
+  <uuid>7109d234-f5a8-30a6-5dd2-39ca85ce3958</uuid>
+  <memory unit='KiB'>0</memory>
+  <currentMemory unit='KiB'>0</currentMemory>
+  <memtune>
+    <hard_limit unit='KiB'>278528</hard_limit>
+    <soft_limit unit='KiB'>262144</soft_limit>
+    <min_guarantee unit='KiB'>135168</min_guarantee>
+  </memtune>
+  <vcpu>1</vcpu>
+  <os>
+    <type>exe</type>
+    <init>/sbin/init</init>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>destroy</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <filesystem type='template' accessmode='passthrough'>
+      <source name='debian'/>
+      <target dir='/'/>
+    </filesystem>
+  </devices>
+</domain>
+
-- 
1.7.10




More information about the libvir-list mailing list