• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14013585018

22 Mar 2025 03:54PM UTC coverage: 71.951% (+0.002%) from 71.949%
14013585018

push

github

web-flow
some dbus property fixes (#36830)

4 of 4 new or added lines in 1 file covered. (100.0%)

145 existing lines in 35 files now uncovered.

296620 of 412255 relevant lines covered (71.95%)

737629.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.62
/src/oom/oomd-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/xattr.h>
4
#include <unistd.h>
5

6
#include "errno-util.h"
7
#include "fd-util.h"
8
#include "fileio.h"
9
#include "format-util.h"
10
#include "memstream-util.h"
11
#include "oomd-util.h"
12
#include "parse-util.h"
13
#include "path-util.h"
14
#include "procfs-util.h"
15
#include "signal-util.h"
16
#include "sort-util.h"
17
#include "stat-util.h"
18
#include "stdio-util.h"
19
#include "user-util.h"
20

21
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
121✔
22
                oomd_cgroup_ctx_hash_ops,
23
                char,
24
                path_hash_func,
25
                path_compare,
26
                OomdCGroupContext,
27
                oomd_cgroup_context_free);
28

29
static int log_kill(const PidRef *pid, int sig, void *userdata) {
75✔
30
        log_debug("oomd attempting to kill " PID_FMT " with %s", pid->pid, signal_to_string(sig));
75✔
31
        return 0;
75✔
32
}
33

34
static int increment_oomd_xattr(const char *path, const char *xattr, uint64_t num_procs_killed) {
12✔
35
        _cleanup_free_ char *value = NULL;
12✔
36
        char buf[DECIMAL_STR_MAX(uint64_t) + 1];
12✔
37
        uint64_t curr_count = 0;
12✔
38
        int r;
12✔
39

40
        assert(path);
12✔
41
        assert(xattr);
12✔
42

43
        r = cg_get_xattr_malloc(path, xattr, &value, /* ret_size= */ NULL);
12✔
44
        if (r < 0 && !ERRNO_IS_XATTR_ABSENT(r))
12✔
45
                return r;
46

47
        if (!isempty(value)) {
12✔
48
                 r = safe_atou64(value, &curr_count);
2✔
49
                 if (r < 0)
2✔
50
                         return r;
51
        }
52

53
        if (curr_count > UINT64_MAX - num_procs_killed)
12✔
54
                return -EOVERFLOW;
55

56
        xsprintf(buf, "%"PRIu64, curr_count + num_procs_killed);
12✔
57
        r = cg_set_xattr(path, xattr, buf, strlen(buf), 0);
12✔
58
        if (r < 0)
12✔
59
                return r;
×
60

61
        return 0;
62
}
63

64
OomdCGroupContext *oomd_cgroup_context_free(OomdCGroupContext *ctx) {
591✔
65
        if (!ctx)
591✔
66
                return NULL;
67

68
        free(ctx->path);
145✔
69
        return mfree(ctx);
145✔
70
}
71

72
int oomd_pressure_above(Hashmap *h, Set **ret) {
47✔
73
        _cleanup_set_free_ Set *targets = NULL;
47✔
74
        OomdCGroupContext *ctx;
47✔
75
        char *key;
47✔
76
        int r;
47✔
77

78
        assert(h);
47✔
79
        assert(ret);
47✔
80

81
        targets = set_new(NULL);
47✔
82
        if (!targets)
47✔
83
                return -ENOMEM;
84

85
        HASHMAP_FOREACH_KEY(ctx, key, h) {
112✔
86
                if (ctx->memory_pressure.avg10 > ctx->mem_pressure_limit) {
65✔
87
                        usec_t diff;
32✔
88

89
                        if (ctx->mem_pressure_limit_hit_start == 0)
32✔
90
                                ctx->mem_pressure_limit_hit_start = now(CLOCK_MONOTONIC);
5✔
91

92
                        diff = now(CLOCK_MONOTONIC) - ctx->mem_pressure_limit_hit_start;
32✔
93
                        if (diff >= ctx->mem_pressure_duration_usec) {
32✔
94
                                r = set_put(targets, ctx);
24✔
95
                                if (r < 0)
24✔
96
                                        return -ENOMEM;
×
97
                        }
98
                } else
99
                        ctx->mem_pressure_limit_hit_start = 0;
33✔
100
        }
101

102
        if (!set_isempty(targets)) {
47✔
103
                *ret = TAKE_PTR(targets);
24✔
104
                return 1;
24✔
105
        }
106

107
        *ret = NULL;
23✔
108
        return 0;
23✔
109
}
110

