UPSTREAM: USB: option: Updated Huawei K4605 has better id
[cascardo/linux.git] / mm / low-mem-notify.c
1 /*
2  * mm/low-mem-notify.c
3  *
4  * Sends low-memory notifications to processes via /dev/low-mem.
5  *
6  * Copyright (C) 2012 The Chromium OS Authors
7  * This program is free software, released under the GPL.
8  * Based on a proposal by Minchan Kim
9  *
10  * A process that polls /dev/low-mem is notified of a low-memory situation.
11  * The intent is to allow the process to free some memory before the OOM killer
12  * is invoked.
13  *
14  * A low-memory condition is estimated by subtracting anonymous memory
15  * (i.e. process data segments), kernel memory, and a fixed amount of
16  * file-backed memory from total memory.  This is just a heuristic, as in
17  * general we don't know how much memory can be reclaimed before we try to
18  * reclaim it, and that's too expensive or too late.
19  *
20  * This is tailored to Chromium OS, where a single program (the browser)
21  * controls most of the memory, and (currently) no swap space is used.
22  */
23
24
25 #include <linux/low-mem-notify.h>
26 #include <linux/module.h>
27 #include <linux/sched.h>
28 #include <linux/wait.h>
29 #include <linux/poll.h>
30 #include <linux/slab.h>
31 #include <linux/mm.h>
32
33 static DECLARE_WAIT_QUEUE_HEAD(low_mem_wait);
34 static atomic_t low_mem_state = ATOMIC_INIT(0);
35 unsigned low_mem_margin_mb = 50;
36 bool low_mem_margin_enabled = true;
37 unsigned long low_mem_minfree;
38 /*
39  * We're interested in worst-case anon memory usage when the low-memory
40  * notification fires.  To contain logging, we limit our interest to
41  * non-trivial steps.
42  */
43 unsigned long low_mem_lowest_seen_anon_mem;
44 const unsigned long low_mem_anon_mem_delta = 10 * 1024 * 1024 / PAGE_SIZE;
45
46 struct low_mem_notify_file_info {
47         unsigned long unused;
48 };
49
50 void low_mem_notify(void)
51 {
52         atomic_set(&low_mem_state, true);
53         wake_up(&low_mem_wait);
54 }
55
56 static int low_mem_notify_open(struct inode *inode, struct file *file)
57 {
58         struct low_mem_notify_file_info *info;
59         int err = 0;
60
61         info = kmalloc(sizeof(*info), GFP_KERNEL);
62         if (!info) {
63                 err = -ENOMEM;
64                 goto out;
65         }
66
67         file->private_data = info;
68 out:
69         return err;
70 }
71
72 static int low_mem_notify_release(struct inode *inode, struct file *file)
73 {
74         kfree(file->private_data);
75         return 0;
76 }
77
78 static unsigned int low_mem_notify_poll(struct file *file, poll_table *wait)
79 {
80         unsigned int ret = 0;
81
82         /* Update state to reflect any recent freeing. */
83         atomic_set(&low_mem_state, is_low_mem_situation());
84
85         poll_wait(file, &low_mem_wait, wait);
86
87         if (low_mem_margin_enabled && atomic_read(&low_mem_state) != 0)
88                 ret = POLLIN;
89
90         return ret;
91 }
92
93 const struct file_operations low_mem_notify_fops = {
94         .open = low_mem_notify_open,
95         .release = low_mem_notify_release,
96         .poll = low_mem_notify_poll,
97 };
98 EXPORT_SYMBOL(low_mem_notify_fops);
99
100 #ifdef CONFIG_SYSFS
101
102 #define LOW_MEM_ATTR(_name)                                   \
103         static struct kobj_attribute low_mem_##_name##_attr = \
104                 __ATTR(_name, 0644, low_mem_##_name##_show,   \
105                        low_mem_##_name##_store)
106
107 static ssize_t low_mem_margin_show(struct kobject *kobj,
108                                   struct kobj_attribute *attr, char *buf)
109 {
110         if (low_mem_margin_enabled)
111                 return sprintf(buf, "%u\n", low_mem_margin_mb);
112         else
113                 return sprintf(buf, "off\n");
114 }
115
116 static unsigned low_mem_margin_to_minfree(unsigned margin_mb)
117 {
118         return margin_mb * (1024 * 1024 / PAGE_SIZE);
119 }
120
121 static ssize_t low_mem_margin_store(struct kobject *kobj,
122                                     struct kobj_attribute *attr,
123                                     const char *buf, size_t count)
124 {
125         int err;
126         unsigned long margin;
127         /*
128          * Even though the API does not say anything about this, the string in
129          * buf is zero-terminated (as long as count < PAGE_SIZE) because buf is
130          * a newly allocated zero-filled page.  Most other sysfs handlers rely
131          * on this too.
132          */
133         if (strncmp("off", buf, 3) == 0) {
134                 printk(KERN_INFO "low_mem: disabling notifier\n");
135                 low_mem_margin_enabled = false;
136                 return count;
137         }
138         if (strncmp("on", buf, 2) == 0) {
139                 printk(KERN_INFO "low_mem: enabling notifier\n");
140                 low_mem_margin_enabled = true;
141                 return count;
142         }
143
144         err = strict_strtoul(buf, 10, &margin);
145         if (err)
146                 return -EINVAL;
147         if (margin * ((1024 * 1024) / PAGE_SIZE) > totalram_pages)
148                 return -EINVAL;
149         /* Notify when the "free" memory is below margin megabytes. */
150         low_mem_margin_enabled = true;
151         low_mem_margin_mb = (unsigned int) margin;
152         /* Convert to pages outside the allocator fast path. */
153         low_mem_minfree = low_mem_margin_to_minfree(low_mem_margin_mb);
154         printk(KERN_INFO "low_mem: setting minfree to %lu kB\n",
155                low_mem_minfree * (PAGE_SIZE / 1024));
156         return count;
157 }
158 LOW_MEM_ATTR(margin);
159
160 static struct attribute *low_mem_attrs[] = {
161         &low_mem_margin_attr.attr,
162         NULL,
163 };
164
165 static struct attribute_group low_mem_attr_group = {
166         .attrs = low_mem_attrs,
167         .name = "chromeos-low_mem",
168 };
169
170 static int __init low_mem_init(void)
171 {
172         int err = sysfs_create_group(mm_kobj, &low_mem_attr_group);
173         if (err)
174                 printk(KERN_ERR "low_mem: register sysfs failed\n");
175         low_mem_minfree = low_mem_margin_to_minfree(low_mem_margin_mb);
176         low_mem_lowest_seen_anon_mem = totalram_pages;
177         return err;
178 }
179 module_init(low_mem_init)
180
181 #endif