util: introduce physical_memory_scale() to unify how we scale by physical memory
authorLennart Poettering <lennart@poettering.net>
Wed, 8 Jun 2016 18:45:32 +0000 (20:45 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 14 Jun 2016 18:01:45 +0000 (20:01 +0200)
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
src/basic/util.h
src/core/load-fragment.c
src/login/logind-user.c
src/login/logind.c
src/test/test-util.c

index 88d58cd..09d1669 100644 (file)
@@ -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;
 
index 9e6df19..db10519 100644 (file)
@@ -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);
 
index 05852cc..58d7275 100644 (file)
@@ -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);
index 6d0904f..de44d36 100644 (file)
@@ -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;
 
index caf149c..d01dd11 100644 (file)
@@ -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;
index 5b3fbcf..e177612 100644 (file)
@@ -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;
 }