render / rpms / libvirt

Forked from rpms/libvirt 10 months ago
Clone
c401cc
From fc0e1e2d1d664df6400e0fef26fa386a467c82e7 Mon Sep 17 00:00:00 2001
c401cc
Message-Id: <fc0e1e2d1d664df6400e0fef26fa386a467c82e7.1390394206.git.jdenemar@redhat.com>
c401cc
From: Martin Kletzander <mkletzan@redhat.com>
c401cc
Date: Mon, 13 Jan 2014 12:09:22 +0100
c401cc
Subject: [PATCH] Fix crash in lxcDomainSetMemoryParameters
c401cc
c401cc
https://bugzilla.redhat.com/show_bug.cgi?id=1052062
c401cc
c401cc
The function doesn't check whether the request is made for active or
c401cc
inactive domain.  Thus when the domain is not running it still tries
c401cc
accessing non-existing cgroups (priv->cgroup, which is NULL).
c401cc
c401cc
I re-made the function in order for it to work the same way it's qemu
c401cc
counterpart does.
c401cc
c401cc
Reproducer:
c401cc
 1) Define an LXC domain
c401cc
 2) Do 'virsh memtune <domain> --hard-limit 133T'
c401cc
c401cc
Backtrace:
c401cc
 Thread 6 (Thread 0x7fffec8c0700 (LWP 26826)):
c401cc
 #0  0x00007ffff70edcc4 in virCgroupPathOfController (group=0x0, controller=3,
c401cc
     key=0x7ffff75734bd "memory.limit_in_bytes", path=0x7fffec8bf718) at util/vircgroup.c:1764
c401cc
 #1  0x00007ffff70e9206 in virCgroupSetValueStr (group=0x0, controller=3,
c401cc
     key=0x7ffff75734bd "memory.limit_in_bytes", value=0x7fffe409f360 "1073741824")
c401cc
     at util/vircgroup.c:669
c401cc
 #2  0x00007ffff70e98b4 in virCgroupSetValueU64 (group=0x0, controller=3,
c401cc
     key=0x7ffff75734bd "memory.limit_in_bytes", value=1073741824) at util/vircgroup.c:740
c401cc
 #3  0x00007ffff70ee518 in virCgroupSetMemory (group=0x0, kb=1048576) at util/vircgroup.c:1904
c401cc
 #4  0x00007ffff70ee675 in virCgroupSetMemoryHardLimit (group=0x0, kb=1048576)
c401cc
     at util/vircgroup.c:1944
c401cc
 #5  0x00005555557d54c8 in lxcDomainSetMemoryParameters (dom=0x7fffe40cc420,
c401cc
     params=0x7fffe409f100, nparams=1, flags=0) at lxc/lxc_driver.c:774
c401cc
 #6  0x00007ffff72c20f9 in virDomainSetMemoryParameters (domain=0x7fffe40cc420,
c401cc
     params=0x7fffe409f100, nparams=1, flags=0) at libvirt.c:4051
c401cc
 #7  0x000055555561365f in remoteDispatchDomainSetMemoryParameters (server=0x555555eb7e00,
c401cc
     client=0x555555ec4b10, msg=0x555555eb94e0, rerr=0x7fffec8bfb70, args=0x7fffe40b8510)
c401cc
     at remote_dispatch.h:7621
c401cc
 #8  0x00005555556133fd in remoteDispatchDomainSetMemoryParametersHelper (server=0x555555eb7e00,
c401cc
     client=0x555555ec4b10, msg=0x555555eb94e0, rerr=0x7fffec8bfb70, args=0x7fffe40b8510,
c401cc
     ret=0x7fffe40b84f0) at remote_dispatch.h:7591
c401cc
 #9  0x00007ffff73b293f in virNetServerProgramDispatchCall (prog=0x555555ec3ae0,
c401cc
     server=0x555555eb7e00, client=0x555555ec4b10, msg=0x555555eb94e0)
c401cc
     at rpc/virnetserverprogram.c:435
c401cc
 #10 0x00007ffff73b207f in virNetServerProgramDispatch (prog=0x555555ec3ae0,
c401cc
     server=0x555555eb7e00, client=0x555555ec4b10, msg=0x555555eb94e0)
c401cc
     at rpc/virnetserverprogram.c:305
c401cc
 #11 0x00007ffff73a4d2c in virNetServerProcessMsg (srv=0x555555eb7e00, client=0x555555ec4b10,
c401cc
     prog=0x555555ec3ae0, msg=0x555555eb94e0) at rpc/virnetserver.c:165
c401cc
 #12 0x00007ffff73a4e8d in virNetServerHandleJob (jobOpaque=0x555555ec3e30, opaque=0x555555eb7e00)
c401cc
     at rpc/virnetserver.c:186
