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