111
uint64_t oomd_pgscan_rate(const OomdCGroupContext *c) {
212✔
112
        uint64_t last_pgscan;
212✔
113

114
        assert(c);
212✔
115

116
        /* If last_pgscan > pgscan, assume the cgroup was recreated and reset last_pgscan to zero.
117
         * pgscan is monotonic and in practice should not decrease (except in the recreation case). */
118
        last_pgscan = c->last_pgscan;
212✔
119
        if (c->last_pgscan > c->pgscan) {
212✔
120
                log_debug("Last pgscan %"PRIu64" greater than current pgscan %"PRIu64" for %s. Using last pgscan of zero.",
8✔
121
                                c->last_pgscan, c->pgscan, c->path);
122
                last_pgscan = 0;
123
        }
124

125
        return c->pgscan - last_pgscan;
212✔
126
}
127

128
bool oomd_mem_available_below(const OomdSystemContext *ctx, int threshold_permyriad) {
3✔
129
        uint64_t mem_threshold;
3✔
130

131
        assert(ctx);
3✔
132
        assert(threshold_permyriad <= 10000);
3✔
133

134
        mem_threshold = ctx->mem_total * threshold_permyriad / (uint64_t) 10000;
3✔
135
        return LESS_BY(ctx->mem_total, ctx->mem_used) < mem_threshold;
3✔
136
}
137

138
bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad) {
3✔
139
        uint64_t swap_threshold;
3✔
140

141
        assert(ctx);
3✔
142
        assert(threshold_permyriad <= 10000);
3✔
143

144
        swap_threshold = ctx->swap_total * threshold_permyriad / (uint64_t) 10000;
3✔
145
        return (ctx->swap_total - ctx->swap_used) < swap_threshold;
3✔
146
}
147

148
int oomd_fetch_cgroup_oom_preference(OomdCGroupContext *ctx, const char *prefix) {
32✔
149
        uid_t uid;
32✔
150
        int r;
32✔
151

152
        assert(ctx);
32✔
153

154
        prefix = empty_to_root(prefix);
32✔
155

156
        if (!path_startswith(ctx->path, prefix))
32✔
157
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
158
                                       "%s is not a descendant of %s", ctx->path, prefix);
159

160
        r = cg_get_owner(ctx->path, &uid);
31✔
161
        if (r < 0)
31✔
162
                return log_debug_errno(r, "Failed to get owner/group from %s: %m", ctx->path);
16✔
163

164
        if (uid != 0) {
15✔
165
                uid_t prefix_uid;
4✔
166

167
                r = cg_get_owner(prefix, &prefix_uid);
4✔
168
                if (r < 0)
4✔
169
                        return log_debug_errno(r, "Failed to get owner/group from %s: %m", prefix);
1✔
170

171
                if (uid != prefix_uid) {
4✔
172
                        ctx->preference = MANAGED_OOM_PREFERENCE_NONE;
1✔
173
                        return 0;
1✔
174
                }
175
        }
176

177
        /* Ignore most errors when reading the xattr since it is usually unset and cgroup xattrs are only used
178
         * as an optional feature of systemd-oomd (and the system might not even support them). */
179
        r = cg_get_xattr_bool(ctx->path, "user.oomd_avoid");
14✔
180
        if (r == -ENOMEM)
14✔
181
                return log_oom_debug();
×
182
        if (r < 0 && !ERRNO_IS_XATTR_ABSENT(r))
14✔
183
                log_debug_errno(r, "Failed to get xattr user.oomd_avoid, ignoring: %m");
×
184
        ctx->preference = r > 0 ? MANAGED_OOM_PREFERENCE_AVOID : ctx->preference;
14✔
185

186
        r = cg_get_xattr_bool(ctx->path, "user.oomd_omit");
14✔
187
        if (r == -ENOMEM)
14✔
188
                return log_oom_debug();
×
189
        if (r < 0 && !ERRNO_IS_XATTR_ABSENT(r))
14✔
190
                log_debug_errno(r, "Failed to get xattr user.oomd_omit, ignoring: %m");
