CHROMIUM: fix low-memory notification patch (change Iec1eb499)
[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_percent = 10;
36 unsigned long low_mem_minfree;
37
38 struct low_mem_notify_file_info {
39         unsigned long unused;
40 };
41
42 void low_mem_notify(void)
43 {
44         atomic_set(&low_mem_state, true);
45         wake_up(&low_mem_wait);
46 }
47
48 static int low_mem_notify_open(struct inode *inode, struct file *file)
49 {
50         struct low_mem_notify_file_info *info;
51         int err = 0;
52
53         info = kmalloc(sizeof(*info), GFP_KERNEL);
54         if (!info) {
55                 err = -ENOMEM;
56                 goto out;
57         }
58
59         file->private_data = info;
60 out:
61         return err;
62 }
63
64 static int low_mem_notify_release(struct inode *inode, struct file *file)
65 {
66         kfree(file->private_data);
67         return 0;
68 }
69
70 static unsigned int low_mem_notify_poll(struct file *file, poll_table *wait)
71 {
72         unsigned int ret = 0;
73
74         /* Update state to reflect any recent freeing. */
75         atomic_set(&low_mem_state, is_low_mem_situation());
76
77         poll_wait(file, &low_mem_wait, wait);
78
79         if (atomic_read(&low_mem_state) != 0)
80                 ret = POLLIN;
81
82         return ret;
83 }
84
85 const struct file_operations low_mem_notify_fops = {
86         .open = low_mem_notify_open,
87         .release = low_mem_notify_release,
88         .poll = low_mem_notify_poll,
89 };
90 EXPORT_SYMBOL(low_mem_notify_fops);
91
92 #ifdef CONFIG_SYSFS
93
94 #define LOW_MEM_ATTR(_name)                                   \
95         static struct kobj_attribute low_mem_##_name##_attr = \
96                 __ATTR(_name, 0644, low_mem_##_name##_show,   \
97                        low_mem_##_name##_store)
98
99 static ssize_t low_mem_margin_show(struct kobject *kobj,
100                                   struct kobj_attribute *attr, char *buf)
101 {
102         return sprintf(buf, "%u\n", low_mem_margin_percent);
103 }
104
105 static unsigned low_mem_margin_to_minfree(unsigned percent)
106 {
107         return percent * totalram_pages / 100;
108 }
109
110 static ssize_t low_mem_margin_store(struct kobject *kobj,
111                                     struct kobj_attribute *attr,
112                                     const char *buf, size_t count)
113 {
114         int err;
115         unsigned long margin;
116
117         err = strict_strtoul(buf, 10, &margin);
118         if (err || margin > 99)
119                 return -EINVAL;
120         /* Notify when the percentage of "free" memory is below margin. */
121         low_mem_margin_percent = (unsigned int) margin;
122         /* Precompute as much as possible outside the allocator fast path. */
123         low_mem_minfree = low_mem_margin_to_minfree(low_mem_margin_percent);
124         printk(KERN_INFO "low_mem: setting minfree to %lu\n", low_mem_minfree);
125         return count;
126 }
127 LOW_MEM_ATTR(margin);
128
129 static struct attribute *low_mem_attrs[] = {
130         &low_mem_margin_attr.attr,
131         NULL,
132 };
133
134 static struct attribute_group low_mem_attr_group = {
135         .attrs = low_mem_attrs,
136         .name = "low_mem",
137 };
138
139 static int __init low_mem_init(void)
140 {
141         int err = sysfs_create_group(mm_kobj, &low_mem_attr_group);
142         if (err)
143                 printk(KERN_ERR "low_mem: register sysfs failed\n");
144         low_mem_minfree = low_mem_margin_to_minfree(low_mem_margin_percent);
145         return err;
146 }
147 module_init(low_mem_init)
148
149 #endif