]> git.sesse.net Git - bcachefs-tools-debian/blob - linux/shrinker.c
Update bcachefs sources to 783085c3cc44 kbuild: Allow gcov to be enabled on the comma...
[bcachefs-tools-debian] / linux / shrinker.c
1
2 #include <stdio.h>
3
4 #include <linux/kthread.h>
5 #include <linux/list.h>
6 #include <linux/mm.h>
7 #include <linux/mutex.h>
8 #include <linux/shrinker.h>
9
10 #include "tools-util.h"
11
12 static LIST_HEAD(shrinker_list);
13 static DEFINE_MUTEX(shrinker_lock);
14
15 struct shrinker *shrinker_alloc(unsigned int flags, const char *fmt, ...)
16 {
17         return calloc(sizeof(struct shrinker), 1);
18 }
19
20 int shrinker_register(struct shrinker *shrinker)
21 {
22         mutex_lock(&shrinker_lock);
23         list_add_tail(&shrinker->list, &shrinker_list);
24         mutex_unlock(&shrinker_lock);
25         return 0;
26 }
27
28 void unregister_shrinker(struct shrinker *shrinker)
29 {
30         mutex_lock(&shrinker_lock);
31         list_del(&shrinker->list);
32         mutex_unlock(&shrinker_lock);
33 }
34
35 struct meminfo {
36         u64             total;
37         u64             available;
38 };
39
40 static u64 parse_meminfo_line(const char *line)
41 {
42         u64 v;
43
44         if (sscanf(line, " %llu kB", &v) < 1)
45                 die("sscanf error");
46         return v << 10;
47 }
48
49 void si_meminfo(struct sysinfo *val)
50 {
51         size_t len, n = 0;
52         char *line = NULL;
53         const char *v;
54         FILE *f;
55
56         memset(val, 0, sizeof(*val));
57         val->mem_unit = 1;
58
59         f = fopen("/proc/meminfo", "r");
60         if (!f)
61                 return;
62
63         while ((len = getline(&line, &n, f)) != -1) {
64                 if ((v = strcmp_prefix(line, "MemTotal:")))
65                         val->totalram = parse_meminfo_line(v);
66
67                 if ((v = strcmp_prefix(line, "MemAvailable:")))
68                         val->freeram = parse_meminfo_line(v);
69         }
70
71         fclose(f);
72         free(line);
73 }
74
75 static void run_shrinkers_allocation_failed(gfp_t gfp_mask)
76 {
77         struct shrinker *shrinker;
78
79         mutex_lock(&shrinker_lock);
80         list_for_each_entry(shrinker, &shrinker_list, list) {
81                 struct shrink_control sc = { .gfp_mask  = gfp_mask, };
82
83                 unsigned long have = shrinker->count_objects(shrinker, &sc);
84
85                 sc.nr_to_scan = have / 8;
86
87                 shrinker->scan_objects(shrinker, &sc);
88         }
89         mutex_unlock(&shrinker_lock);
90 }
91
92 void run_shrinkers(gfp_t gfp_mask, bool allocation_failed)
93 {
94         struct shrinker *shrinker;
95         struct sysinfo info;
96         s64 want_shrink;
97
98         if (!(gfp_mask & GFP_KERNEL))
99                 return;
100
101         /* Fast out if there are no shrinkers to run. */
102         if (list_empty(&shrinker_list))
103                 return;
104
105         if (allocation_failed) {
106                 run_shrinkers_allocation_failed(gfp_mask);
107                 return;
108         }
109
110         si_meminfo(&info);
111
112         if (info.totalram && info.freeram) {
113                 want_shrink = (info.totalram >> 2) - info.freeram;
114
115                 if (want_shrink <= 0)
116                         return;
117         } else {
118                 /* If we weren't able to read /proc/meminfo, we must be pretty
119                  * low: */
120
121                 want_shrink = 8 << 20;
122         }
123
124         mutex_lock(&shrinker_lock);
125         list_for_each_entry(shrinker, &shrinker_list, list) {
126                 struct shrink_control sc = {
127                         .gfp_mask       = gfp_mask,
128                         .nr_to_scan     = want_shrink >> PAGE_SHIFT
129                 };
130
131                 shrinker->scan_objects(shrinker, &sc);
132         }
133         mutex_unlock(&shrinker_lock);
134 }
135
136 static int shrinker_thread(void *arg)
137 {
138         while (!kthread_should_stop()) {
139                 struct timespec to;
140                 int v;
141
142                 clock_gettime(CLOCK_MONOTONIC, &to);
143                 to.tv_sec += 1;
144                 __set_current_state(TASK_INTERRUPTIBLE);
145                 errno = 0;
146                 while ((v = READ_ONCE(current->state)) != TASK_RUNNING &&
147                        errno != ETIMEDOUT)
148                         futex(&current->state, FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG,
149                               v, &to, NULL, (uint32_t)~0);
150                 if (kthread_should_stop())
151                         break;
152                 if (v != TASK_RUNNING)
153                         __set_current_state(TASK_RUNNING);
154                 run_shrinkers(GFP_KERNEL, false);
155         }
156
157         return 0;
158 }
159
160 struct task_struct *shrinker_task;
161
162 __attribute__((constructor(103)))
163 static void shrinker_thread_init(void)
164 {
165         shrinker_task = kthread_run(shrinker_thread, NULL, "shrinkers");
166         BUG_ON(IS_ERR(shrinker_task));
167 }
168
169 __attribute__((destructor(103)))
170 static void shrinker_thread_exit(void)
171 {
172         int ret = kthread_stop(shrinker_task);
173         BUG_ON(ret);
174
175         shrinker_task = NULL;
176 }