1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 #pragma once
3 
4 #include <stdbool.h>
5 
6 #include "cgroup-util.h"
7 #include "hashmap.h"
8 #include "psi-util.h"
9 
10 #define DUMP_ON_KILL_COUNT 10
11 #define GROWING_SIZE_PERCENTILE 80
12 
13 extern const struct hash_ops oomd_cgroup_ctx_hash_ops;
14 
15 typedef struct OomdCGroupContext OomdCGroupContext;
16 typedef struct OomdSystemContext OomdSystemContext;
17 
18 typedef int (oomd_compare_t)(OomdCGroupContext * const *, OomdCGroupContext * const *);
19 
20 struct OomdCGroupContext {
21         char *path;
22 
23         ResourcePressure memory_pressure;
24 
25         uint64_t current_memory_usage;
26 
27         uint64_t memory_min;
28         uint64_t memory_low;
29         uint64_t swap_usage;
30 
31         uint64_t last_pgscan;
32         uint64_t pgscan;
33 
34         ManagedOOMPreference preference;
35 
36         /* These are only used for acting on high memory pressure. */
37         loadavg_t mem_pressure_limit;
38         usec_t mem_pressure_limit_hit_start;
39         usec_t last_had_mem_reclaim;
40 };
41 
42 struct OomdSystemContext {
43         uint64_t mem_total;
44         uint64_t mem_used;
45         uint64_t swap_total;
46         uint64_t swap_used;
47 };
48 
49 OomdCGroupContext *oomd_cgroup_context_free(OomdCGroupContext *ctx);
50 DEFINE_TRIVIAL_CLEANUP_FUNC(OomdCGroupContext*, oomd_cgroup_context_free);
51 
52 /* All hashmaps used with these functions are expected to be of the form
53  * key: cgroup paths -> value: OomdCGroupContext. */
54 
55 /* Scans all the OomdCGroupContexts in `h` and returns 1 and a set of pointers to those OomdCGroupContexts in `ret`
56  * if any of them have exceeded their supplied memory pressure limits for the `duration` length of time.
57  * `mem_pressure_limit_hit_start` is updated accordingly for the first time the limit is exceeded, and when it returns
58  * below the limit.
59  * Returns 0 and sets `ret` to an empty set if no entries exceeded limits for `duration`.
60  * Returns -ENOMEM for allocation errors. */
61 int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret);
62 
63 /* Returns true if the amount of memory available (see proc(5)) is below the permyriad of memory specified by `threshold_permyriad`. */
64 bool oomd_mem_available_below(const OomdSystemContext *ctx, int threshold_permyriad);
65 
66 /* Returns true if the amount of swap free is below the permyriad of swap specified by `threshold_permyriad`. */
67 bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad);
68 
69 /* Returns pgscan - last_pgscan, accounting for corner cases. */
70 uint64_t oomd_pgscan_rate(const OomdCGroupContext *c);
71 
72 /* The compare functions will sort from largest to smallest, putting all the contexts with "avoid" at the end
73  * (after the smallest values). */
compare_pgscan_rate_and_memory_usage(OomdCGroupContext * const * c1,OomdCGroupContext * const * c2)74 static inline int compare_pgscan_rate_and_memory_usage(OomdCGroupContext * const *c1, OomdCGroupContext * const *c2) {
75         uint64_t diff1, diff2;
76         int r;
77 
78         assert(c1);
79         assert(c2);
80 
81         r = CMP((*c1)->preference, (*c2)->preference);
82         if (r != 0)
83                 return r;
84 
85         diff1 = oomd_pgscan_rate(*c1);
86         diff2 = oomd_pgscan_rate(*c2);
87         r = CMP(diff2, diff1);
88         if (r != 0)
89                 return r;
90 
91         return CMP((*c2)->current_memory_usage, (*c1)->current_memory_usage);
92 }
93 
compare_swap_usage(OomdCGroupContext * const * c1,OomdCGroupContext * const * c2)94 static inline int compare_swap_usage(OomdCGroupContext * const *c1, OomdCGroupContext * const *c2) {
95         int r;
96 
97         assert(c1);
98         assert(c2);
99 
100         r = CMP((*c1)->preference, (*c2)->preference);
101         if (r != 0)
102                 return r;
103 
104         return CMP((*c2)->swap_usage, (*c1)->swap_usage);
105 }
106 
107 /* Get an array of OomdCGroupContexts from `h`, qsorted from largest to smallest values according to `compare_func`.
108  * If `prefix` is not NULL, only include OomdCGroupContexts whose paths start with prefix. Otherwise all paths are sorted.
109  * Returns the number of sorted items; negative on error. */
110 int oomd_sort_cgroup_contexts(Hashmap *h, oomd_compare_t compare_func, const char *prefix, OomdCGroupContext ***ret);
111 
112 /* Returns a negative value on error, 0 if no processes were killed, or 1 if processes were killed. */
113 int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run);
114 
115 /* The following oomd_kill_by_* functions return 1 if processes were killed, or negative otherwise. */
116 /* If `prefix` is supplied, only cgroups whose paths start with `prefix` are eligible candidates. Otherwise,
117  * everything in `h` is a candidate.
118  * Returns the killed cgroup in ret_selected. */
119 int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char **ret_selected);
120 int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected);
121 
122 int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret);
123 int oomd_system_context_acquire(const char *proc_swaps_path, OomdSystemContext *ret);
124 
125 /* Get the OomdCGroupContext of `path` and insert it into `new_h`. The key for the inserted context will be `path`.
126  *
127  * `old_h` is used to get data used to calculate prior interval information. `old_h` can be NULL in which case there
128  * was no prior data to reference. */
129 int oomd_insert_cgroup_context(Hashmap *old_h, Hashmap *new_h, const char *path);
130 
131 /* Update each OomdCGroupContext in `curr_h` with prior interval information from `old_h`. */
132 void oomd_update_cgroup_contexts_between_hashmaps(Hashmap *old_h, Hashmap *curr_h);
133 
134 void oomd_dump_swap_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
135 void oomd_dump_memory_pressure_cgroup_context(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
136 void oomd_dump_system_context(const OomdSystemContext *ctx, FILE *f, const char *prefix);
137