Commit | Line | Data |
---|---|---|
355b0502 GKH |
1 | /* drivers/misc/lowmemorykiller.c |
2 | * | |
3 | * The lowmemorykiller driver lets user-space specify a set of memory thresholds | |
4 | * where processes with a range of oom_adj values will get killed. Specify the | |
5 | * minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the | |
6 | * number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both | |
7 | * files take a comma separated list of numbers in ascending order. | |
8 | * | |
9 | * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and | |
10 | * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes | |
11 | * with a oom_adj value of 8 or higher when the free memory drops below 4096 pages | |
12 | * and kill processes with a oom_adj value of 0 or higher when the free memory | |
13 | * drops below 1024 pages. | |
14 | * | |
15 | * The driver considers memory used for caches to be free, but if a large | |
16 | * percentage of the cached memory is locked this can be very inaccurate | |
17 | * and processes may not get killed until the normal oom killer is triggered. | |
18 | * | |
19 | * Copyright (C) 2007-2008 Google, Inc. | |
20 | * | |
21 | * This software is licensed under the terms of the GNU General Public | |
22 | * License version 2, as published by the Free Software Foundation, and | |
23 | * may be copied, distributed, and modified under those terms. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | */ | |
31 | ||
32 | #include <linux/module.h> | |
33 | #include <linux/kernel.h> | |
34 | #include <linux/mm.h> | |
35 | #include <linux/oom.h> | |
36 | #include <linux/sched.h> | |
4755b72e SM |
37 | #include <linux/profile.h> |
38 | #include <linux/notifier.h> | |
355b0502 GKH |
39 | |
40 | static uint32_t lowmem_debug_level = 2; | |
41 | static int lowmem_adj[6] = { | |
42 | 0, | |
43 | 1, | |
44 | 6, | |
45 | 12, | |
46 | }; | |
47 | static int lowmem_adj_size = 4; | |
48 | static size_t lowmem_minfree[6] = { | |
49 | 3 * 512, /* 6MB */ | |
50 | 2 * 1024, /* 8MB */ | |
51 | 4 * 1024, /* 16MB */ | |
52 | 16 * 1024, /* 64MB */ | |
53 | }; | |
54 | static int lowmem_minfree_size = 4; | |
55 | ||
4755b72e SM |
56 | static struct task_struct *lowmem_deathpending; |
57 | ||
355b0502 GKH |
58 | #define lowmem_print(level, x...) \ |
59 | do { \ | |
60 | if (lowmem_debug_level >= (level)) \ | |
61 | printk(x); \ | |
62 | } while (0) | |
63 | ||
4755b72e SM |
64 | static int |
65 | task_notify_func(struct notifier_block *self, unsigned long val, void *data); | |
66 | ||
67 | static struct notifier_block task_nb = { | |
68 | .notifier_call = task_notify_func, | |
69 | }; | |
70 | ||
71 | static int | |
72 | task_notify_func(struct notifier_block *self, unsigned long val, void *data) | |
73 | { | |
74 | struct task_struct *task = data; | |
75 | if (task == lowmem_deathpending) { | |
76 | lowmem_deathpending = NULL; | |
77 | task_handoff_unregister(&task_nb); | |
78 | } | |
79 | return NOTIFY_OK; | |
80 | } | |
81 | ||
cae9bf11 | 82 | static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) |
355b0502 GKH |
83 | { |
84 | struct task_struct *p; | |
85 | struct task_struct *selected = NULL; | |
86 | int rem = 0; | |
87 | int tasksize; | |
88 | int i; | |
89 | int min_adj = OOM_ADJUST_MAX + 1; | |
90 | int selected_tasksize = 0; | |
91 | int selected_oom_adj; | |
92 | int array_size = ARRAY_SIZE(lowmem_adj); | |
93 | int other_free = global_page_state(NR_FREE_PAGES); | |
71b2c82b AH |
94 | int other_file = global_page_state(NR_FILE_PAGES) - |
95 | global_page_state(NR_SHMEM); | |
355b0502 | 96 | |
4755b72e SM |
97 | /* |
98 | * If we already have a death outstanding, then | |
99 | * bail out right away; indicating to vmscan | |
100 | * that we have nothing further to offer on | |
101 | * this pass. | |
102 | * | |
103 | * Note: Currently you need CONFIG_PROFILING | |
104 | * for this to work correctly. | |
105 | */ | |
106 | if (lowmem_deathpending) | |
107 | return 0; | |
108 | ||
355b0502 GKH |
109 | if (lowmem_adj_size < array_size) |
110 | array_size = lowmem_adj_size; | |
111 | if (lowmem_minfree_size < array_size) | |
112 | array_size = lowmem_minfree_size; | |
113 | for (i = 0; i < array_size; i++) { | |
114 | if (other_free < lowmem_minfree[i] && | |
115 | other_file < lowmem_minfree[i]) { | |
116 | min_adj = lowmem_adj[i]; | |
117 | break; | |
118 | } | |
119 | } | |
cae9bf11 CC |
120 | if (sc->nr_to_scan > 0) |
121 | lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n", | |
122 | sc->nr_to_scan, sc->gfp_mask, other_free, other_file, | |
355b0502 GKH |
123 | min_adj); |
124 | rem = global_page_state(NR_ACTIVE_ANON) + | |
125 | global_page_state(NR_ACTIVE_FILE) + | |
126 | global_page_state(NR_INACTIVE_ANON) + | |
127 | global_page_state(NR_INACTIVE_FILE); | |
cae9bf11 CC |
128 | if (sc->nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { |
129 | lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n", | |
130 | sc->nr_to_scan, sc->gfp_mask, rem); | |
355b0502 GKH |
131 | return rem; |
132 | } | |
133 | selected_oom_adj = min_adj; | |
134 | ||
135 | read_lock(&tasklist_lock); | |
136 | for_each_process(p) { | |
137 | struct mm_struct *mm; | |
23687af9 | 138 | struct signal_struct *sig; |
355b0502 GKH |
139 | int oom_adj; |
140 | ||
141 | task_lock(p); | |
142 | mm = p->mm; | |
23687af9 CC |
143 | sig = p->signal; |
144 | if (!mm || !sig) { | |
355b0502 GKH |
145 | task_unlock(p); |
146 | continue; | |
147 | } | |
23687af9 | 148 | oom_adj = sig->oom_adj; |
355b0502 GKH |
149 | if (oom_adj < min_adj) { |
150 | task_unlock(p); | |
151 | continue; | |
152 | } | |
153 | tasksize = get_mm_rss(mm); | |
154 | task_unlock(p); | |
155 | if (tasksize <= 0) | |
156 | continue; | |
157 | if (selected) { | |
158 | if (oom_adj < selected_oom_adj) | |
159 | continue; | |
160 | if (oom_adj == selected_oom_adj && | |
161 | tasksize <= selected_tasksize) | |
162 | continue; | |
163 | } | |
164 | selected = p; | |
165 | selected_tasksize = tasksize; | |
166 | selected_oom_adj = oom_adj; | |
167 | lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", | |
168 | p->pid, p->comm, oom_adj, tasksize); | |
169 | } | |
170 | if (selected) { | |
171 | lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", | |
172 | selected->pid, selected->comm, | |
173 | selected_oom_adj, selected_tasksize); | |
4755b72e SM |
174 | /* |
175 | * If CONFIG_PROFILING is off, then task_handoff_register() | |
176 | * is a nop. In that case we don't want to stall the killer | |
177 | * by setting lowmem_deathpending. | |
178 | */ | |
179 | #ifdef CONFIG_PROFILING | |
180 | lowmem_deathpending = selected; | |
181 | task_handoff_register(&task_nb); | |
182 | #endif | |
355b0502 GKH |
183 | force_sig(SIGKILL, selected); |
184 | rem -= selected_tasksize; | |
185 | } | |
cae9bf11 CC |
186 | lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", |
187 | sc->nr_to_scan, sc->gfp_mask, rem); | |
355b0502 GKH |
188 | read_unlock(&tasklist_lock); |
189 | return rem; | |
190 | } | |
191 | ||
192 | static struct shrinker lowmem_shrinker = { | |
193 | .shrink = lowmem_shrink, | |
194 | .seeks = DEFAULT_SEEKS * 16 | |
195 | }; | |
196 | ||
197 | static int __init lowmem_init(void) | |
198 | { | |
199 | register_shrinker(&lowmem_shrinker); | |
200 | return 0; | |
201 | } | |
202 | ||
203 | static void __exit lowmem_exit(void) | |
204 | { | |
205 | unregister_shrinker(&lowmem_shrinker); | |
206 | } | |
207 | ||
208 | module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); | |
209 | module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, | |
210 | S_IRUGO | S_IWUSR); | |
211 | module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, | |
212 | S_IRUGO | S_IWUSR); | |
213 | module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); | |
214 | ||
215 | module_init(lowmem_init); | |
216 | module_exit(lowmem_exit); | |
217 | ||
218 | MODULE_LICENSE("GPL"); | |
219 |