c401cc
 #13 0x00007ffff7187f3f in virThreadPoolWorker (opaque=0x555555eb7ac0) at util/virthreadpool.c:144
c401cc
 #14 0x00007ffff718733a in virThreadHelper (data=0x555555eb7890) at util/virthreadpthread.c:161
c401cc
 #15 0x00007ffff468ed89 in start_thread (arg=0x7fffec8c0700) at pthread_create.c:308
c401cc
 #16 0x00007ffff3da26bd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113
c401cc
c401cc
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
c401cc
(cherry picked from commit 9faf3f2950aed1643ab7564afcb4c693c77f71b5)
c401cc
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
c401cc
---
c401cc
 src/lxc/lxc_driver.c | 112 +++++++++++++++++++++++++++++++++++++++++++--------
c401cc
 1 file changed, 96 insertions(+), 16 deletions(-)
c401cc
c401cc
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
c401cc
index 90d4878..5144137 100644
c401cc
--- a/src/lxc/lxc_driver.c
c401cc
+++ b/src/lxc/lxc_driver.c
c401cc
@@ -742,12 +742,24 @@ lxcDomainSetMemoryParameters(virDomainPtr dom,
c401cc
                              int nparams,
c401cc
                              unsigned int flags)
c401cc
 {
c401cc
-    size_t i;
c401cc
+    virCapsPtr caps = NULL;
c401cc
+    virDomainDefPtr vmdef = NULL;
c401cc
     virDomainObjPtr vm = NULL;
c401cc
+    virLXCDomainObjPrivatePtr priv = NULL;
c401cc
+    virLXCDriverConfigPtr cfg = NULL;
c401cc
+    virLXCDriverPtr driver = dom->conn->privateData;
c401cc
+    unsigned long long hard_limit;
c401cc
+    unsigned long long soft_limit;
c401cc
+    unsigned long long swap_hard_limit;
c401cc
+    bool set_hard_limit = false;
c401cc
+    bool set_soft_limit = false;
c401cc
+    bool set_swap_hard_limit = false;
c401cc
+    int rc;
c401cc
     int ret = -1;
c401cc
-    virLXCDomainObjPrivatePtr priv;
c401cc
 
c401cc
-    virCheckFlags(0, -1);
c401cc
+    virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
c401cc
+                  VIR_DOMAIN_AFFECT_CONFIG, -1);
c401cc
+
c401cc
     if (virTypedParamsValidate(params, nparams,
c401cc
                                VIR_DOMAIN_MEMORY_HARD_LIMIT,
c401cc
                                VIR_TYPED_PARAM_ULLONG,
c401cc
@@ -762,29 +774,97 @@ lxcDomainSetMemoryParameters(virDomainPtr dom,
c401cc
         goto cleanup;
c401cc
 
c401cc
     priv = vm->privateData;
c401cc
+    cfg = virLXCDriverGetConfig(driver);
c401cc
 
c401cc
-    if (virDomainSetMemoryParametersEnsureACL(dom->conn, vm->def, flags) < 0)
c401cc
+    if (virDomainSetMemoryParametersEnsureACL(dom->conn, vm->def, flags) < 0 ||
c401cc
+        !(caps = virLXCDriverGetCapabilities(driver, false)) ||
c401cc
+        virDomainLiveConfigHelperMethod(caps, driver->xmlopt,
c401cc
+                                        vm, &flags, &vmdef) < 0)
c401cc
         goto cleanup;
c401cc
 
c401cc
-    ret = 0;
c401cc
-    for (i = 0; i < nparams; i++) {
c401cc
-        virTypedParameterPtr param = &params[i];
c401cc
+    if (flags & VIR_DOMAIN_AFFECT_LIVE &&
c401cc
+        !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_MEMORY)) {
c401cc
+        virReportError(VIR_ERR_OPERATION_INVALID,
c401cc
+                       "%s", _("cgroup memory controller is not mounted"));
c401cc
+        goto cleanup;
c401cc
+    }
c401cc
+
c401cc
+#define VIR_GET_LIMIT_PARAMETER(PARAM, VALUE)                                \
c401cc
+    if ((rc = virTypedParamsGetULLong(params, nparams, PARAM, &VALUE)) < 0)  \
c401cc
+        goto cleanup;                                                        \
c401cc
+                                                                             \
c401cc
+    if (rc == 1)                                                             \
c401cc
+        set_ ## VALUE = true;
c401cc
+
c401cc
+    VIR_GET_LIMIT_PARAMETER(VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT, swap_hard_limit)
c401cc
+    VIR_GET_LIMIT_PARAMETER(VIR_DOMAIN_MEMORY_HARD_LIMIT, hard_limit)
c401cc
+    VIR_GET_LIMIT_PARAMETER(VIR_DOMAIN_MEMORY_SOFT_LIMIT, soft_limit)
c401cc
+
c401cc
+#undef VIR_GET_LIMIT_PARAMETER
c401cc
+
c401cc
+    /* Swap hard limit must be greater than hard limit.
c401cc
+     * Note that limit of 0 denotes unlimited */
c401cc
+    if (set_swap_hard_limit || set_hard_limit) {
c401cc
+        unsigned long long mem_limit = vm->def->mem.hard_limit;
c401cc
+        unsigned long long swap_limit = vm->def->mem.swap_hard_limit;
c401cc
+
c401cc
+        if (set_swap_hard_limit)
c401cc
+            swap_limit = swap_hard_limit;
c401cc
 
c401cc
-        if (STREQ(param->field, VIR_DOMAIN_MEMORY_HARD_LIMIT)) {
c401cc
-            if (virCgroupSetMemoryHardLimit(priv->cgroup, params[i].value.ul) < 0)
c401cc
-                ret = -1;
c401cc
-        } else if (STREQ(param->field, VIR_DOMAIN_MEMORY_SOFT_LIMIT)) {
c401cc
-            if (virCgroupSetMemorySoftLimit(priv->cgroup, params[i].value.ul) < 0)
c401cc
-                ret = -1;
c401cc
-        } else if (STREQ(param->field, VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT)) {
c401cc
-            if (virCgroupSetMemSwapHardLimit(priv->cgroup, params[i].value.ul) < 0)
c401cc
-                ret = -1;
c401cc
+        if (set_hard_limit)
c401cc
+            mem_limit = hard_limit;
c401cc
+
c401cc
+        if (virCompareLimitUlong(mem_limit, swap_limit) > 0) {
c401cc
+            virReportError(VIR_ERR_INVALID_ARG, "%s",
c401cc
+                           _("memory hard_limit tunable value must be lower "
c401cc
+                             "than or equal to swap_hard_limit"));
c401cc
+            goto cleanup;
c401cc
         }
c401cc
     }
c401cc
 
c401cc
+#define LXC_SET_MEM_PARAMETER(FUNC, VALUE)                                     \
c401cc
+    if (set_ ## VALUE) {                                                        \
c401cc
+        if (flags & VIR_DOMAIN_AFFECT_LIVE) {                                   \
c401cc
+            if ((rc = FUNC(priv->cgroup, VALUE)) < 0) {                         \
c401cc
+                virReportSystemError(-rc, _("unable to set memory %s tunable"), \
c401cc
+                                     #VALUE);                                   \
c401cc
+                                                                                \
c401cc
+                goto cleanup;                                                   \
c401cc
+            }                                                                   \
c401cc
+            vm->def->mem.VALUE = VALUE;                                         \
c401cc
+        }                                                                       \
c401cc
+                                                                                \
c401cc
+        if (flags & VIR_DOMAIN_AFFECT_CONFIG)                                   \
c401cc
+            vmdef->mem.VALUE = VALUE;                                   \
c401cc
+    }
c401cc
+
c401cc
+    /* Soft limit doesn't clash with the others */
c401cc
+    LXC_SET_MEM_PARAMETER(virCgroupSetMemorySoftLimit, soft_limit);
c401cc
+
c401cc
+    /* set hard limit before swap hard limit if decreasing it */
c401cc
+    if (virCompareLimitUlong(vm->def->mem.hard_limit, hard_limit) > 0) {
c401cc
+        LXC_SET_MEM_PARAMETER(virCgroupSetMemoryHardLimit, hard_limit);
c401cc
+        /* inhibit changing the limit a second time */
c401cc
+        set_hard_limit = false;
c401cc
+    }
c401cc
+
c401cc
+    LXC_SET_MEM_PARAMETER(virCgroupSetMemSwapHardLimit, swap_hard_limit);
c401cc
+
c401cc
+    /* otherwise increase it after swap hard limit */
c401cc
+    LXC_SET_MEM_PARAMETER(virCgroupSetMemoryHardLimit, hard_limit);
c401cc
+
c401cc
+#undef LXC_SET_MEM_PARAMETER
c401cc
+
c401cc
+    if (flags & VIR_DOMAIN_AFFECT_CONFIG &&
c401cc
+        virDomainSaveConfig(cfg->configDir, vmdef) < 0)
c401cc
+        goto cleanup;
c401cc
+
c401cc
+    ret = 0;
c401cc
 cleanup:
c401cc
     if (vm)
c401cc
         virObjectUnlock(vm);
c401cc
+    virObjectUnref(caps);
c401cc
+    virObjectUnref(cfg);
c401cc
     return ret;
c401cc
 }
c401cc
 
c401cc
-- 
c401cc
1.8.5.3
c401cc