Commit | Line | Data |
---|---|---|
08b88cc7 SM |
1 | /* drivers/misc/lowmemorykiller.c |
2 | * | |
3 | * Copyright (C) 2007-2008 Google, Inc. | |
4 | * | |
5 | * This software is licensed under the terms of the GNU General Public | |
6 | * License version 2, as published by the Free Software Foundation, and | |
7 | * may be copied, distributed, and modified under those terms. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/module.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/mm.h> | |
19 | #include <linux/oom.h> | |
20 | #include <linux/sched.h> | |
21 | ||
22 | static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask); | |
23 | ||
24 | static struct shrinker lowmem_shrinker = { | |
25 | .shrink = lowmem_shrink, | |
26 | .seeks = DEFAULT_SEEKS * 16 | |
27 | }; | |
28 | static uint32_t lowmem_debug_level = 2; | |
29 | static int lowmem_adj[6] = { | |
30 | 0, | |
31 | 1, | |
32 | 6, | |
33 | 12, | |
34 | }; | |
35 | static int lowmem_adj_size = 4; | |
36 | static size_t lowmem_minfree[6] = { | |
37 | 3*512, // 6MB | |
38 | 2*1024, // 8MB | |
39 | 4*1024, // 16MB | |
40 | 16*1024, // 64MB | |
41 | }; | |
42 | static int lowmem_minfree_size = 4; | |
43 | ||
44 | #define lowmem_print(level, x...) do { if(lowmem_debug_level >= (level)) printk(x); } while(0) | |
45 | ||
46 | module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); | |
47 | module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, S_IRUGO | S_IWUSR); | |
48 | module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, S_IRUGO | S_IWUSR); | |
49 | module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); | |
50 | ||
51 | static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask) | |
52 | { | |
53 | struct task_struct *p; | |
54 | struct task_struct *selected = NULL; | |
55 | int rem = 0; | |
56 | int tasksize; | |
57 | int i; | |
58 | int min_adj = OOM_ADJUST_MAX + 1; | |
59 | int selected_tasksize = 0; | |
34006e11 | 60 | int selected_oom_adj; |
08b88cc7 | 61 | int array_size = ARRAY_SIZE(lowmem_adj); |
31d59a41 AH |
62 | int other_free = global_page_state(NR_FREE_PAGES); |
63 | int other_file = global_page_state(NR_FILE_PAGES); | |
34006e11 DR |
64 | |
65 | if (lowmem_adj_size < array_size) | |
08b88cc7 | 66 | array_size = lowmem_adj_size; |
34006e11 | 67 | if (lowmem_minfree_size < array_size) |
08b88cc7 | 68 | array_size = lowmem_minfree_size; |
34006e11 | 69 | for (i = 0; i < array_size; i++) { |
31d59a41 AH |
70 | if (other_free < lowmem_minfree[i] && |
71 | other_file < lowmem_minfree[i]) { | |
08b88cc7 SM |
72 | min_adj = lowmem_adj[i]; |
73 | break; | |
74 | } | |
75 | } | |
34006e11 DR |
76 | if (nr_to_scan > 0) |
77 | lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", | |
78 | nr_to_scan, gfp_mask, other_free, other_file, | |
79 | min_adj); | |
31d59a41 AH |
80 | rem = global_page_state(NR_ACTIVE_ANON) + |
81 | global_page_state(NR_ACTIVE_FILE) + | |
82 | global_page_state(NR_INACTIVE_ANON) + | |
83 | global_page_state(NR_INACTIVE_FILE); | |
f501d001 | 84 | if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { |
34006e11 DR |
85 | lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", |
86 | nr_to_scan, gfp_mask, rem); | |
f501d001 AH |
87 | return rem; |
88 | } | |
34006e11 | 89 | selected_oom_adj = min_adj; |
f501d001 | 90 | |
08b88cc7 SM |
91 | read_lock(&tasklist_lock); |
92 | for_each_process(p) { | |
34006e11 DR |
93 | int oom_adj; |
94 | ||
5d14a573 DR |
95 | task_lock(p); |
96 | if (!p->mm) { | |
97 | task_unlock(p); | |
34006e11 | 98 | continue; |
5d14a573 | 99 | } |
34006e11 | 100 | oom_adj = p->oomkilladj; |
5d14a573 DR |
101 | if (oom_adj < min_adj) { |
102 | task_unlock(p); | |
f501d001 | 103 | continue; |
5d14a573 | 104 | } |
f501d001 | 105 | tasksize = get_mm_rss(p->mm); |
5d14a573 | 106 | task_unlock(p); |
f501d001 AH |
107 | if (tasksize <= 0) |
108 | continue; | |
109 | if (selected) { | |
34006e11 | 110 | if (oom_adj < selected_oom_adj) |
f501d001 | 111 | continue; |
34006e11 | 112 | if (oom_adj == selected_oom_adj && |
f501d001 AH |
113 | tasksize <= selected_tasksize) |
114 | continue; | |
08b88cc7 | 115 | } |
f501d001 AH |
116 | selected = p; |
117 | selected_tasksize = tasksize; | |
34006e11 | 118 | selected_oom_adj = oom_adj; |
f501d001 | 119 | lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", |
34006e11 | 120 | p->pid, p->comm, oom_adj, tasksize); |
08b88cc7 | 121 | } |
34006e11 | 122 | if (selected) { |
08b88cc7 SM |
123 | lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", |
124 | selected->pid, selected->comm, | |
34006e11 | 125 | selected_oom_adj, selected_tasksize); |
08b88cc7 SM |
126 | force_sig(SIGKILL, selected); |
127 | rem -= selected_tasksize; | |
128 | } | |
34006e11 DR |
129 | lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", |
130 | nr_to_scan, gfp_mask, rem); | |
08b88cc7 SM |
131 | read_unlock(&tasklist_lock); |
132 | return rem; | |
133 | } | |
134 | ||
135 | static int __init lowmem_init(void) | |
136 | { | |
137 | register_shrinker(&lowmem_shrinker); | |
138 | return 0; | |
139 | } | |
140 | ||
141 | static void __exit lowmem_exit(void) | |
142 | { | |
143 | unregister_shrinker(&lowmem_shrinker); | |
144 | } | |
145 | ||
146 | module_init(lowmem_init); | |
147 | module_exit(lowmem_exit); | |
148 | ||
149 | MODULE_LICENSE("GPL"); | |
150 |