Commit | Line | Data |
---|---|---|
355b0502 GKH |
1 | /* drivers/misc/lowmemorykiller.c |
2 | * | |
3 | * The lowmemorykiller driver lets user-space specify a set of memory thresholds | |
940f77b0 DR |
4 | * where processes with a range of oom_score_adj values will get killed. Specify |
5 | * the minimum oom_score_adj values in | |
6 | * /sys/module/lowmemorykiller/parameters/adj and the number of free pages in | |
7 | * /sys/module/lowmemorykiller/parameters/minfree. Both files take a comma | |
8 | * separated list of numbers in ascending order. | |
355b0502 GKH |
9 | * |
10 | * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and | |
3bf5d65f | 11 | * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill |
940f77b0 DR |
12 | * processes with a oom_score_adj value of 8 or higher when the free memory |
13 | * drops below 4096 pages and kill processes with a oom_score_adj value of 0 or | |
14 | * higher when the free memory drops below 1024 pages. | |
355b0502 GKH |
15 | * |
16 | * The driver considers memory used for caches to be free, but if a large | |
17 | * percentage of the cached memory is locked this can be very inaccurate | |
18 | * and processes may not get killed until the normal oom killer is triggered. | |
19 | * | |
20 | * Copyright (C) 2007-2008 Google, Inc. | |
21 | * | |
22 | * This software is licensed under the terms of the GNU General Public | |
23 | * License version 2, as published by the Free Software Foundation, and | |
24 | * may be copied, distributed, and modified under those terms. | |
25 | * | |
26 | * This program is distributed in the hope that it will be useful, | |
27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
29 | * GNU General Public License for more details. | |
30 | * | |
31 | */ | |
32 | ||
6b83f915 DV |
33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
34 | ||
355b0502 GKH |
35 | #include <linux/module.h> |
36 | #include <linux/kernel.h> | |
37 | #include <linux/mm.h> | |
38 | #include <linux/oom.h> | |
39 | #include <linux/sched.h> | |
eeb0f4f3 | 40 | #include <linux/swap.h> |
294b2711 | 41 | #include <linux/rcupdate.h> |
4755b72e SM |
42 | #include <linux/profile.h> |
43 | #include <linux/notifier.h> | |
355b0502 | 44 | |
99150f6a | 45 | static uint32_t lowmem_debug_level = 1; |
a9c58b90 | 46 | static short lowmem_adj[6] = { |
355b0502 GKH |
47 | 0, |
48 | 1, | |
49 | 6, | |
50 | 12, | |
51 | }; | |
52 | static int lowmem_adj_size = 4; | |
624b2250 | 53 | static int lowmem_minfree[6] = { |
355b0502 GKH |
54 | 3 * 512, /* 6MB */ |
55 | 2 * 1024, /* 8MB */ | |
56 | 4 * 1024, /* 16MB */ | |
57 | 16 * 1024, /* 64MB */ | |
58 | }; | |
59 | static int lowmem_minfree_size = 4; | |
60 | ||
e5d7965f | 61 | static unsigned long lowmem_deathpending_timeout; |
4755b72e | 62 | |
355b0502 GKH |
63 | #define lowmem_print(level, x...) \ |
64 | do { \ | |
65 | if (lowmem_debug_level >= (level)) \ | |
6b83f915 | 66 | pr_info(x); \ |
355b0502 GKH |
67 | } while (0) |
68 | ||
7dc19d5a DC |
69 | static unsigned long lowmem_count(struct shrinker *s, |
70 | struct shrink_control *sc) | |
71 | { | |
72 | return global_page_state(NR_ACTIVE_ANON) + | |
73 | global_page_state(NR_ACTIVE_FILE) + | |
74 | global_page_state(NR_INACTIVE_ANON) + | |
75 | global_page_state(NR_INACTIVE_FILE); | |
76 | } | |
77 | ||
78 | static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) | |
355b0502 | 79 | { |
95670001 | 80 | struct task_struct *tsk; |
355b0502 | 81 | struct task_struct *selected = NULL; |
7dc19d5a | 82 | unsigned long rem = 0; |
355b0502 GKH |
83 | int tasksize; |
84 | int i; | |
a9c58b90 | 85 | short min_score_adj = OOM_SCORE_ADJ_MAX + 1; |
355b0502 | 86 | int selected_tasksize = 0; |
a9c58b90 | 87 | short selected_oom_score_adj; |
355b0502 | 88 | int array_size = ARRAY_SIZE(lowmem_adj); |
eeb0f4f3 | 89 | int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages; |
71b2c82b | 90 | int other_file = global_page_state(NR_FILE_PAGES) - |
058dbde9 VM |
91 | global_page_state(NR_SHMEM) - |
92 | total_swapcache_pages(); | |
355b0502 GKH |
93 | |
94 | if (lowmem_adj_size < array_size) | |
95 | array_size = lowmem_adj_size; | |
96 | if (lowmem_minfree_size < array_size) | |
97 | array_size = lowmem_minfree_size; | |
98 | for (i = 0; i < array_size; i++) { | |
99 | if (other_free < lowmem_minfree[i] && | |
100 | other_file < lowmem_minfree[i]) { | |
940f77b0 | 101 | min_score_adj = lowmem_adj[i]; |
355b0502 GKH |
102 | break; |
103 | } | |
104 | } | |
7dc19d5a DC |
105 | |
106 | lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n", | |
107 | sc->nr_to_scan, sc->gfp_mask, other_free, | |
108 | other_file, min_score_adj); | |
109 | ||
110 | if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) { | |
111 | lowmem_print(5, "lowmem_scan %lu, %x, return 0\n", | |
112 | sc->nr_to_scan, sc->gfp_mask); | |
113 | return 0; | |
355b0502 | 114 | } |
7dc19d5a | 115 | |
940f77b0 | 116 | selected_oom_score_adj = min_score_adj; |
355b0502 | 117 | |
294b2711 | 118 | rcu_read_lock(); |
95670001 AV |
119 | for_each_process(tsk) { |
120 | struct task_struct *p; | |
a9c58b90 | 121 | short oom_score_adj; |
355b0502 | 122 | |
9823ec9d | 123 | if (tsk->flags & PF_KTHREAD) |
355b0502 | 124 | continue; |
9823ec9d | 125 | |
95670001 AV |
126 | p = find_lock_task_mm(tsk); |
127 | if (!p) | |
128 | continue; | |
129 | ||
83dbbdbb DR |
130 | if (test_tsk_thread_flag(p, TIF_MEMDIE) && |
131 | time_before_eq(jiffies, lowmem_deathpending_timeout)) { | |
132 | task_unlock(p); | |
133 | rcu_read_unlock(); | |
134 | return 0; | |
135 | } | |
940f77b0 DR |
136 | oom_score_adj = p->signal->oom_score_adj; |
137 | if (oom_score_adj < min_score_adj) { | |
355b0502 GKH |
138 | task_unlock(p); |
139 | continue; | |
140 | } | |
95670001 | 141 | tasksize = get_mm_rss(p->mm); |
355b0502 GKH |
142 | task_unlock(p); |
143 | if (tasksize <= 0) | |
144 | continue; | |
145 | if (selected) { | |
940f77b0 | 146 | if (oom_score_adj < selected_oom_score_adj) |
355b0502 | 147 | continue; |
940f77b0 | 148 | if (oom_score_adj == selected_oom_score_adj && |
355b0502 GKH |
149 | tasksize <= selected_tasksize) |
150 | continue; | |
151 | } | |
152 | selected = p; | |
153 | selected_tasksize = tasksize; | |
940f77b0 | 154 | selected_oom_score_adj = oom_score_adj; |
a9c58b90 | 155 | lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n", |
940f77b0 | 156 | p->pid, p->comm, oom_score_adj, tasksize); |
355b0502 GKH |
157 | } |
158 | if (selected) { | |
a9c58b90 | 159 | lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n", |
355b0502 | 160 | selected->pid, selected->comm, |
940f77b0 | 161 | selected_oom_score_adj, selected_tasksize); |
e5d7965f | 162 | lowmem_deathpending_timeout = jiffies + HZ; |
49550b60 MH |
163 | /* |
164 | * FIXME: lowmemorykiller shouldn't abuse global OOM killer | |
165 | * infrastructure. There is no real reason why the selected | |
166 | * task should have access to the memory reserves. | |
167 | */ | |
168 | mark_tsk_oom_victim(selected); | |
6bc2b856 | 169 | send_sig(SIGKILL, selected, 0); |
7dc19d5a | 170 | rem += selected_tasksize; |
355b0502 | 171 | } |
7dc19d5a DC |
172 | |
173 | lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n", | |
cae9bf11 | 174 | sc->nr_to_scan, sc->gfp_mask, rem); |
294b2711 | 175 | rcu_read_unlock(); |
355b0502 GKH |
176 | return rem; |
177 | } | |
178 | ||
179 | static struct shrinker lowmem_shrinker = { | |
7dc19d5a DC |
180 | .scan_objects = lowmem_scan, |
181 | .count_objects = lowmem_count, | |
355b0502 GKH |
182 | .seeks = DEFAULT_SEEKS * 16 |
183 | }; | |
184 | ||
185 | static int __init lowmem_init(void) | |
186 | { | |
187 | register_shrinker(&lowmem_shrinker); | |
188 | return 0; | |
189 | } | |
190 | ||
191 | static void __exit lowmem_exit(void) | |
192 | { | |
193 | unregister_shrinker(&lowmem_shrinker); | |
194 | } | |
195 | ||
196 | module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); | |
a9c58b90 | 197 | module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size, |
355b0502 GKH |
198 | S_IRUGO | S_IWUSR); |
199 | module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, | |
200 | S_IRUGO | S_IWUSR); | |
201 | module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); | |
202 | ||
203 | module_init(lowmem_init); | |
204 | module_exit(lowmem_exit); | |
205 | ||
206 | MODULE_LICENSE("GPL"); | |
207 |