Commit | Line | Data |
---|---|---|
08b88cc7 | 1 | /* drivers/misc/lowmemorykiller.c |
272bb3d8 DW |
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. | |
08b88cc7 SM |
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> | |
37 | ||
08b88cc7 SM |
38 | static uint32_t lowmem_debug_level = 2; |
39 | static int lowmem_adj[6] = { | |
40 | 0, | |
41 | 1, | |
42 | 6, | |
43 | 12, | |
44 | }; | |
45 | static int lowmem_adj_size = 4; | |
46 | static size_t lowmem_minfree[6] = { | |
1dbb5765 GKH |
47 | 3 * 512, /* 6MB */ |
48 | 2 * 1024, /* 8MB */ | |
49 | 4 * 1024, /* 16MB */ | |
50 | 16 * 1024, /* 64MB */ | |
08b88cc7 SM |
51 | }; |
52 | static int lowmem_minfree_size = 4; | |
53 | ||
1dbb5765 GKH |
54 | #define lowmem_print(level, x...) \ |
55 | do { \ | |
56 | if (lowmem_debug_level >= (level)) \ | |
57 | printk(x); \ | |
58 | } while (0) | |
08b88cc7 | 59 | |
08b88cc7 SM |
60 | static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask) |
61 | { | |
62 | struct task_struct *p; | |
63 | struct task_struct *selected = NULL; | |
64 | int rem = 0; | |
65 | int tasksize; | |
66 | int i; | |
67 | int min_adj = OOM_ADJUST_MAX + 1; | |
68 | int selected_tasksize = 0; | |
34006e11 | 69 | int selected_oom_adj; |
08b88cc7 | 70 | int array_size = ARRAY_SIZE(lowmem_adj); |
31d59a41 AH |
71 | int other_free = global_page_state(NR_FREE_PAGES); |
72 | int other_file = global_page_state(NR_FILE_PAGES); | |
34006e11 DR |
73 | |
74 | if (lowmem_adj_size < array_size) | |
08b88cc7 | 75 | array_size = lowmem_adj_size; |
34006e11 | 76 | if (lowmem_minfree_size < array_size) |
08b88cc7 | 77 | array_size = lowmem_minfree_size; |
34006e11 | 78 | for (i = 0; i < array_size; i++) { |
31d59a41 AH |
79 | if (other_free < lowmem_minfree[i] && |
80 | other_file < lowmem_minfree[i]) { | |
08b88cc7 SM |
81 | min_adj = lowmem_adj[i]; |
82 | break; | |
83 | } | |
84 | } | |
34006e11 DR |
85 | if (nr_to_scan > 0) |
86 | lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", | |
87 | nr_to_scan, gfp_mask, other_free, other_file, | |
88 | min_adj); | |
31d59a41 AH |
89 | rem = global_page_state(NR_ACTIVE_ANON) + |
90 | global_page_state(NR_ACTIVE_FILE) + | |
91 | global_page_state(NR_INACTIVE_ANON) + | |
92 | global_page_state(NR_INACTIVE_FILE); | |
f501d001 | 93 | if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { |
34006e11 DR |
94 | lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", |
95 | nr_to_scan, gfp_mask, rem); | |
f501d001 AH |
96 | return rem; |
97 | } | |
34006e11 | 98 | selected_oom_adj = min_adj; |
f501d001 | 99 | |
08b88cc7 SM |
100 | read_lock(&tasklist_lock); |
101 | for_each_process(p) { | |
a6a9f81c | 102 | struct mm_struct *mm; |
34006e11 DR |
103 | int oom_adj; |
104 | ||
5d14a573 | 105 | task_lock(p); |
a6a9f81c DR |
106 | mm = p->mm; |
107 | if (!mm) { | |
5d14a573 | 108 | task_unlock(p); |
34006e11 | 109 | continue; |
5d14a573 | 110 | } |
a6a9f81c | 111 | oom_adj = mm->oom_adj; |
5d14a573 DR |
112 | if (oom_adj < min_adj) { |
113 | task_unlock(p); | |
f501d001 | 114 | continue; |
5d14a573 | 115 | } |
a6a9f81c | 116 | tasksize = get_mm_rss(mm); |
5d14a573 | 117 | task_unlock(p); |
f501d001 AH |
118 | if (tasksize <= 0) |
119 | continue; | |
120 | if (selected) { | |
34006e11 | 121 | if (oom_adj < selected_oom_adj) |
f501d001 | 122 | continue; |
34006e11 | 123 | if (oom_adj == selected_oom_adj && |
f501d001 AH |
124 | tasksize <= selected_tasksize) |
125 | continue; | |
08b88cc7 | 126 | } |
f501d001 AH |
127 | selected = p; |
128 | selected_tasksize = tasksize; | |
34006e11 | 129 | selected_oom_adj = oom_adj; |
f501d001 | 130 | lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", |
1dbb5765 | 131 | p->pid, p->comm, oom_adj, tasksize); |
08b88cc7 | 132 | } |
34006e11 | 133 | if (selected) { |
08b88cc7 | 134 | lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", |
1dbb5765 GKH |
135 | selected->pid, selected->comm, |
136 | selected_oom_adj, selected_tasksize); | |
08b88cc7 SM |
137 | force_sig(SIGKILL, selected); |
138 | rem -= selected_tasksize; | |
139 | } | |
34006e11 DR |
140 | lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", |
141 | nr_to_scan, gfp_mask, rem); | |
08b88cc7 SM |
142 | read_unlock(&tasklist_lock); |
143 | return rem; | |
144 | } | |
145 | ||
edd540ea DW |
146 | static struct shrinker lowmem_shrinker = { |
147 | .shrink = lowmem_shrink, | |
148 | .seeks = DEFAULT_SEEKS * 16 | |
149 | }; | |
150 | ||
08b88cc7 SM |
151 | static int __init lowmem_init(void) |
152 | { | |
153 | register_shrinker(&lowmem_shrinker); | |
154 | return 0; | |
155 | } | |
156 | ||
157 | static void __exit lowmem_exit(void) | |
158 | { | |
159 | unregister_shrinker(&lowmem_shrinker); | |
160 | } | |
161 | ||
0984e56a RD |
162 | module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); |
163 | module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, | |
164 | S_IRUGO | S_IWUSR); | |
165 | module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, | |
166 | S_IRUGO | S_IWUSR); | |
167 | module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); | |
168 | ||
08b88cc7 SM |
169 | module_init(lowmem_init); |
170 | module_exit(lowmem_exit); | |
171 | ||
172 | MODULE_LICENSE("GPL"); | |
173 |