×
191
        ctx->preference = r > 0 ? MANAGED_OOM_PREFERENCE_OMIT : ctx->preference;
14✔
192

193
        return 0;
14✔
194
}
195

196
int oomd_sort_cgroup_contexts(Hashmap *h, oomd_compare_t compare_func, const char *prefix, OomdCGroupContext ***ret) {
7✔
197
        _cleanup_free_ OomdCGroupContext **sorted = NULL;
7✔
198
        OomdCGroupContext *item;
7✔
199
        size_t k = 0;
7✔
200
        int r;
7✔
201

202
        assert(h);
7✔
203
        assert(compare_func);
7✔
204
        assert(ret);
7✔
205

206
        sorted = new0(OomdCGroupContext*, hashmap_size(h));
7✔
207
        if (!sorted)
7✔
208
                return -ENOMEM;
209

210
        HASHMAP_FOREACH(item, h) {
37✔
211
                /* Skip over cgroups that are not valid candidates or are explicitly marked for omission */
212
                if (item->path && prefix && !path_startswith(item->path, prefix))
30✔
213
                        continue;
5✔
214

215
                r = oomd_fetch_cgroup_oom_preference(item, prefix);
25✔
216
                if (r == -ENOMEM)
25✔
217
                        return r;
×
218

219
                if (item->preference == MANAGED_OOM_PREFERENCE_OMIT)
25✔
220
                        continue;
2✔
221

222
                sorted[k++] = item;
23✔
223
        }
224

225
        typesafe_qsort(sorted, k, compare_func);
7✔
226

227
        *ret = TAKE_PTR(sorted);
7✔
228

229
        assert(k <= INT_MAX);
7✔
230
        return (int) k;
7✔
231
}
232

233
int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
6✔
234
        _cleanup_set_free_ Set *pids_killed = NULL;
6✔
235
        int r;
6✔
236

237
        assert(path);
6✔
238

239
        if (dry_run) {
6✔
240
                _cleanup_free_ char *cg_path = NULL;
×
241

242
                r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &cg_path);
×
243
                if (r < 0)
×
244
                        return r;
245

246
                log_info("oomd dry-run: Would have tried to kill %s with recurse=%s", cg_path, true_false(recurse));
×
247
                return 0;
×
248
        }
249

250
        pids_killed = set_new(NULL);
6✔
251
        if (!pids_killed)
6✔
252
                return -ENOMEM;
253

254
        r = increment_oomd_xattr(path, "user.oomd_ooms", 1);
6✔
255
        if (r < 0)
6✔
256
                log_debug_errno(r, "Failed to set user.oomd_ooms before kill: %m");
×
257

258
        if (recurse)
