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 | ||
380b6549 PG |
35 | #include <linux/init.h> |
36 | #include <linux/moduleparam.h> | |
355b0502 GKH |
37 | #include <linux/kernel.h> |
38 | #include <linux/mm.h> | |
39 | #include <linux/oom.h> | |
40 | #include <linux/sched.h> | |
eeb0f4f3 | 41 | #include <linux/swap.h> |
294b2711 | 42 | #include <linux/rcupdate.h> |
4755b72e SM |
43 | #include <linux/profile.h> |
44 | #include <linux/notifier.h> | |
355b0502 | 45 | |
36f16ff2 | 46 | static u32 lowmem_debug_level = 1; |
a9c58b90 | 47 | static short lowmem_adj[6] = { |
355b0502 GKH |
48 | 0, |
49 | 1, | |
50 | 6, | |
51 | 12, | |
52 | }; | |
53 | static int lowmem_adj_size = 4; | |
624b2250 | 54 | static int lowmem_minfree[6] = { |
355b0502 GKH |
55 | 3 * 512, /* 6MB */ |
56 | 2 * 1024, /* 8MB */ | |
57 | 4 * 1024, /* 16MB */ | |
58 | 16 * 1024, /* 64MB */ | |
59 | }; | |
60 | static int lowmem_minfree_size = 4; | |
61 | ||
e5d7965f | 62 | static unsigned long lowmem_deathpending_timeout; |
4755b72e | 63 | |
355b0502 GKH |
64 | #define lowmem_print(level, x...) \ |
65 | do { \ | |
66 | if (lowmem_debug_level >= (level)) \ | |
6b83f915 | 67 | pr_info(x); \ |
355b0502 GKH |
68 | } while (0) |
69 | ||
7dc19d5a DC |
70 | static unsigned long lowmem_count(struct shrinker *s, |
71 | struct shrink_control *sc) | |
72 | { | |
73 | return global_page_state(NR_ACTIVE_ANON) + | |
74 | global_page_state(NR_ACTIVE_FILE) + | |
75 | global_page_state(NR_INACTIVE_ANON) + | |
76 | global_page_state(NR_INACTIVE_FILE); | |
77 | } | |
78 | ||
79 | static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) | |
355b0502 | 80 | { |
95670001 | 81 | struct task_struct *tsk; |
355b0502 | 82 | struct task_struct *selected = NULL; |
7dc19d5a | 83 | unsigned long rem = 0; |
355b0502 GKH |
84 | int tasksize; |
85 | int i; | |
a9c58b90 | 86 | short min_score_adj = OOM_SCORE_ADJ_MAX + 1; |
355b0502 | 87 | int selected_tasksize = 0; |
a9c58b90 | 88 | short selected_oom_score_adj; |
355b0502 | 89 | int array_size = ARRAY_SIZE(lowmem_adj); |
eeb0f4f3 | 90 | int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages; |
71b2c82b | 91 | int other_file = global_page_state(NR_FILE_PAGES) - |
058dbde9 VM |
92 | global_page_state(NR_SHMEM) - |
93 | total_swapcache_pages(); | |
355b0502 GKH |
94 | |
95 | if (lowmem_adj_size < array_size) | |
96 | array_size = lowmem_adj_size; | |
97 | if (lowmem_minfree_size < array_size) | |
98 | array_size = lowmem_minfree_size; | |
99 | for (i = 0; i < array_size; i++) { | |
100 | if (other_free < lowmem_minfree[i] && | |
101 | other_file < lowmem_minfree[i]) { | |
940f77b0 | 102 | min_score_adj = lowmem_adj[i]; |
355b0502 GKH |
103 | break; |
104 | } | |
105 | } | |
7dc19d5a DC |
106 | |
107 | lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n", | |
f8b053e3 IC |
108 | sc->nr_to_scan, sc->gfp_mask, other_free, |
109 | other_file, min_score_adj); | |
7dc19d5a DC |
110 | |
111 | if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) { | |
112 | lowmem_print(5, "lowmem_scan %lu, %x, return 0\n", | |
113 | sc->nr_to_scan, sc->gfp_mask); | |
114 | return 0; | |
355b0502 | 115 | } |
7dc19d5a | 116 | |
940f77b0 | 117 | selected_oom_score_adj = min_score_adj; |
355b0502 | 118 | |
294b2711 | 119 | rcu_read_lock(); |
95670001 AV |
120 | for_each_process(tsk) { |
121 | struct task_struct *p; | |
a9c58b90 | 122 | short oom_score_adj; |
355b0502 | 123 | |
9823ec9d | 124 | if (tsk->flags & PF_KTHREAD) |
355b0502 | 125 | continue; |
9823ec9d | 126 | |
95670001 AV |
127 | p = find_lock_task_mm(tsk); |
128 | if (!p) | |
129 | continue; | |
130 | ||
83dbbdbb DR |
131 | if (test_tsk_thread_flag(p, TIF_MEMDIE) && |
132 | time_before_eq(jiffies, lowmem_deathpending_timeout)) { | |
133 | task_unlock(p); | |
134 | rcu_read_unlock(); | |
135 | return 0; | |
136 | } | |
940f77b0 DR |
137 | oom_score_adj = p->signal->oom_score_adj; |
138 | if (oom_score_adj < min_score_adj) { | |
355b0502 GKH |
139 | task_unlock(p); |
140 | continue; | |
141 | } | |
95670001 | 142 | tasksize = get_mm_rss(p->mm); |
355b0502 GKH |
143 | task_unlock(p); |
144 | if (tasksize <= 0) | |
145 | continue; | |
146 | if (selected) { | |
940f77b0 | 147 | if (oom_score_adj < selected_oom_score_adj) |
355b0502 | 148 | continue; |
940f77b0 | 149 | if (oom_score_adj == selected_oom_score_adj && |
355b0502 GKH |
150 | tasksize <= selected_tasksize) |
151 | continue; | |
152 | } | |
153 | selected = p; | |
154 | selected_tasksize = tasksize; | |
940f77b0 | 155 | selected_oom_score_adj = oom_score_adj; |
a9c58b90 | 156 | lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n", |
940f77b0 | 157 | p->pid, p->comm, oom_score_adj, tasksize); |
355b0502 GKH |
158 | } |
159 | if (selected) { | |
e1099a69 | 160 | task_lock(selected); |
3a81fc20 | 161 | send_sig(SIGKILL, selected, 0); |
49550b60 MH |
162 | /* |
163 | * FIXME: lowmemorykiller shouldn't abuse global OOM killer | |
164 | * infrastructure. There is no real reason why the selected | |
165 | * task should have access to the memory reserves. | |
166 | */ | |
3a81fc20 TH |
167 | if (selected->mm) |
168 | mark_oom_victim(selected); | |
e1099a69 DR |
169 | task_unlock(selected); |
170 | lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n", | |
171 | selected->pid, selected->comm, | |
172 | selected_oom_score_adj, selected_tasksize); | |
173 | lowmem_deathpending_timeout = jiffies + HZ; | |
7dc19d5a | 174 | rem += selected_tasksize; |
355b0502 | 175 | } |
3a81fc20 | 176 | |
7dc19d5a | 177 | lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n", |
cae9bf11 | 178 | sc->nr_to_scan, sc->gfp_mask, rem); |
294b2711 | 179 | rcu_read_unlock(); |
355b0502 GKH |
180 | return rem; |
181 | } | |
182 | ||
183 | static struct shrinker lowmem_shrinker = { | |
7dc19d5a DC |
184 | .scan_objects = lowmem_scan, |
185 | .count_objects = lowmem_count, | |
355b0502 GKH |
186 | .seeks = DEFAULT_SEEKS * 16 |
187 | }; | |
188 | ||
189 | static int __init lowmem_init(void) | |
190 | { | |
191 | register_shrinker(&lowmem_shrinker); | |
192 | return 0; | |
193 | } | |
380b6549 | 194 | device_initcall(lowmem_init); |
355b0502 | 195 | |
380b6549 PG |
196 | /* |
197 | * not really modular, but the easiest way to keep compat with existing | |
198 | * bootargs behaviour is to continue using module_param here. | |
199 | */ | |
355b0502 | 200 | module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); |
a9c58b90 | 201 | module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size, |
355b0502 GKH |
202 | S_IRUGO | S_IWUSR); |
203 | module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, | |
204 | S_IRUGO | S_IWUSR); | |
205 | module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); | |
206 |