]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
61ff7397 AZ |
2 | |
3 | #include <unistd.h> | |
4 | ||
5 | #include "alloc-util.h" | |
6 | #include "cgroup-setup.h" | |
7 | #include "cgroup-util.h" | |
282696ce | 8 | #include "fd-util.h" |
61ff7397 AZ |
9 | #include "fileio.h" |
10 | #include "fs-util.h" | |
11 | #include "oomd-util.h" | |
12 | #include "parse-util.h" | |
13 | #include "path-util.h" | |
14 | #include "string-util.h" | |
15 | #include "strv.h" | |
16 | #include "tests.h" | |
282696ce | 17 | #include "tmpfile-util.h" |
61ff7397 AZ |
18 | |
19 | static int fork_and_sleep(unsigned sleep_min) { | |
20 | usec_t n, timeout, ts; | |
21 | ||
22 | pid_t pid = fork(); | |
23 | assert_se(pid >= 0); | |
24 | ||
25 | if (pid == 0) { | |
26 | timeout = sleep_min * USEC_PER_MINUTE; | |
27 | ts = now(CLOCK_MONOTONIC); | |
2940b128 | 28 | for (;;) { |
61ff7397 AZ |
29 | n = now(CLOCK_MONOTONIC); |
30 | if (ts + timeout < n) { | |
31 | log_error("Child timed out waiting to be killed"); | |
32 | abort(); | |
33 | } | |
34 | sleep(1); | |
35 | } | |
36 | } | |
37 | ||
38 | return pid; | |
39 | } | |
40 | ||
41 | static void test_oomd_cgroup_kill(void) { | |
42 | _cleanup_free_ char *cgroup_root = NULL, *cgroup = NULL; | |
43 | int pid[2]; | |
3e9b4f91 | 44 | int r; |
61ff7397 AZ |
45 | |
46 | if (geteuid() != 0) | |
47 | return (void) log_tests_skipped("not root"); | |
48 | ||
49 | if (cg_all_unified() <= 0) | |
50 | return (void) log_tests_skipped("cgroups are not running in unified mode"); | |
51 | ||
52 | assert_se(cg_pid_get_path(NULL, 0, &cgroup_root) >= 0); | |
53 | ||
54 | /* Create another cgroup below this one for the pids we forked off. We need this to be managed | |
55 | * by the test so that pid1 doesn't delete it before we can read the xattrs. */ | |
56 | cgroup = path_join(cgroup_root, "oomdkilltest"); | |
f21b863e | 57 | assert_se(cgroup); |
3e9b4f91 | 58 | assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, cgroup) >= 0); |
61ff7397 AZ |
59 | |
60 | /* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities */ | |
3e9b4f91 | 61 | r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_test", "test", 4, 0); |
018b6f45 | 62 | if (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r)) |
3e9b4f91 | 63 | return (void) log_tests_skipped("Cannot set user xattrs"); |
61ff7397 AZ |
64 | |
65 | /* Do this twice to also check the increment behavior on the xattrs */ | |
66 | for (int i = 0; i < 2; i++) { | |
67 | _cleanup_free_ char *v = NULL; | |
61ff7397 AZ |
68 | |
69 | for (int j = 0; j < 2; j++) { | |
70 | pid[j] = fork_and_sleep(5); | |
3e9b4f91 | 71 | assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, cgroup, pid[j]) >= 0); |
61ff7397 AZ |
72 | } |
73 | ||
74 | r = oomd_cgroup_kill(cgroup, false /* recurse */, false /* dry run */); | |
75 | if (r <= 0) { | |
76 | log_debug_errno(r, "Failed to kill processes under %s: %m", cgroup); | |
77 | abort(); | |
78 | } | |
79 | ||
c0ebfef3 NK |
80 | assert_se(cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_ooms", &v) >= 0); |
81 | assert_se(streq(v, i == 0 ? "1" : "2")); | |
82 | v = mfree(v); | |
83 | ||
61ff7397 AZ |
84 | /* Wait a bit since processes may take some time to be cleaned up. */ |
85 | sleep(2); | |
86 | assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) == true); | |
87 | ||
e3038333 | 88 | assert_se(cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_kill", &v) >= 0); |
c0ebfef3 | 89 | assert_se(streq(v, i == 0 ? "2" : "4")); |
61ff7397 AZ |
90 | } |
91 | } | |
92 | ||
93 | static void test_oomd_cgroup_context_acquire_and_insert(void) { | |
94 | _cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL; | |
95 | _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL; | |
96 | _cleanup_free_ char *cgroup = NULL; | |
97 | OomdCGroupContext *c1, *c2; | |
8b2e2257 | 98 | CGroupMask mask; |
61ff7397 AZ |
99 | |
100 | if (geteuid() != 0) | |
101 | return (void) log_tests_skipped("not root"); | |
102 | ||
103 | if (!is_pressure_supported()) | |
104 | return (void) log_tests_skipped("system does not support pressure"); | |
105 | ||
106 | if (cg_all_unified() <= 0) | |
107 | return (void) log_tests_skipped("cgroups are not running in unified mode"); | |
108 | ||
8b2e2257 YW |
109 | assert_se(cg_mask_supported(&mask) >= 0); |
110 | ||
111 | if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY)) | |
112 | return (void) log_tests_skipped("cgroup memory controller is not available"); | |
113 | ||
61ff7397 | 114 | assert_se(cg_pid_get_path(NULL, 0, &cgroup) >= 0); |
61ff7397 AZ |
115 | assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0); |
116 | ||
117 | assert_se(streq(ctx->path, cgroup)); | |
61ff7397 AZ |
118 | assert_se(ctx->current_memory_usage > 0); |
119 | assert_se(ctx->memory_min == 0); | |
120 | assert_se(ctx->memory_low == 0); | |
121 | assert_se(ctx->swap_usage == 0); | |
122 | assert_se(ctx->last_pgscan == 0); | |
123 | assert_se(ctx->pgscan == 0); | |
59331b8e AZ |
124 | ctx = oomd_cgroup_context_free(ctx); |
125 | ||
61ff7397 AZ |
126 | assert_se(oomd_cgroup_context_acquire("", &ctx) == 0); |
127 | assert_se(streq(ctx->path, "/")); | |
128 | assert_se(ctx->current_memory_usage > 0); | |
129 | ||
130 | /* Test hashmap inserts */ | |
131 | assert_se(h1 = hashmap_new(&oomd_cgroup_ctx_hash_ops)); | |
132 | assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == 0); | |
133 | c1 = hashmap_get(h1, cgroup); | |
134 | assert_se(c1); | |
399d80ba | 135 | assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == -EEXIST); |
61ff7397 AZ |
136 | |
137 | /* make sure certain values from h1 get updated in h2 */ | |
df637ede | 138 | c1->pgscan = UINT64_MAX; |
61ff7397 | 139 | c1->mem_pressure_limit = 6789; |
69c8f025 | 140 | c1->mem_pressure_limit_hit_start = 42; |
df637ede | 141 | c1->last_had_mem_reclaim = 888; |
61ff7397 AZ |
142 | assert_se(h2 = hashmap_new(&oomd_cgroup_ctx_hash_ops)); |
143 | assert_se(oomd_insert_cgroup_context(h1, h2, cgroup) == 0); | |
144 | c1 = hashmap_get(h1, cgroup); | |
145 | c2 = hashmap_get(h2, cgroup); | |
146 | assert_se(c1); | |
147 | assert_se(c2); | |
148 | assert_se(c1 != c2); | |
df637ede | 149 | assert_se(c2->last_pgscan == UINT64_MAX); |
61ff7397 | 150 | assert_se(c2->mem_pressure_limit == 6789); |
69c8f025 | 151 | assert_se(c2->mem_pressure_limit_hit_start == 42); |
df637ede | 152 | assert_se(c2->last_had_mem_reclaim == 888); /* assumes the live pgscan is less than UINT64_MAX */ |
61ff7397 AZ |
153 | } |
154 | ||
b037a6da AZ |
155 | static void test_oomd_update_cgroup_contexts_between_hashmaps(void) { |
156 | _cleanup_hashmap_free_ Hashmap *h_old = NULL, *h_new = NULL; | |
157 | OomdCGroupContext *c_old, *c_new; | |
158 | char **paths = STRV_MAKE("/0.slice", | |
159 | "/1.slice"); | |
160 | ||
df637ede | 161 | OomdCGroupContext ctx_old[2] = { |
b037a6da AZ |
162 | { .path = paths[0], |
163 | .mem_pressure_limit = 5, | |
69c8f025 | 164 | .mem_pressure_limit_hit_start = 777, |
df637ede | 165 | .last_had_mem_reclaim = 888, |
b037a6da AZ |
166 | .pgscan = 57 }, |
167 | { .path = paths[1], | |
168 | .mem_pressure_limit = 6, | |
69c8f025 | 169 | .mem_pressure_limit_hit_start = 888, |
df637ede | 170 | .last_had_mem_reclaim = 888, |
b037a6da AZ |
171 | .pgscan = 42 }, |
172 | }; | |
173 | ||
df637ede | 174 | OomdCGroupContext ctx_new[2] = { |
b037a6da | 175 | { .path = paths[0], |
df637ede | 176 | .pgscan = 57 }, |
b037a6da AZ |
177 | { .path = paths[1], |
178 | .pgscan = 101 }, | |
179 | }; | |
180 | ||
181 | assert_se(h_old = hashmap_new(&string_hash_ops)); | |
182 | assert_se(hashmap_put(h_old, paths[0], &ctx_old[0]) >= 0); | |
183 | assert_se(hashmap_put(h_old, paths[1], &ctx_old[1]) >= 0); | |
184 | ||
185 | assert_se(h_new = hashmap_new(&string_hash_ops)); | |
186 | assert_se(hashmap_put(h_new, paths[0], &ctx_new[0]) >= 0); | |
187 | assert_se(hashmap_put(h_new, paths[1], &ctx_new[1]) >= 0); | |
188 | ||
189 | oomd_update_cgroup_contexts_between_hashmaps(h_old, h_new); | |
190 | ||
191 | assert_se(c_old = hashmap_get(h_old, "/0.slice")); | |
192 | assert_se(c_new = hashmap_get(h_new, "/0.slice")); | |
193 | assert_se(c_old->pgscan == c_new->last_pgscan); | |
194 | assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit); | |
69c8f025 | 195 | assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start); |
df637ede | 196 | assert_se(c_old->last_had_mem_reclaim == c_new->last_had_mem_reclaim); |
b037a6da AZ |
197 | |
198 | assert_se(c_old = hashmap_get(h_old, "/1.slice")); | |
199 | assert_se(c_new = hashmap_get(h_new, "/1.slice")); | |
200 | assert_se(c_old->pgscan == c_new->last_pgscan); | |
201 | assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit); | |
69c8f025 | 202 | assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start); |
df637ede | 203 | assert_se(c_new->last_had_mem_reclaim > c_old->last_had_mem_reclaim); |
b037a6da AZ |
204 | } |
205 | ||
61ff7397 AZ |
206 | static void test_oomd_system_context_acquire(void) { |
207 | _cleanup_(unlink_tempfilep) char path[] = "/oomdgetsysctxtestXXXXXX"; | |
254d1313 | 208 | _cleanup_close_ int fd = -EBADF; |
61ff7397 AZ |
209 | OomdSystemContext ctx; |
210 | ||
211 | if (geteuid() != 0) | |
212 | return (void) log_tests_skipped("not root"); | |
213 | ||
282696ce | 214 | assert_se((fd = mkostemp_safe(path)) >= 0); |
61ff7397 AZ |
215 | |
216 | assert_se(oomd_system_context_acquire("/verylikelynonexistentpath", &ctx) == -ENOENT); | |
217 | ||
47136b9d | 218 | assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL); |
61ff7397 AZ |
219 | |
220 | assert_se(write_string_file(path, "some\nwords\nacross\nmultiple\nlines", WRITE_STRING_FILE_CREATE) == 0); | |
47136b9d AZ |
221 | assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL); |
222 | ||
223 | assert_se(write_string_file(path, "MemTotal: 32495256 kB trailing\n" | |
224 | "MemFree: 9880512 kB data\n" | |
225 | "SwapTotal: 8388604 kB is\n" | |
226 | "SwapFree: 7604 kB bad\n", WRITE_STRING_FILE_CREATE) == 0); | |
227 | assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL); | |
228 | ||
47136b9d AZ |
229 | assert_se(write_string_file(path, "MemTotal: 32495256 kB\n" |
230 | "MemFree: 9880512 kB\n" | |
231 | "MemAvailable: 21777088 kB\n" | |
232 | "Buffers: 5968 kB\n" | |
233 | "Cached: 14344796 kB\n" | |
234 | "Unevictable: 740004 kB\n" | |
235 | "Mlocked: 4484 kB\n" | |
236 | "SwapTotal: 8388604 kB\n" | |
237 | "SwapFree: 7604 kB\n", WRITE_STRING_FILE_CREATE) == 0); | |
61ff7397 | 238 | assert_se(oomd_system_context_acquire(path, &ctx) == 0); |
eeeaa422 | 239 | assert_se(ctx.mem_total == 33275142144); |
030bc91c | 240 | assert_se(ctx.mem_used == 10975404032); |
47136b9d AZ |
241 | assert_se(ctx.swap_total == 8589930496); |
242 | assert_se(ctx.swap_used == 8582144000); | |
61ff7397 AZ |
243 | } |
244 | ||
245 | static void test_oomd_pressure_above(void) { | |
246 | _cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL; | |
247 | _cleanup_set_free_ Set *t1 = NULL, *t2 = NULL, *t3 = NULL; | |
a6d6a51d | 248 | OomdCGroupContext ctx[2] = {}, *c; |
61ff7397 AZ |
249 | loadavg_t threshold; |
250 | ||
251 | assert_se(store_loadavg_fixed_point(80, 0, &threshold) == 0); | |
252 | ||
253 | /* /herp.slice */ | |
254 | assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg10)) == 0); | |
255 | assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg60)) == 0); | |
256 | assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg300)) == 0); | |
257 | ctx[0].mem_pressure_limit = threshold; | |
258 | ||
259 | /* /derp.slice */ | |
260 | assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg10)) == 0); | |
261 | assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg60)) == 0); | |
262 | assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg300)) == 0); | |
263 | ctx[1].mem_pressure_limit = threshold; | |
264 | ||
61ff7397 AZ |
265 | /* High memory pressure */ |
266 | assert_se(h1 = hashmap_new(&string_hash_ops)); | |
267 | assert_se(hashmap_put(h1, "/herp.slice", &ctx[0]) >= 0); | |
268 | assert_se(oomd_pressure_above(h1, 0 /* duration */, &t1) == 1); | |
d9fe39b2 | 269 | assert_se(set_contains(t1, &ctx[0])); |
61ff7397 | 270 | assert_se(c = hashmap_get(h1, "/herp.slice")); |
69c8f025 | 271 | assert_se(c->mem_pressure_limit_hit_start > 0); |
61ff7397 AZ |
272 | |
273 | /* Low memory pressure */ | |
274 | assert_se(h2 = hashmap_new(&string_hash_ops)); | |
275 | assert_se(hashmap_put(h2, "/derp.slice", &ctx[1]) >= 0); | |
276 | assert_se(oomd_pressure_above(h2, 0 /* duration */, &t2) == 0); | |
d9fe39b2 | 277 | assert_se(!t2); |
61ff7397 | 278 | assert_se(c = hashmap_get(h2, "/derp.slice")); |
69c8f025 | 279 | assert_se(c->mem_pressure_limit_hit_start == 0); |
61ff7397 AZ |
280 | |
281 | /* High memory pressure w/ multiple cgroups */ | |
282 | assert_se(hashmap_put(h1, "/derp.slice", &ctx[1]) >= 0); | |
283 | assert_se(oomd_pressure_above(h1, 0 /* duration */, &t3) == 1); | |
d9fe39b2 | 284 | assert_se(set_contains(t3, &ctx[0])); |
61ff7397 AZ |
285 | assert_se(set_size(t3) == 1); |
286 | assert_se(c = hashmap_get(h1, "/herp.slice")); | |
69c8f025 | 287 | assert_se(c->mem_pressure_limit_hit_start > 0); |
61ff7397 | 288 | assert_se(c = hashmap_get(h1, "/derp.slice")); |
69c8f025 | 289 | assert_se(c->mem_pressure_limit_hit_start == 0); |
61ff7397 AZ |
290 | } |
291 | ||
eeeaa422 | 292 | static void test_oomd_mem_and_swap_free_below(void) { |
61ff7397 | 293 | OomdSystemContext ctx = (OomdSystemContext) { |
eeeaa422 AZ |
294 | .mem_total = 20971512 * 1024U, |
295 | .mem_used = 3310136 * 1024U, | |
61ff7397 AZ |
296 | .swap_total = 20971512 * 1024U, |
297 | .swap_used = 20971440 * 1024U, | |
298 | }; | |
030bc91c | 299 | assert_se(oomd_mem_available_below(&ctx, 2000) == false); |
d06e7fb5 | 300 | assert_se(oomd_swap_free_below(&ctx, 2000) == true); |
61ff7397 AZ |
301 | |
302 | ctx = (OomdSystemContext) { | |
eeeaa422 AZ |
303 | .mem_total = 20971512 * 1024U, |
304 | .mem_used = 20971440 * 1024U, | |
61ff7397 AZ |
305 | .swap_total = 20971512 * 1024U, |
306 | .swap_used = 3310136 * 1024U, | |
307 | }; | |
030bc91c | 308 | assert_se(oomd_mem_available_below(&ctx, 2000) == true); |
d06e7fb5 | 309 | assert_se(oomd_swap_free_below(&ctx, 2000) == false); |
408a3bbd AZ |
310 | |
311 | ctx = (OomdSystemContext) { | |
eeeaa422 AZ |
312 | .mem_total = 0, |
313 | .mem_used = 0, | |
408a3bbd AZ |
314 | .swap_total = 0, |
315 | .swap_used = 0, | |
316 | }; | |
030bc91c | 317 | assert_se(oomd_mem_available_below(&ctx, 2000) == false); |
d06e7fb5 | 318 | assert_se(oomd_swap_free_below(&ctx, 2000) == false); |
61ff7397 AZ |
319 | } |
320 | ||
321 | static void test_oomd_sort_cgroups(void) { | |
322 | _cleanup_hashmap_free_ Hashmap *h = NULL; | |
323 | _cleanup_free_ OomdCGroupContext **sorted_cgroups; | |
324 | char **paths = STRV_MAKE("/herp.slice", | |
325 | "/herp.slice/derp.scope", | |
326 | "/herp.slice/derp.scope/sheep.service", | |
59331b8e | 327 | "/zupa.slice", |
88e47952 | 328 | "/boop.slice", |
59331b8e AZ |
329 | "/omitted.slice", |
330 | "/avoid.slice"); | |
61ff7397 | 331 | |
88e47952 | 332 | OomdCGroupContext ctx[7] = { |
61ff7397 AZ |
333 | { .path = paths[0], |
334 | .swap_usage = 20, | |
88e47952 AZ |
335 | .last_pgscan = 0, |
336 | .pgscan = 33, | |
1f76411b | 337 | .current_memory_usage = 10 }, |
61ff7397 AZ |
338 | { .path = paths[1], |
339 | .swap_usage = 60, | |
88e47952 AZ |
340 | .last_pgscan = 33, |
341 | .pgscan = 1, | |
1f76411b | 342 | .current_memory_usage = 20 }, |
61ff7397 AZ |
343 | { .path = paths[2], |
344 | .swap_usage = 40, | |
88e47952 AZ |
345 | .last_pgscan = 1, |
346 | .pgscan = 33, | |
1f76411b | 347 | .current_memory_usage = 40 }, |
61ff7397 AZ |
348 | { .path = paths[3], |
349 | .swap_usage = 10, | |
88e47952 AZ |
350 | .last_pgscan = 33, |
351 | .pgscan = 2, | |
1f76411b | 352 | .current_memory_usage = 10 }, |
59331b8e | 353 | { .path = paths[4], |
88e47952 AZ |
354 | .swap_usage = 11, |
355 | .last_pgscan = 33, | |
356 | .pgscan = 33, | |
357 | .current_memory_usage = 10 }, | |
358 | { .path = paths[5], | |
59331b8e | 359 | .swap_usage = 90, |
88e47952 AZ |
360 | .last_pgscan = 0, |
361 | .pgscan = UINT64_MAX, | |
59331b8e | 362 | .preference = MANAGED_OOM_PREFERENCE_OMIT }, |
88e47952 | 363 | { .path = paths[6], |
59331b8e | 364 | .swap_usage = 99, |
88e47952 AZ |
365 | .last_pgscan = 0, |
366 | .pgscan = UINT64_MAX, | |
59331b8e | 367 | .preference = MANAGED_OOM_PREFERENCE_AVOID }, |
61ff7397 AZ |
368 | }; |
369 | ||
370 | assert_se(h = hashmap_new(&string_hash_ops)); | |
371 | ||
372 | assert_se(hashmap_put(h, "/herp.slice", &ctx[0]) >= 0); | |
373 | assert_se(hashmap_put(h, "/herp.slice/derp.scope", &ctx[1]) >= 0); | |
374 | assert_se(hashmap_put(h, "/herp.slice/derp.scope/sheep.service", &ctx[2]) >= 0); | |
375 | assert_se(hashmap_put(h, "/zupa.slice", &ctx[3]) >= 0); | |
88e47952 AZ |
376 | assert_se(hashmap_put(h, "/boop.slice", &ctx[4]) >= 0); |
377 | assert_se(hashmap_put(h, "/omitted.slice", &ctx[5]) >= 0); | |
378 | assert_se(hashmap_put(h, "/avoid.slice", &ctx[6]) >= 0); | |
61ff7397 | 379 | |
88e47952 | 380 | assert_se(oomd_sort_cgroup_contexts(h, compare_swap_usage, NULL, &sorted_cgroups) == 6); |
61ff7397 AZ |
381 | assert_se(sorted_cgroups[0] == &ctx[1]); |
382 | assert_se(sorted_cgroups[1] == &ctx[2]); | |
383 | assert_se(sorted_cgroups[2] == &ctx[0]); | |
88e47952 AZ |
384 | assert_se(sorted_cgroups[3] == &ctx[4]); |
385 | assert_se(sorted_cgroups[4] == &ctx[3]); | |
386 | assert_se(sorted_cgroups[5] == &ctx[6]); | |
61ff7397 AZ |
387 | sorted_cgroups = mfree(sorted_cgroups); |
388 | ||
88e47952 AZ |
389 | assert_se(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, NULL, &sorted_cgroups) == 6); |
390 | assert_se(sorted_cgroups[0] == &ctx[0]); | |
391 | assert_se(sorted_cgroups[1] == &ctx[2]); | |
392 | assert_se(sorted_cgroups[2] == &ctx[3]); | |
1f76411b | 393 | assert_se(sorted_cgroups[3] == &ctx[1]); |
88e47952 AZ |
394 | assert_se(sorted_cgroups[4] == &ctx[4]); |
395 | assert_se(sorted_cgroups[5] == &ctx[6]); | |
61ff7397 AZ |
396 | sorted_cgroups = mfree(sorted_cgroups); |
397 | ||
88e47952 | 398 | assert_se(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, "/herp.slice/derp.scope", &sorted_cgroups) == 2); |
1f76411b AZ |
399 | assert_se(sorted_cgroups[0] == &ctx[2]); |
400 | assert_se(sorted_cgroups[1] == &ctx[1]); | |
61ff7397 AZ |
401 | assert_se(sorted_cgroups[2] == 0); |
402 | assert_se(sorted_cgroups[3] == 0); | |
59331b8e AZ |
403 | assert_se(sorted_cgroups[4] == 0); |
404 | assert_se(sorted_cgroups[5] == 0); | |
88e47952 | 405 | assert_se(sorted_cgroups[6] == 0); |
61ff7397 AZ |
406 | sorted_cgroups = mfree(sorted_cgroups); |
407 | } | |
408 | ||
39f9eee8 NR |
409 | static void test_oomd_fetch_cgroup_oom_preference(void) { |
410 | _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL; | |
411 | _cleanup_free_ char *cgroup = NULL; | |
412 | ManagedOOMPreference root_pref; | |
413 | CGroupMask mask; | |
414 | bool test_xattrs; | |
415 | int root_xattrs, r; | |
416 | ||
417 | if (geteuid() != 0) | |
418 | return (void) log_tests_skipped("not root"); | |
419 | ||
420 | if (!is_pressure_supported()) | |
421 | return (void) log_tests_skipped("system does not support pressure"); | |
422 | ||
423 | if (cg_all_unified() <= 0) | |
424 | return (void) log_tests_skipped("cgroups are not running in unified mode"); | |
425 | ||
426 | assert_se(cg_mask_supported(&mask) >= 0); | |
427 | ||
428 | if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY)) | |
429 | return (void) log_tests_skipped("cgroup memory controller is not available"); | |
430 | ||
431 | assert_se(cg_pid_get_path(NULL, 0, &cgroup) >= 0); | |
432 | assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0); | |
433 | ||
434 | /* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities | |
435 | * so skip the xattr portions of the test. */ | |
436 | r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_test", "1", 1, 0); | |
437 | test_xattrs = !ERRNO_IS_PRIVILEGE(r) && !ERRNO_IS_NOT_SUPPORTED(r); | |
438 | ||
439 | if (test_xattrs) { | |
440 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0); | |
441 | assert_se(cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_omit", "1", 1, 0) >= 0); | |
442 | assert_se(cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_avoid", "1", 1, 0) >= 0); | |
443 | ||
444 | /* omit takes precedence over avoid when both are set to true */ | |
445 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0); | |
446 | assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_OMIT); | |
447 | } else { | |
448 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) < 0); | |
449 | assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_NONE); | |
450 | } | |
451 | ctx = oomd_cgroup_context_free(ctx); | |
452 | ||
453 | /* also check when only avoid is set to true */ | |
454 | if (test_xattrs) { | |
455 | assert_se(cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_omit", "0", 1, 0) >= 0); | |
456 | assert_se(cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_avoid", "1", 1, 0) >= 0); | |
457 | assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0); | |
458 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0); | |
459 | assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_AVOID); | |
460 | ctx = oomd_cgroup_context_free(ctx); | |
461 | } | |
462 | ||
463 | /* Test the root cgroup */ | |
464 | /* Root cgroup is live and not made on demand like the cgroup the test runs in. It can have varying | |
465 | * xattrs set already so let's read in the booleans first to get the final preference value. */ | |
466 | assert_se(oomd_cgroup_context_acquire("", &ctx) == 0); | |
467 | root_xattrs = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, "", "user.oomd_omit"); | |
468 | root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_OMIT : MANAGED_OOM_PREFERENCE_NONE; | |
469 | root_xattrs = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, "", "user.oomd_avoid"); | |
470 | root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_AVOID : MANAGED_OOM_PREFERENCE_NONE; | |
471 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0); | |
472 | assert_se(ctx->preference == root_pref); | |
473 | ||
474 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, "/herp.slice/derp.scope") == -EINVAL); | |
475 | ||
476 | /* Assert that avoid/omit are not set if the cgroup and prefix are not | |
0b75493d | 477 | * owned by the same user. */ |
f05bcc18 | 478 | if (test_xattrs && !empty_or_root(cgroup)) { |
39f9eee8 | 479 | ctx = oomd_cgroup_context_free(ctx); |
f05bcc18 | 480 | assert_se(cg_set_access(SYSTEMD_CGROUP_CONTROLLER, cgroup, 61183, 0) >= 0); |
39f9eee8 NR |
481 | assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0); |
482 | ||
483 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0); | |
484 | assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_NONE); | |
485 | ||
486 | assert_se(oomd_fetch_cgroup_oom_preference(ctx, ctx->path) == 0); | |
487 | assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_AVOID); | |
488 | } | |
489 | } | |
490 | ||
61ff7397 AZ |
491 | int main(void) { |
492 | int r; | |
493 | ||
494 | test_setup_logging(LOG_DEBUG); | |
495 | ||
b037a6da | 496 | test_oomd_update_cgroup_contexts_between_hashmaps(); |
61ff7397 AZ |
497 | test_oomd_system_context_acquire(); |
498 | test_oomd_pressure_above(); | |
eeeaa422 | 499 | test_oomd_mem_and_swap_free_below(); |
61ff7397 AZ |
500 | test_oomd_sort_cgroups(); |
501 | ||
502 | /* The following tests operate on live cgroups */ | |
503 | ||
504 | r = enter_cgroup_root(NULL); | |
505 | if (r < 0) | |
506 | return log_tests_skipped_errno(r, "failed to enter a test cgroup scope"); | |
507 | ||
508 | test_oomd_cgroup_kill(); | |
509 | test_oomd_cgroup_context_acquire_and_insert(); | |
39f9eee8 | 510 | test_oomd_fetch_cgroup_oom_preference(); |
61ff7397 AZ |
511 | |
512 | return 0; | |
513 | } |