6✔
259
                r = cg_kill_recursive(path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
4✔
260
        else
261
                r = cg_kill(path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
2✔
262

263
        /* The cgroup could have been cleaned up after we have sent SIGKILL to all of the processes, but before
264
         * we could do one last iteration of cgroup.procs to check. Or the service unit could have exited and
265
         * was removed between picking candidates and coming into this function. In either case, let's log
266
         * about it let the caller decide what to do once they know how many PIDs were killed. */
267
        if (IN_SET(r, -ENOENT, -ENODEV))
6✔
268
                log_debug_errno(r, "Error when sending SIGKILL to processes in cgroup path %s, ignoring: %m", path);
×
269
        else if (r < 0)
6✔
270
                return r;
271

272
        if (set_isempty(pids_killed))
6✔
273
                log_debug("Nothing killed when attempting to kill %s", path);
×
274

275
        r = increment_oomd_xattr(path, "user.oomd_kill", set_size(pids_killed));
6✔
276
        if (r < 0)
6✔
277
                log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m");
×
278

279
        return !set_isempty(pids_killed);
6✔
280
}
281

282
typedef void (*dump_candidate_func)(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
283

284
static int dump_kill_candidates(
4✔
285
                OomdCGroupContext *sorted[],
286
                size_t n,
287
                const OomdCGroupContext *killed,
288
                dump_candidate_func dump_func) {
289

290
        _cleanup_(memstream_done) MemStream m = {};
4✔
291
        FILE *f;
4✔
292

293
        /* Try dumping top offendors, ignoring any errors that might happen. */
294

295
        assert(sorted || n == 0);
4✔
296
        assert(dump_func);
4✔
297

298
        /* If nothing killed, then limit the number of contexts to be dumped, for safety. */
299
        if (!killed)
4✔
300
                n = MIN(n, DUMP_ON_KILL_COUNT);
×
301

302
        f = memstream_init(&m);
4✔
303
        if (!f)
4✔
304
                return -ENOMEM;
305

306
        fprintf(f, "Considered %zu cgroups for killing, top candidates were:\n", n);
4✔
307
        FOREACH_ARRAY(i, sorted, n) {
4✔
308
                const OomdCGroupContext *c = *i;
4✔
309

310
                dump_func(c, f, "\t");
4✔
311

312
                if (c == killed)
4✔
313
                        break;
314
        }
315

316
        return memstream_dump(LOG_INFO, &m);
4✔
317
}
318

319
int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char **ret_selected) {
4✔
320
        _cleanup_free_ OomdCGroupContext **sorted = NULL;
4✔
321
        const OomdCGroupContext *killed = NULL;
4✔
322
        int n, r, ret = 0;
4✔
323

324
        assert(h);
4✔
325
        assert(ret_selected);
4✔
326

327
        n = oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, prefix, &sorted);
4✔
328
        if (n < 0)
4✔
329
                return n;
330

331
        FOREACH_ARRAY(i, sorted, n) {
4✔
332
                const OomdCGroupContext *c = *i;
4✔
333

334
                /* Skip cgroups with no reclaim and memory usage; it won't alleviate pressure.
335
                 * Continue since there might be "avoid" cgroups at the end. */
336
                if (c->pgscan == 0 && c->current_memory_usage == 0)
4✔
337
                        continue;
×
338

339
                r = oomd_cgroup_kill(c->path, /* recurse= */ true, /* dry_run= */ dry_run);
4✔
340
                if (r == -ENOMEM)
4✔
341
                        return r; /* Treat oom as a hard error */
342
                if (r < 0) {
4✔
343
                        RET_GATHER(ret, r);
×
344
                        continue; /* Try to find something else to kill */
×
345
                }
346

347
                ret = r;
4✔
348
                r = strdup_to(ret_selected, c->path);
4✔
349
                if (r < 0)
4✔
350
                        return r;
351

352
                killed = c;
353
                break;
354
        }
355

356
        (void) dump_kill_candidates(sorted, n, killed, oomd_dump_memory_pressure_cgroup_context);
4✔
357
        return ret;
358
}
359

360
int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected) {
×
361
        _cleanup_free_ OomdCGroupContext **sorted = NULL;
×
362
        const OomdCGroupContext *killed = NULL;
×
363
        int n, r, ret = 0;
×
364

365
        assert(h);
×
366
        assert(ret_selected);
×
367

368
        n = oomd_sort_cgroup_contexts(h, compare_swap_usage, NULL, &sorted);
×
369
        if (n < 0)
×
370
                return n;
371

372
        /* Try to kill cgroups with non-zero swap usage until we either succeed in killing or we get to a cgroup with
373
         * no swap usage. Threshold killing only cgroups with more than threshold swap usage. */
374

375
        FOREACH_ARRAY(i, sorted, n) {
×
376
                const OomdCGroupContext *c = *i;
×
377

378
                /* Skip over cgroups with not enough swap usage. Don't break since there might be "avoid"
379
                 * cgroups at the end. */
380
                if (c->swap_usage <= threshold_usage)
×
381
                        continue;
×
382

383
                r = oomd_cgroup_kill(c->path, /* recurse= */ true, /* dry_run= */ dry_run);
×
384
                if (r == -ENOMEM)
×
385
                        return r; /* Treat oom as a hard error */
386
                if (r < 0) {
×
387
                        RET_GATHER(ret, r);
×
388
                        continue; /* Try to find something else to kill */
×
389
                }
390

391
                ret = r;
×
392
                r = strdup_to(ret_selected, c->path);
×
393
                if (r < 0)
×
394
                        return r;
395

396
                killed = c;
397
                break;
398
        }
399

400
        (void) dump_kill_candidates(sorted, n, killed, oomd_dump_swap_cgroup_context);
×
401
        return ret;
402
}
403

