]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/limits-util.c
tree-wide: "<n>bit" → "<n>-bit"
[thirdparty/systemd.git] / src / basic / limits-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "alloc-util.h"
6 #include "cgroup-util.h"
7 #include "limits-util.h"
8 #include "memory-util.h"
9 #include "parse-util.h"
10 #include "process-util.h"
11 #include "procfs-util.h"
12 #include "string-util.h"
13
14 uint64_t physical_memory(void) {
15 _cleanup_free_ char *root = NULL, *value = NULL;
16 uint64_t mem, lim;
17 size_t ps;
18 long sc;
19 int r;
20
21 /* We return this as uint64_t in case we are running as 32-bit process on a 64-bit kernel with huge amounts of
22 * memory.
23 *
24 * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
25 * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
26
27 sc = sysconf(_SC_PHYS_PAGES);
28 assert(sc > 0);
29
30 ps = page_size();
31 mem = (uint64_t) sc * (uint64_t) ps;
32
33 r = cg_get_root_path(&root);
34 if (r < 0) {
35 log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m");
36 return mem;
37 }
38
39 r = cg_all_unified();
40 if (r < 0) {
41 log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m");
42 return mem;
43 }
44 if (r > 0) {
45 r = cg_get_attribute("memory", root, "memory.max", &value);
46 if (r == -ENOENT) /* Field does not exist on the system's top-level cgroup, hence don't
47 * complain. (Note that it might exist on our own root though, if we live
48 * in a cgroup namespace, hence check anyway instead of not even
49 * trying.) */
50 return mem;
51 if (r < 0) {
52 log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m");
53 return mem;
54 }
55
56 if (streq(value, "max"))
57 return mem;
58 } else {
59 r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value);
60 if (r < 0) {
61 log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m");
62 return mem;
63 }
64 }
65
66 r = safe_atou64(value, &lim);
67 if (r < 0) {
68 log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value);
69 return mem;
70 }
71 if (lim == UINT64_MAX)
72 return mem;
73
74 /* Make sure the limit is a multiple of our own page size */
75 lim /= ps;
76 lim *= ps;
77
78 return MIN(mem, lim);
79 }
80
81 uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
82 uint64_t p, m, ps;
83
84 /* Shortcut two special cases */
85 if (v == 0)
86 return 0;
87 if (v == max)
88 return physical_memory();
89
90 assert(max > 0);
91
92 /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
93 * the result is a multiple of the page size (rounds down). */
94
95 ps = page_size();
96 assert(ps > 0);
97
98 p = physical_memory() / ps;
99 assert(p > 0);
100
101 if (v > UINT64_MAX / p)
102 return UINT64_MAX;
103
104 m = p * v;
105 m /= max;
106
107 if (m > UINT64_MAX / ps)
108 return UINT64_MAX;
109
110 return m * ps;
111 }
112
113 uint64_t system_tasks_max(void) {
114 uint64_t a = TASKS_MAX, b = TASKS_MAX, c = TASKS_MAX;
115 _cleanup_free_ char *root = NULL;
116 int r;
117
118 /* Determine the maximum number of tasks that may run on this system. We check three sources to
119 * determine this limit:
120 *
121 * a) kernel.threads-max sysctl: the maximum number of tasks (threads) the kernel allows.
122 *
123 * This puts a direct limit on the number of concurrent tasks.
124 *
125 * b) kernel.pid_max sysctl: the maximum PID value.
126 *
127 * This limits the numeric range PIDs can take, and thus indirectly also limits the number of
128 * concurrent threads. It's primarily a compatibility concept: some crappy old code used a signed
129 * 16-bit type for PIDs, hence the kernel provides a way to ensure the PIDs never go beyond
130 * INT16_MAX by default.
131 *
132 * Also note the weird definition: PIDs assigned will be kept below this value, which means
133 * the number of tasks that can be created is one lower, as PID 0 is not a valid process ID.
134 *
135 * c) pids.max on the root cgroup: the kernel's configured maximum number of tasks.
136 *
137 * and then pick the smallest of the three.
138 *
139 * By default pid_max is set to much lower values than threads-max, hence the limit people come into
140 * contact with first, as it's the lowest boundary they need to bump when they want higher number of
141 * processes.
142 */
143
144 r = procfs_get_threads_max(&a);
145 if (r < 0)
146 log_debug_errno(r, "Failed to read kernel.threads-max, ignoring: %m");
147
148 r = procfs_get_pid_max(&b);
149 if (r < 0)
150 log_debug_errno(r, "Failed to read kernel.pid_max, ignoring: %m");
151 else if (b > 0)
152 /* Subtract one from pid_max, since PID 0 is not a valid PID */
153 b--;
154
155 r = cg_get_root_path(&root);
156 if (r < 0)
157 log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m");
158 else {
159 r = cg_get_attribute_as_uint64("pids", root, "pids.max", &c);
160 if (r < 0)
161 log_debug_errno(r, "Failed to read pids.max attribute of root cgroup, ignoring: %m");
162 }
163
164 return MIN3(a, b, c);
165 }
166
167 uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
168 uint64_t t, m;
169
170 /* Shortcut two special cases */
171 if (v == 0)
172 return 0;
173 if (v == max)
174 return system_tasks_max();
175
176 assert(max > 0);
177
178 /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
179 * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
180
181 t = system_tasks_max();
182 assert(t > 0);
183
184 if (v > UINT64_MAX / t) /* overflow? */
185 return UINT64_MAX;
186
187 m = t * v;
188 return m / max;
189 }