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 
physical_memory(void)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 32bit process on a 64bit 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 
physical_memory_scale(uint64_t v,uint64_t max)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 
system_tasks_max(void)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          *    16bit 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 
system_tasks_max_scale(uint64_t v,uint64_t max)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 }
190