404
int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret) {
145✔
405
        _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL;
×
406
        _cleanup_free_ char *p = NULL, *val = NULL;
145✔
407
        bool is_root;
145✔
408
        int r;
145✔
409

410
        assert(path);
145✔
411
        assert(ret);
145✔
412

413
        ctx = new0(OomdCGroupContext, 1);
145✔
414
        if (!ctx)
145✔
415
                return -ENOMEM;
416

417
        is_root = empty_or_root(path);
145✔
418
        ctx->preference = MANAGED_OOM_PREFERENCE_NONE;
145✔
419

420
        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, "memory.pressure", &p);
145✔
421
        if (r < 0)
145✔
422
                return log_debug_errno(r, "Error getting cgroup memory pressure path from %s: %m", path);
×
423

424
        r = read_resource_pressure(p, PRESSURE_TYPE_FULL, &ctx->memory_pressure);
145✔
425
        if (r < 0)
145✔
426
                return log_debug_errno(r, "Error parsing memory pressure from %s: %m", p);
×
427

428
        if (is_root) {
145✔
429
                r = procfs_memory_get_used(&ctx->current_memory_usage);
2✔
430
                if (r < 0)
2✔
431
                        return log_debug_errno(r, "Error getting memory used from procfs: %m");
×
432
        } else {
433
                r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.current", &ctx->current_memory_usage);
143✔
434
                if (r < 0)
143✔
UNCOV
435
                        return log_debug_errno(r, "Error getting memory.current from %s: %m", path);
×
436

437
                r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.min", &ctx->memory_min);
143✔
438
                if (r < 0)
143✔
439
                        return log_debug_errno(r, "Error getting memory.min from %s: %m", path);
×
440

441
                r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.low", &ctx->memory_low);
143✔
442
                if (r < 0)
143✔
443
                        return log_debug_errno(r, "Error getting memory.low from %s: %m", path);
×
444

445
                r = cg_get_attribute_as_uint64(SYSTEMD_CGROUP_CONTROLLER, path, "memory.swap.current", &ctx->swap_usage);
143✔
446
                if (r == -ENODATA)
143✔
447
                        /* The kernel can be compiled without support for memory.swap.* files,
448
                         * or it can be disabled with boot param 'swapaccount=0' */
449
                        log_once(LOG_WARNING, "No kernel support for memory.swap.current from %s (try boot param swapaccount=1), ignoring.", path);
×
450
                else if (r < 0)
143✔
451
                        return log_debug_errno(r, "Error getting memory.swap.current from %s: %m", path);
×
452

453
                r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, path, "memory.stat", STRV_MAKE("pgscan"), &val);
143✔
454
                if (r < 0)
143✔
UNCOV
455
                        return log_debug_errno(r, "Error getting pgscan from memory.stat under %s: %m", path);
×
456

457
                r = safe_atou64(val, &ctx->pgscan);
143✔
458
                if (r < 0)
143✔
459
                        return log_debug_errno(r, "Error converting pgscan value to uint64_t: %m");
×
460
        }
461

462
        r = strdup_to(&ctx->path, empty_to_root(path));
290✔
463
        if (r < 0)
145✔
464
                return r;
465

466
        *ret = TAKE_PTR(ctx);
145✔
467
        return 0;
145✔
468
}
469

