diff options
Diffstat (limited to 'mm/backing-dev.c')
-rw-r--r-- | mm/backing-dev.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/mm/backing-dev.c b/mm/backing-dev.c index e8644b1e5527..7c4f9e097095 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c | |||
@@ -4,12 +4,229 @@ | |||
4 | #include <linux/fs.h> | 4 | #include <linux/fs.h> |
5 | #include <linux/sched.h> | 5 | #include <linux/sched.h> |
6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
7 | #include <linux/writeback.h> | ||
8 | #include <linux/device.h> | ||
9 | |||
10 | |||
11 | static struct class *bdi_class; | ||
12 | |||
13 | #ifdef CONFIG_DEBUG_FS | ||
14 | #include <linux/debugfs.h> | ||
15 | #include <linux/seq_file.h> | ||
16 | |||
17 | static struct dentry *bdi_debug_root; | ||
18 | |||
19 | static void bdi_debug_init(void) | ||
20 | { | ||
21 | bdi_debug_root = debugfs_create_dir("bdi", NULL); | ||
22 | } | ||
23 | |||
24 | static int bdi_debug_stats_show(struct seq_file *m, void *v) | ||
25 | { | ||
26 | struct backing_dev_info *bdi = m->private; | ||
27 | long background_thresh; | ||
28 | long dirty_thresh; | ||
29 | long bdi_thresh; | ||
30 | |||
31 | get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi); | ||
32 | |||
33 | #define K(x) ((x) << (PAGE_SHIFT - 10)) | ||
34 | seq_printf(m, | ||
35 | "BdiWriteback: %8lu kB\n" | ||
36 | "BdiReclaimable: %8lu kB\n" | ||
37 | "BdiDirtyThresh: %8lu kB\n" | ||
38 | "DirtyThresh: %8lu kB\n" | ||
39 | "BackgroundThresh: %8lu kB\n", | ||
40 | (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), | ||
41 | (unsigned long) K(bdi_stat(bdi, BDI_RECLAIMABLE)), | ||
42 | K(bdi_thresh), | ||
43 | K(dirty_thresh), | ||
44 | K(background_thresh)); | ||
45 | #undef K | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static int bdi_debug_stats_open(struct inode *inode, struct file *file) | ||
51 | { | ||
52 | return single_open(file, bdi_debug_stats_show, inode->i_private); | ||
53 | } | ||
54 | |||
55 | static const struct file_operations bdi_debug_stats_fops = { | ||
56 | .open = bdi_debug_stats_open, | ||
57 | .read = seq_read, | ||
58 | .llseek = seq_lseek, | ||
59 | .release = single_release, | ||
60 | }; | ||
61 | |||
62 | static void bdi_debug_register(struct backing_dev_info *bdi, const char *name) | ||
63 | { | ||
64 | bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root); | ||
65 | bdi->debug_stats = debugfs_create_file("stats", 0444, bdi->debug_dir, | ||
66 | bdi, &bdi_debug_stats_fops); | ||
67 | } | ||
68 | |||
69 | static void bdi_debug_unregister(struct backing_dev_info *bdi) | ||
70 | { | ||
71 | debugfs_remove(bdi->debug_stats); | ||
72 | debugfs_remove(bdi->debug_dir); | ||
73 | } | ||
74 | #else | ||
75 | static inline void bdi_debug_init(void) | ||
76 | { | ||
77 | } | ||
78 | static inline void bdi_debug_register(struct backing_dev_info *bdi, | ||
79 | const char *name) | ||
80 | { | ||
81 | } | ||
82 | static inline void bdi_debug_unregister(struct backing_dev_info *bdi) | ||
83 | { | ||
84 | } | ||
85 | #endif | ||
86 | |||
87 | static ssize_t read_ahead_kb_store(struct device *dev, | ||
88 | struct device_attribute *attr, | ||
89 | const char *buf, size_t count) | ||
90 | { | ||
91 | struct backing_dev_info *bdi = dev_get_drvdata(dev); | ||
92 | char *end; | ||
93 | unsigned long read_ahead_kb; | ||
94 | ssize_t ret = -EINVAL; | ||
95 | |||
96 | read_ahead_kb = simple_strtoul(buf, &end, 10); | ||
97 | if (*buf && (end[0] == '\0' || (end[0] == '\n' && end[1] == '\0'))) { | ||
98 | bdi->ra_pages = read_ahead_kb >> (PAGE_SHIFT - 10); | ||
99 | ret = count; | ||
100 | } | ||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | #define K(pages) ((pages) << (PAGE_SHIFT - 10)) | ||
105 | |||
106 | #define BDI_SHOW(name, expr) \ | ||
107 | static ssize_t name##_show(struct device *dev, \ | ||
108 | struct device_attribute *attr, char *page) \ | ||
109 | { \ | ||
110 | struct backing_dev_info *bdi = dev_get_drvdata(dev); \ | ||
111 | \ | ||
112 | return snprintf(page, PAGE_SIZE-1, "%lld\n", (long long)expr); \ | ||
113 | } | ||
114 | |||
115 | BDI_SHOW(read_ahead_kb, K(bdi->ra_pages)) | ||
116 | |||
117 | static ssize_t min_ratio_store(struct device *dev, | ||
118 | struct device_attribute *attr, const char *buf, size_t count) | ||
119 | { | ||
120 | struct backing_dev_info *bdi = dev_get_drvdata(dev); | ||
121 | char *end; | ||
122 | unsigned int ratio; | ||
123 | ssize_t ret = -EINVAL; | ||
124 | |||
125 | ratio = simple_strtoul(buf, &end, 10); | ||
126 | if (*buf && (end[0] == '\0' || (end[0] == '\n' && end[1] == '\0'))) { | ||
127 | ret = bdi_set_min_ratio(bdi, ratio); | ||
128 | if (!ret) | ||
129 | ret = count; | ||
130 | } | ||
131 | return ret; | ||
132 | } | ||
133 | BDI_SHOW(min_ratio, bdi->min_ratio) | ||
134 | |||
135 | static ssize_t max_ratio_store(struct device *dev, | ||
136 | struct device_attribute *attr, const char *buf, size_t count) | ||
137 | { | ||
138 | struct backing_dev_info *bdi = dev_get_drvdata(dev); | ||
139 | char *end; | ||
140 | unsigned int ratio; | ||
141 | ssize_t ret = -EINVAL; | ||
142 | |||
143 | ratio = simple_strtoul(buf, &end, 10); | ||
144 | if (*buf && (end[0] == '\0' || (end[0] == '\n' && end[1] == '\0'))) { | ||
145 | ret = bdi_set_max_ratio(bdi, ratio); | ||
146 | if (!ret) | ||
147 | ret = count; | ||
148 | } | ||
149 | return ret; | ||
150 | } | ||
151 | BDI_SHOW(max_ratio, bdi->max_ratio) | ||
152 | |||
153 | #define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store) | ||
154 | |||
155 | static struct device_attribute bdi_dev_attrs[] = { | ||
156 | __ATTR_RW(read_ahead_kb), | ||
157 | __ATTR_RW(min_ratio), | ||
158 | __ATTR_RW(max_ratio), | ||
159 | __ATTR_NULL, | ||
160 | }; | ||
161 | |||
162 | static __init int bdi_class_init(void) | ||
163 | { | ||
164 | bdi_class = class_create(THIS_MODULE, "bdi"); | ||
165 | bdi_class->dev_attrs = bdi_dev_attrs; | ||
166 | bdi_debug_init(); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | postcore_initcall(bdi_class_init); | ||
171 | |||
172 | int bdi_register(struct backing_dev_info *bdi, struct device *parent, | ||
173 | const char *fmt, ...) | ||
174 | { | ||
175 | char *name; | ||
176 | va_list args; | ||
177 | int ret = 0; | ||
178 | struct device *dev; | ||
179 | |||
180 | va_start(args, fmt); | ||
181 | name = kvasprintf(GFP_KERNEL, fmt, args); | ||
182 | va_end(args); | ||
183 | |||
184 | if (!name) | ||
185 | return -ENOMEM; | ||
186 | |||
187 | dev = device_create(bdi_class, parent, MKDEV(0, 0), name); | ||
188 | if (IS_ERR(dev)) { | ||
189 | ret = PTR_ERR(dev); | ||
190 | goto exit; | ||
191 | } | ||
192 | |||
193 | bdi->dev = dev; | ||
194 | dev_set_drvdata(bdi->dev, bdi); | ||
195 | bdi_debug_register(bdi, name); | ||
196 | |||
197 | exit: | ||
198 | kfree(name); | ||
199 | return ret; | ||
200 | } | ||
201 | EXPORT_SYMBOL(bdi_register); | ||
202 | |||
203 | int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev) | ||
204 | { | ||
205 | return bdi_register(bdi, NULL, "%u:%u", MAJOR(dev), MINOR(dev)); | ||
206 | } | ||
207 | EXPORT_SYMBOL(bdi_register_dev); | ||
208 | |||
209 | void bdi_unregister(struct backing_dev_info *bdi) | ||
210 | { | ||
211 | if (bdi->dev) { | ||
212 | bdi_debug_unregister(bdi); | ||
213 | device_unregister(bdi->dev); | ||
214 | bdi->dev = NULL; | ||
215 | } | ||
216 | } | ||
217 | EXPORT_SYMBOL(bdi_unregister); | ||
7 | 218 | ||
8 | int bdi_init(struct backing_dev_info *bdi) | 219 | int bdi_init(struct backing_dev_info *bdi) |
9 | { | 220 | { |
10 | int i; | 221 | int i; |
11 | int err; | 222 | int err; |
12 | 223 | ||
224 | bdi->dev = NULL; | ||
225 | |||
226 | bdi->min_ratio = 0; | ||
227 | bdi->max_ratio = 100; | ||
228 | bdi->max_prop_frac = PROP_FRAC_BASE; | ||
229 | |||
13 | for (i = 0; i < NR_BDI_STAT_ITEMS; i++) { | 230 | for (i = 0; i < NR_BDI_STAT_ITEMS; i++) { |
14 | err = percpu_counter_init_irq(&bdi->bdi_stat[i], 0); | 231 | err = percpu_counter_init_irq(&bdi->bdi_stat[i], 0); |
15 | if (err) | 232 | if (err) |
@@ -33,6 +250,8 @@ void bdi_destroy(struct backing_dev_info *bdi) | |||
33 | { | 250 | { |
34 | int i; | 251 | int i; |
35 | 252 | ||
253 | bdi_unregister(bdi); | ||
254 | |||
36 | for (i = 0; i < NR_BDI_STAT_ITEMS; i++) | 255 | for (i = 0; i < NR_BDI_STAT_ITEMS; i++) |
37 | percpu_counter_destroy(&bdi->bdi_stat[i]); | 256 | percpu_counter_destroy(&bdi->bdi_stat[i]); |
38 | 257 | ||