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 | ||
33 | #include <linux/module.h> | |
34 | #include <linux/kernel.h> | |
35 | #include <linux/mm.h> | |
36 | #include <linux/oom.h> | |
37 | #include <linux/sched.h> | |
294b2711 | 38 | #include <linux/rcupdate.h> |
4755b72e SM |
39 | #include <linux/profile.h> |
40 | #include <linux/notifier.h> | |
355b0502 GKH |
41 | |
42 | static uint32_t lowmem_debug_level = 2; | |
43 | static int lowmem_adj[6] = { | |
44 | 0, | |
45 | 1, | |
46 | 6, | |
47 | 12, | |
48 | }; | |
49 | static int lowmem_adj_size = 4; | |
624b2250 | 50 | static int lowmem_minfree[6] = { |
355b0502 GKH |
51 | 3 * 512, /* 6MB */ |
52 | 2 * 1024, /* 8MB */ | |
53 | 4 * 1024, /* 16MB */ | |
54 | 16 * 1024, /* 64MB */ | |
55 | }; | |
56 | static int lowmem_minfree_size = 4; | |
57 | ||
e5d7965f | 58 | static unsigned long lowmem_deathpending_timeout; |
4755b72e | 59 | |
355b0502 GKH |
60 | #define lowmem_print(level, x...) \ |
61 | do { \ | |
62 | if (lowmem_debug_level >= (level)) \ | |
63 | printk(x); \ | |
64 | } while (0) | |
65 | ||
cae9bf11 | 66 | static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) |
355b0502 | 67 | { |
95670001 | 68 | struct task_struct *tsk; |
355b0502 GKH |
69 | struct task_struct *selected = NULL; |
70 | int rem = 0; | |
71 | int tasksize; | |
72 | int i; | |
940f77b0 | 73 | int min_score_adj = OOM_SCORE_ADJ_MAX + 1; |
355b0502 | 74 | int selected_tasksize = 0; |
940f77b0 | 75 | int selected_oom_score_adj; |
355b0502 GKH |
76 | int array_size = ARRAY_SIZE(lowmem_adj); |
77 | int other_free = global_page_state(NR_FREE_PAGES); | |
71b2c82b AH |
78 | int other_file = global_page_state(NR_FILE_PAGES) - |
79 | global_page_state(NR_SHMEM); | |
355b0502 GKH |
80 | |
81 | if (lowmem_adj_size < array_size) | |
82 | array_size = lowmem_adj_size; | |
83 | if (lowmem_minfree_size < array_size) | |
84 | array_size = lowmem_minfree_size; | |
85 | for (i = 0; i < array_size; i++) { | |
86 | if (other_free < lowmem_minfree[i] && | |
87 | other_file < lowmem_minfree[i]) { | |
940f77b0 | 88 | min_score_adj = lowmem_adj[i]; |
355b0502 GKH |
89 | break; |
90 | } | |
91 | } | |
cae9bf11 CC |
92 | if (sc->nr_to_scan > 0) |
93 | lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n", | |
3bf5d65f | 94 | sc->nr_to_scan, sc->gfp_mask, other_free, |
940f77b0 | 95 | other_file, min_score_adj); |
355b0502 GKH |
96 | rem = global_page_state(NR_ACTIVE_ANON) + |
97 | global_page_state(NR_ACTIVE_FILE) + | |
98 | global_page_state(NR_INACTIVE_ANON) + | |
99 | global_page_state(NR_INACTIVE_FILE); | |
940f77b0 | 100 | if (sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) { |
cae9bf11 CC |
101 | lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n", |
102 | sc->nr_to_scan, sc->gfp_mask, rem); | |
355b0502 GKH |
103 | return rem; |
104 | } | |
940f77b0 | 105 | selected_oom_score_adj = min_score_adj; |
355b0502 | 106 | |
294b2711 | 107 | rcu_read_lock(); |
95670001 AV |
108 | for_each_process(tsk) { |
109 | struct task_struct *p; | |
940f77b0 | 110 | int oom_score_adj; |
355b0502 | 111 | |
9823ec9d | 112 | if (tsk->flags & PF_KTHREAD) |
355b0502 | 113 | continue; |
9823ec9d | 114 | |
95670001 AV |
115 | p = find_lock_task_mm(tsk); |
116 | if (!p) | |
117 | continue; | |
118 | ||
83dbbdbb DR |
119 | if (test_tsk_thread_flag(p, TIF_MEMDIE) && |
120 | time_before_eq(jiffies, lowmem_deathpending_timeout)) { | |
121 | task_unlock(p); | |
122 | rcu_read_unlock(); | |
123 | return 0; | |
124 | } | |
940f77b0 DR |
125 | oom_score_adj = p->signal->oom_score_adj; |
126 | if (oom_score_adj < min_score_adj) { | |
355b0502 GKH |
127 | task_unlock(p); |
128 | continue; | |
129 | } | |
95670001 | 130 | tasksize = get_mm_rss(p->mm); |
355b0502 GKH |
131 | task_unlock(p); |
132 | if (tasksize <= 0) | |
133 | continue; | |
134 | if (selected) { | |
940f77b0 | 135 | if (oom_score_adj < selected_oom_score_adj) |
355b0502 | 136 | continue; |
940f77b0 | 137 | if (oom_score_adj == selected_oom_score_adj && |
355b0502 GKH |
138 | tasksize <= selected_tasksize) |
139 | continue; | |
140 | } | |
141 | selected = p; | |
142 | selected_tasksize = tasksize; | |
940f77b0 | 143 | selected_oom_score_adj = oom_score_adj; |
355b0502 | 144 | lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", |
940f77b0 | 145 | p->pid, p->comm, oom_score_adj, tasksize); |
355b0502 GKH |
146 | } |
147 | if (selected) { | |
148 | lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", | |
149 | selected->pid, selected->comm, | |
940f77b0 | 150 | selected_oom_score_adj, selected_tasksize); |
e5d7965f | 151 | lowmem_deathpending_timeout = jiffies + HZ; |
294b2711 | 152 | send_sig(SIGKILL, selected, 0); |
83dbbdbb | 153 | set_tsk_thread_flag(selected, TIF_MEMDIE); |
355b0502 GKH |
154 | rem -= selected_tasksize; |
155 | } | |
cae9bf11 CC |
156 | lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", |
157 | sc->nr_to_scan, sc->gfp_mask, rem); | |
294b2711 | 158 | rcu_read_unlock(); |
355b0502 GKH |
159 | return rem; |
160 | } | |
161 | ||
162 | static struct shrinker lowmem_shrinker = { | |
163 | .shrink = lowmem_shrink, | |
164 | .seeks = DEFAULT_SEEKS * 16 | |
165 | }; | |
166 | ||
167 | static int __init lowmem_init(void) | |
168 | { | |
169 | register_shrinker(&lowmem_shrinker); | |
170 | return 0; | |
171 | } | |
172 | ||
173 | static void __exit lowmem_exit(void) | |
174 | { | |
175 | unregister_shrinker(&lowmem_shrinker); | |
176 | } | |
177 | ||
178 | module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); | |
179 | module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, | |
180 | S_IRUGO | S_IWUSR); | |
181 | module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, | |
182 | S_IRUGO | S_IWUSR); | |
183 | module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); | |
184 | ||
185 | module_init(lowmem_init); | |
186 | module_exit(lowmem_exit); | |
187 | ||
188 | MODULE_LICENSE("GPL"); | |
189 |