Branch data Line data Source code
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 : 152 : uint64_t physical_memory(void) {
13 : 152 : _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 : 152 : sc = sysconf(_SC_PHYS_PAGES);
26 [ - + ]: 152 : assert(sc > 0);
27 : :
28 : 152 : ps = page_size();
29 : 152 : mem = (uint64_t) sc * (uint64_t) ps;
30 : :
31 : 152 : r = cg_get_root_path(&root);
32 [ - + ]: 152 : if (r < 0) {
33 [ # # ]: 0 : log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m");
34 : 0 : return mem;
35 : : }
36 : :
37 : 152 : r = cg_all_unified();
38 [ - + ]: 152 : 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 : : }
42 [ - + ]: 152 : 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 : : }
48 : :
49 [ # # ]: 0 : if (streq(value, "max"))
50 : 0 : return mem;
51 : : } else {
52 : 152 : r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value);
53 [ - + ]: 152 : if (r < 0) {
54 [ # # ]: 0 : log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m");
55 : 0 : return mem;
56 : : }
57 : : }
58 : :
59 : 152 : r = safe_atou64(value, &lim);
60 [ - + ]: 152 : if (r < 0) {
61 [ # # ]: 0 : log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value);
62 : 0 : return mem;
63 : : }
64 [ - + ]: 152 : if (lim == UINT64_MAX)
65 : 0 : return mem;
66 : :
67 : : /* Make sure the limit is a multiple of our own page size */
68 : 152 : lim /= ps;
69 : 152 : lim *= ps;
70 : :
71 : 152 : return MIN(mem, lim);
72 : : }
73 : :
74 : 60 : uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
75 : : uint64_t p, m, ps, r;
76 : :
77 [ - + ]: 60 : 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 : 60 : ps = page_size();
83 [ - + ]: 60 : assert(ps > 0);
84 : :
85 : 60 : p = physical_memory() / ps;
86 [ - + ]: 60 : assert(p > 0);
87 : :
88 : 60 : m = p * v;
89 [ + + ]: 60 : if (m / p != v)
90 : 4 : return UINT64_MAX;
91 : :
92 : 56 : m /= max;
93 : :
94 : 56 : r = m * ps;
95 [ - + ]: 56 : if (r / ps != m)
96 : 0 : return UINT64_MAX;
97 : :
98 : 56 : return r;
99 : : }
100 : :
101 : 64 : uint64_t system_tasks_max(void) {
102 : :
103 : 64 : uint64_t a = TASKS_MAX, b = TASKS_MAX;
104 : 128 : _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 : 64 : r = procfs_tasks_get_limit(&a);
117 [ - + ]: 64 : if (r < 0)
118 [ # # ]: 0 : log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m");
119 : :
120 : 64 : r = cg_get_root_path(&root);
121 [ - + ]: 64 : if (r < 0)
122 [ # # ]: 0 : log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m");
123 : : else {
124 : 64 : _cleanup_free_ char *value = NULL;
125 : :
126 : 64 : r = cg_get_attribute("pids", root, "pids.max", &value);
127 [ + - ]: 64 : if (r < 0)
128 [ - + ]: 64 : log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m");
129 [ # # ]: 0 : 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 : : }
134 : : }
135 : :
136 [ + - + - ]: 64 : return MIN3(TASKS_MAX,
137 : : a <= 0 ? TASKS_MAX : a,
138 : : b <= 0 ? TASKS_MAX : b);
139 : : }
140 : :
141 : 56 : uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
142 : : uint64_t t, m;
143 : :
144 [ - + ]: 56 : 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 : 56 : t = system_tasks_max();
150 [ - + ]: 56 : assert(t > 0);
151 : :
152 : 56 : m = t * v;
153 [ + + ]: 56 : if (m / t != v) /* overflow? */
154 : 4 : return UINT64_MAX;
155 : :
156 : 52 : return m / max;
157 : : }
|