470
int oomd_system_context_acquire(const char *proc_meminfo_path, OomdSystemContext *ret) {
43✔
471
        _cleanup_fclose_ FILE *f = NULL;
43✔
472
        unsigned field_filled = 0;
43✔
473
        OomdSystemContext ctx = {};
43✔
474
        uint64_t mem_available, swap_free;
43✔
475
        int r;
43✔
476

477
        enum {
43✔
478
                MEM_TOTAL = 1U << 0,
479
                MEM_AVAILABLE = 1U << 1,
480
                SWAP_TOTAL = 1U << 2,
481
                SWAP_FREE = 1U << 3,
482
                ALL = MEM_TOTAL|MEM_AVAILABLE|SWAP_TOTAL|SWAP_FREE,
483
        };
484

485
        assert(proc_meminfo_path);
43✔
486
        assert(ret);
43✔
487

488
        f = fopen(proc_meminfo_path, "re");
43✔
489
        if (!f)
43✔
490
                return -errno;
1✔
491

492
        for (;;) {
625✔
493
                _cleanup_free_ char *line = NULL;
586✔
494
                char *word;
625✔
495

496
                r = read_line(f, LONG_LINE_MAX, &line);
625✔
497
                if (r < 0)
625✔
498
                        return r;
499
                if (r == 0)
625✔
500
                        return -EINVAL;
501

502
                if ((word = startswith(line, "MemTotal:"))) {
623✔
503
                        field_filled |= MEM_TOTAL;
40✔
504
                        r = convert_meminfo_value_to_uint64_bytes(word, &ctx.mem_total);
40✔
505
                } else if ((word = startswith(line, "MemAvailable:"))) {
583✔
506
                        field_filled |= MEM_AVAILABLE;
39✔
507
                        r = convert_meminfo_value_to_uint64_bytes(word, &mem_available);
39✔
508
                } else if ((word = startswith(line, "SwapTotal:"))) {
544✔
509
                        field_filled |= SWAP_TOTAL;
39✔
510
                        r = convert_meminfo_value_to_uint64_bytes(word, &ctx.swap_total);
39✔
511
                } else if ((word = startswith(line, "SwapFree:"))) {
505✔
512
                        field_filled |= SWAP_FREE;
39✔
513
                        r = convert_meminfo_value_to_uint64_bytes(word, &swap_free);
39✔
514
                } else
515
                        continue;
466✔
516

517
                if (r < 0)
157✔
518
                        return log_debug_errno(r, "Error converting '%s' from %s to uint64_t: %m", line, proc_meminfo_path);
1✔
519

520
                if (field_filled == ALL)
156✔
521
                        break;
522
        }
523

524
        if (field_filled != ALL)
39✔
525
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "%s is missing expected fields", proc_meminfo_path);
526

527
        if (mem_available > ctx.mem_total)
39✔
528
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
529
                                       "MemAvailable (%" PRIu64 ") cannot be greater than MemTotal (%" PRIu64 ") %m",
530
                                       mem_available,
531
                                       ctx.mem_total);
532

533
        if (swap_free > ctx.swap_total)
39✔
534
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
535
                                       "SwapFree (%" PRIu64 ") cannot be greater than SwapTotal (%" PRIu64 ") %m",
536
                                       swap_free,
537
                                       ctx.swap_total);
538

539
        ctx.mem_used = ctx.mem_total - mem_available;
39✔
540
        ctx.swap_used = ctx.swap_total - swap_free;
39✔
541

542
        *ret = ctx;
39✔
543
        return 0;
39✔
544
}
545

546
int oomd_insert_cgroup_context(Hashmap *old_h, Hashmap *new_h, const char *path) {
139✔
547
        _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *curr_ctx = NULL;
139✔
548
        OomdCGroupContext *old_ctx;
139✔
549
        int r;
139✔
550

551
        assert(new_h);
139✔
552
        assert(path);
139✔
553

554
        path = empty_to_root(path);
139✔
555

556
        r = oomd_cgroup_context_acquire(path, &curr_ctx);
139✔
557
        if (r < 0)
139✔
UNCOV
558
                return log_debug_errno(r, "Failed to get OomdCGroupContext for %s: %m", path);
×
559

560
        assert_se(streq(path, curr_ctx->path));
139✔
561

562
        old_ctx = hashmap_get(old_h, path);
139✔
563
        if (old_ctx) {
139✔
564
                curr_ctx->last_pgscan = old_ctx->pgscan;
62✔
565
                curr_ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
62✔
566
                curr_ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
62✔
567
                curr_ctx->mem_pressure_duration_usec = old_ctx->mem_pressure_duration_usec;
62✔
568
                curr_ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
62✔
569
        }
570

571
        if (oomd_pgscan_rate(curr_ctx) > 0)
139✔
572
                curr_ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
106✔
573

574
        r = hashmap_put(new_h, curr_ctx->path, curr_ctx);
139✔
575
        if (r < 0)
139✔
576
                return r;
577

578
        TAKE_PTR(curr_ctx);
127✔
579
        return 0;
127✔
580
}
581

582
void oomd_update_cgroup_contexts_between_hashmaps(Hashmap *old_h, Hashmap *curr_h) {
31✔
583
        OomdCGroupContext *ctx;
31✔
584

585
        assert(old_h);
31✔
586
        assert(curr_h);
31✔
587

588
        HASHMAP_FOREACH(ctx, curr_h) {
91✔
589
                OomdCGroupContext *old_ctx;
60✔
590

591
                old_ctx = hashmap_get(old_h, ctx->path);
60✔
592
                if (!old_ctx)
60✔
593
                        continue;
11✔
594

595
                ctx->last_pgscan = old_ctx->pgscan;
49✔
596
                ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
49✔
597
                ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
49✔
598
                ctx->mem_pressure_duration_usec = old_ctx->mem_pressure_duration_usec;
49✔
599
                ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
49✔
600

601
                if (oomd_pgscan_rate(ctx) > 0)
49✔
602
                        ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
20✔
603
        }
604
}
31✔
605

