From d9ab2bcf0591b496f1a4750c7ff790b33f9c7e59 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 8 Jun 2016 18:56:20 +0200 Subject: util: when determining the amount of memory on this system, take cgroup limit into account When determining the amount of RAM in the system, let's make sure we also read the root-level cgroup memory limit into account. This isn't particularly useful on the host, but in containers it makes sure that whatever memory the container got assigned is actually used for RAM size calculations. --- src/basic/util.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'src/basic/util.c') diff --git a/src/basic/util.c b/src/basic/util.c index f2f92fb3b7..88d58cd94a 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -36,6 +36,7 @@ #include "alloc-util.h" #include "build.h" +#include "cgroup-util.h" #include "def.h" #include "dirent-util.h" #include "fd-util.h" @@ -771,15 +772,37 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int } uint64_t physical_memory(void) { - long mem; + _cleanup_free_ char *root = NULL, *value = NULL; + uint64_t mem, lim; + size_t ps; + long sc; - /* We return this as uint64_t in case we are running as 32bit - * process on a 64bit kernel with huge amounts of memory */ + /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of + * memory. + * + * In order to support containers nicely that have a configured memory limit we'll take the minimum of the + * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ + + sc = sysconf(_SC_PHYS_PAGES); + assert(sc > 0); + + ps = page_size(); + mem = (uint64_t) sc * (uint64_t) ps; + + if (cg_get_root_path(&root) < 0) + return mem; + + if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value)) + return mem; + + if (safe_atou64(value, &lim) < 0) + return mem; - mem = sysconf(_SC_PHYS_PAGES); - assert(mem > 0); + /* Make sure the limit is a multiple of our own page size */ + lim /= ps; + lim *= ps; - return (uint64_t) mem * (uint64_t) page_size(); + return MIN(mem, lim); } int update_reboot_parameter_and_warn(const char *param) { -- cgit v1.2.3-54-g00ecf From d8cf2ac79b524d7784bccb428295ebc9c5e8548c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 8 Jun 2016 20:45:32 +0200 Subject: util: introduce physical_memory_scale() to unify how we scale by physical memory The various bits of code did the scaling all different, let's unify this, given that the code is not trivial. --- src/basic/util.c | 27 +++++++++++++++++++++++++++ src/basic/util.h | 1 + src/core/load-fragment.c | 2 +- src/login/logind-user.c | 2 +- src/login/logind.c | 2 +- src/test/test-util.c | 38 +++++++++++++++++++++++++++++++++++++- 6 files changed, 68 insertions(+), 4 deletions(-) (limited to 'src/basic/util.c') diff --git a/src/basic/util.c b/src/basic/util.c index 88d58cd94a..09d16697b7 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -805,6 +805,33 @@ uint64_t physical_memory(void) { return MIN(mem, lim); } +uint64_t physical_memory_scale(uint64_t v, uint64_t max) { + uint64_t p, m, ps, r; + + assert(max > 0); + + /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success + * the result is a multiple of the page size (rounds down). */ + + ps = page_size(); + assert(ps > 0); + + p = physical_memory() / ps; + assert(p > 0); + + m = p * v; + if (m / p != v) + return UINT64_MAX; + + m /= max; + + r = m * ps; + if (r / ps != m) + return UINT64_MAX; + + return r; +} + int update_reboot_parameter_and_warn(const char *param) { int r; diff --git a/src/basic/util.h b/src/basic/util.h index 9e6df19ef1..db105197e8 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -184,6 +184,7 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int * int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); uint64_t physical_memory(void); +uint64_t physical_memory_scale(uint64_t v, uint64_t max); int update_reboot_parameter_and_warn(const char *param); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 05852cc7e3..58d7275a96 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2821,7 +2821,7 @@ int config_parse_memory_limit( return 0; } } else - bytes = (((physical_memory() / page_size()) * (uint64_t) r) / 100) * page_size(); + bytes = physical_memory_scale(r, 100U); if (bytes < 1) { log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' too small. Ignoring.", rvalue); diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 6d0904f5ca..de44d369cf 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -853,7 +853,7 @@ int config_parse_tmpfs_size( /* First, try to parse as percentage */ r = parse_percent(rvalue); if (r > 0 && r < 100) - *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) r) / 100U)); + *sz = physical_memory_scale(r, 100U); else { uint64_t k; diff --git a/src/login/logind.c b/src/login/logind.c index caf149cfb7..d01dd110ea 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -61,7 +61,7 @@ static void manager_reset_config(Manager *m) { m->idle_action_usec = 30 * USEC_PER_MINUTE; m->idle_action = HANDLE_IGNORE; - m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */ + m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */ m->user_tasks_max = 12288; m->sessions_max = 8192; m->inhibitors_max = 8192; diff --git a/src/test/test-util.c b/src/test/test-util.c index 5b3fbcff53..e177612a9f 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -273,7 +273,42 @@ static void test_physical_memory(void) { assert_se(p < UINT64_MAX); assert_se(p % page_size() == 0); - log_info("Memory: %s", format_bytes(buf, sizeof(buf), p)); + log_info("Memory: %s (%" PRIu64 ")", format_bytes(buf, sizeof(buf), p), p); +} + +static void test_physical_memory_scale(void) { + uint64_t p; + + p = physical_memory(); + + assert_se(physical_memory_scale(0, 100) == 0); + assert_se(physical_memory_scale(100, 100) == p); + + log_info("Memory original: %" PRIu64, physical_memory()); + log_info("Memory scaled by 50%%: %" PRIu64, physical_memory_scale(50, 100)); + log_info("Memory divided by 2: %" PRIu64, physical_memory() / 2); + log_info("Page size: %zu", page_size()); + + /* There might be an uneven number of pages, hence permit these calculations to be half a page off... */ + assert_se(page_size()/2 + physical_memory_scale(50, 100) - p/2 <= page_size()); + assert_se(physical_memory_scale(200, 100) == p*2); + + assert_se(physical_memory_scale(0, 1) == 0); + assert_se(physical_memory_scale(1, 1) == p); + assert_se(physical_memory_scale(2, 1) == p*2); + + assert_se(physical_memory_scale(0, 2) == 0); + + assert_se(page_size()/2 + physical_memory_scale(1, 2) - p/2 <= page_size()); + assert_se(physical_memory_scale(2, 2) == p); + assert_se(physical_memory_scale(4, 2) == p*2); + + assert_se(physical_memory_scale(0, UINT32_MAX) == 0); + assert_se(physical_memory_scale(UINT32_MAX, UINT32_MAX) == p); + + /* overflow */ + assert_se(physical_memory_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX); + } int main(int argc, char *argv[]) { @@ -291,6 +326,7 @@ int main(int argc, char *argv[]) { test_execute_directory(); test_raw_clone(); test_physical_memory(); + test_physical_memory_scale(); return 0; } -- cgit v1.2.3-54-g00ecf