]>
Commit | Line | Data |
---|---|---|
eefc66aa LP |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | ||
3 | #include "alloc-util.h" | |
4 | #include "cgroup-util.h" | |
5 | #include "limits-util.h" | |
0a970718 | 6 | #include "memory-util.h" |
eefc66aa LP |
7 | #include "parse-util.h" |
8 | #include "process-util.h" | |
9 | #include "procfs-util.h" | |
10 | #include "string-util.h" | |
11 | ||
12 | uint64_t physical_memory(void) { | |
13 | _cleanup_free_ char *root = NULL, *value = NULL; | |
14 | uint64_t mem, lim; | |
15 | size_t ps; | |
16 | long sc; | |
17 | int r; | |
18 | ||
19 | /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of | |
20 | * memory. | |
21 | * | |
22 | * In order to support containers nicely that have a configured memory limit we'll take the minimum of the | |
23 | * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ | |
24 | ||
25 | sc = sysconf(_SC_PHYS_PAGES); | |
26 | assert(sc > 0); | |
27 | ||
28 | ps = page_size(); | |
29 | mem = (uint64_t) sc * (uint64_t) ps; | |
30 | ||
31 | r = cg_get_root_path(&root); | |
32 | if (r < 0) { | |
33 | log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m"); | |
34 | return mem; | |
35 | } | |
36 | ||
37 | r = cg_all_unified(); | |
38 | if (r < 0) { | |
39 | log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m"); | |
40 | return mem; | |
41 | } | |
42 | if (r > 0) { | |
43 | r = cg_get_attribute("memory", root, "memory.max", &value); | |
44 | if (r < 0) { | |
45 | log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m"); | |
46 | return mem; | |
47 | } | |
48 | ||
49 | if (streq(value, "max")) | |
50 | return mem; | |
51 | } else { | |
52 | r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value); | |
53 | if (r < 0) { | |
54 | log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m"); | |
55 | return mem; | |
56 | } | |
57 | } | |
58 | ||
59 | r = safe_atou64(value, &lim); | |
60 | if (r < 0) { | |
61 | log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value); | |
62 | return mem; | |
63 | } | |
64 | if (lim == UINT64_MAX) | |
65 | return mem; | |
66 | ||
67 | /* Make sure the limit is a multiple of our own page size */ | |
68 | lim /= ps; | |
69 | lim *= ps; | |
70 | ||
71 | return MIN(mem, lim); | |
72 | } | |
73 | ||
74 | uint64_t physical_memory_scale(uint64_t v, uint64_t max) { | |
75 | uint64_t p, m, ps, r; | |
76 | ||
77 | assert(max > 0); | |
78 | ||
79 | /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success | |
80 | * the result is a multiple of the page size (rounds down). */ | |
81 | ||
82 | ps = page_size(); | |
83 | assert(ps > 0); | |
84 | ||
85 | p = physical_memory() / ps; | |
86 | assert(p > 0); | |
87 | ||
88 | m = p * v; | |
89 | if (m / p != v) | |
90 | return UINT64_MAX; | |
91 | ||
92 | m /= max; | |
93 | ||
94 | r = m * ps; | |
95 | if (r / ps != m) | |
96 | return UINT64_MAX; | |
97 | ||
98 | return r; | |
99 | } | |
100 | ||
101 | uint64_t system_tasks_max(void) { | |
102 | ||
103 | uint64_t a = TASKS_MAX, b = TASKS_MAX; | |
104 | _cleanup_free_ char *root = NULL; | |
105 | int r; | |
106 | ||
107 | /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this | |
108 | * limit: | |
109 | * | |
110 | * a) the maximum tasks value the kernel allows on this architecture | |
111 | * b) the cgroups pids_max attribute for the system | |
112 | * c) the kernel's configured maximum PID value | |
113 | * | |
114 | * And then pick the smallest of the three */ | |
115 | ||
116 | r = procfs_tasks_get_limit(&a); | |
117 | if (r < 0) | |
118 | log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m"); | |
119 | ||
120 | r = cg_get_root_path(&root); | |
121 | if (r < 0) | |
122 | log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); | |
123 | else { | |
124 | _cleanup_free_ char *value = NULL; | |
125 | ||
126 | r = cg_get_attribute("pids", root, "pids.max", &value); | |
127 | if (r < 0) | |
128 | log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); | |
129 | else if (!streq(value, "max")) { | |
130 | r = safe_atou64(value, &b); | |
131 | if (r < 0) | |
132 | log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); | |
133 | } | |
134 | } | |
135 | ||
136 | return MIN3(TASKS_MAX, | |
137 | a <= 0 ? TASKS_MAX : a, | |
138 | b <= 0 ? TASKS_MAX : b); | |
139 | } | |
140 | ||
141 | uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { | |
142 | uint64_t t, m; | |
143 | ||
144 | assert(max > 0); | |
145 | ||
146 | /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages | |
147 | * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ | |
148 | ||
149 | t = system_tasks_max(); | |
150 | assert(t > 0); | |
151 | ||
152 | m = t * v; | |
153 | if (m / t != v) /* overflow? */ | |
154 | return UINT64_MAX; | |
155 | ||
156 | return m / max; | |
157 | } |