606
void oomd_dump_swap_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix) {
×
607
        assert(ctx);
×
608
        assert(f);
×
609

610
        if (!empty_or_root(ctx->path))
×
611
                fprintf(f,
×
612
                        "%sPath: %s\n"
613
                        "%s\tSwap Usage: %s\n",
614
                        strempty(prefix), ctx->path,
×
615
                        strempty(prefix), FORMAT_BYTES(ctx->swap_usage));
×
616
        else
617
                fprintf(f,
×
618
                        "%sPath: %s\n"
619
                        "%s\tSwap Usage: (see System Context)\n",
620
                        strempty(prefix), ctx->path,
×
621
                        strempty(prefix));
622
}
×
623

624
void oomd_dump_memory_pressure_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix) {
50✔
625
        assert(ctx);
50✔
626
        assert(f);
50✔
627

628
        fprintf(f,
100✔
629
                "%sPath: %s\n"
630
                "%s\tMemory Pressure Limit: %lu.%02lu%%\n"
631
                "%s\tMemory Pressure Duration: %s\n"
632
                "%s\tPressure: Avg10: %lu.%02lu, Avg60: %lu.%02lu, Avg300: %lu.%02lu, Total: %s\n"
633
                "%s\tCurrent Memory Usage: %s\n",
634
                strempty(prefix), ctx->path,
50✔
635
                strempty(prefix), LOADAVG_INT_SIDE(ctx->mem_pressure_limit), LOADAVG_DECIMAL_SIDE(ctx->mem_pressure_limit),
50✔
636
                strempty(prefix), FORMAT_TIMESPAN(ctx->mem_pressure_duration_usec, USEC_PER_SEC),
50✔
637
                strempty(prefix),
638
                LOADAVG_INT_SIDE(ctx->memory_pressure.avg10), LOADAVG_DECIMAL_SIDE(ctx->memory_pressure.avg10),
50✔
639
                LOADAVG_INT_SIDE(ctx->memory_pressure.avg60), LOADAVG_DECIMAL_SIDE(ctx->memory_pressure.avg60),
50✔
640
                LOADAVG_INT_SIDE(ctx->memory_pressure.avg300), LOADAVG_DECIMAL_SIDE(ctx->memory_pressure.avg300),
50✔
641
                FORMAT_TIMESPAN(ctx->memory_pressure.total, USEC_PER_SEC),
50✔
642
                strempty(prefix), FORMAT_BYTES(ctx->current_memory_usage));
50✔
643

644
        if (!empty_or_root(ctx->path))
50✔
645
                fprintf(f,
250✔
646
                        "%s\tMemory Min: %s\n"
647
                        "%s\tMemory Low: %s\n"
648
                        "%s\tPgscan: %" PRIu64 "\n"
649
                        "%s\tLast Pgscan: %" PRIu64 "\n",
650
                        strempty(prefix), FORMAT_BYTES_CGROUP_PROTECTION(ctx->memory_min),
50✔
651
                        strempty(prefix), FORMAT_BYTES_CGROUP_PROTECTION(ctx->memory_low),
50✔
652
                        strempty(prefix), ctx->pgscan,
50✔
653
                        strempty(prefix), ctx->last_pgscan);
50✔
654
}
50✔
655

656
void oomd_dump_system_context(const OomdSystemContext *ctx, FILE *f, const char *prefix) {
38✔
657
        assert(ctx);
38✔
658
        assert(f);
38✔
659

660
        fprintf(f,
76✔
661
                "%sMemory: Used: %s, Total: %s\n"
662
                "%sSwap: Used: %s, Total: %s\n",
663
                strempty(prefix),
664
                FORMAT_BYTES(ctx->mem_used),
38✔
665
                FORMAT_BYTES(ctx->mem_total),
38✔
666
                strempty(prefix),
667
                FORMAT_BYTES(ctx->swap_used),
38✔
668
                FORMAT_BYTES(ctx->swap_total));
38✔
669
}
38✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc