Blame view

mm/backing-dev.c 6.99 KB
3fcfab16c   Andrew Morton   [PATCH] separate ...
1
2
3
4
5
6
  
  #include <linux/wait.h>
  #include <linux/backing-dev.h>
  #include <linux/fs.h>
  #include <linux/sched.h>
  #include <linux/module.h>
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
7
8
9
10
11
  #include <linux/writeback.h>
  #include <linux/device.h>
  
  
  static struct class *bdi_class;
76f1418b4   Miklos Szeredi   mm: bdi: move sta...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  #ifdef CONFIG_DEBUG_FS
  #include <linux/debugfs.h>
  #include <linux/seq_file.h>
  
  static struct dentry *bdi_debug_root;
  
  static void bdi_debug_init(void)
  {
  	bdi_debug_root = debugfs_create_dir("bdi", NULL);
  }
  
  static int bdi_debug_stats_show(struct seq_file *m, void *v)
  {
  	struct backing_dev_info *bdi = m->private;
364aeb284   David Rientjes   mm: change dirty ...
26
27
28
  	unsigned long background_thresh;
  	unsigned long dirty_thresh;
  	unsigned long bdi_thresh;
76f1418b4   Miklos Szeredi   mm: bdi: move sta...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  
  	get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi);
  
  #define K(x) ((x) << (PAGE_SHIFT - 10))
  	seq_printf(m,
  		   "BdiWriteback:     %8lu kB
  "
  		   "BdiReclaimable:   %8lu kB
  "
  		   "BdiDirtyThresh:   %8lu kB
  "
  		   "DirtyThresh:      %8lu kB
  "
  		   "BackgroundThresh: %8lu kB
  ",
  		   (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)),
  		   (unsigned long) K(bdi_stat(bdi, BDI_RECLAIMABLE)),
  		   K(bdi_thresh),
  		   K(dirty_thresh),
  		   K(background_thresh));
  #undef K
  
  	return 0;
  }
  
  static int bdi_debug_stats_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, bdi_debug_stats_show, inode->i_private);
  }
  
  static const struct file_operations bdi_debug_stats_fops = {
  	.open		= bdi_debug_stats_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
  };
  
  static void bdi_debug_register(struct backing_dev_info *bdi, const char *name)
  {
  	bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root);
  	bdi->debug_stats = debugfs_create_file("stats", 0444, bdi->debug_dir,
  					       bdi, &bdi_debug_stats_fops);
  }
  
  static void bdi_debug_unregister(struct backing_dev_info *bdi)
  {
  	debugfs_remove(bdi->debug_stats);
  	debugfs_remove(bdi->debug_dir);
  }
  #else
  static inline void bdi_debug_init(void)
  {
  }
  static inline void bdi_debug_register(struct backing_dev_info *bdi,
  				      const char *name)
  {
  }
  static inline void bdi_debug_unregister(struct backing_dev_info *bdi)
  {
  }
  #endif
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  static ssize_t read_ahead_kb_store(struct device *dev,
  				  struct device_attribute *attr,
  				  const char *buf, size_t count)
  {
  	struct backing_dev_info *bdi = dev_get_drvdata(dev);
  	char *end;
  	unsigned long read_ahead_kb;
  	ssize_t ret = -EINVAL;
  
  	read_ahead_kb = simple_strtoul(buf, &end, 10);
  	if (*buf && (end[0] == '\0' || (end[0] == '
  ' && end[1] == '\0'))) {
  		bdi->ra_pages = read_ahead_kb >> (PAGE_SHIFT - 10);
  		ret = count;
  	}
  	return ret;
  }
  
  #define K(pages) ((pages) << (PAGE_SHIFT - 10))
  
  #define BDI_SHOW(name, expr)						\
  static ssize_t name##_show(struct device *dev,				\
  			   struct device_attribute *attr, char *page)	\
  {									\
  	struct backing_dev_info *bdi = dev_get_drvdata(dev);		\
  									\
  	return snprintf(page, PAGE_SIZE-1, "%lld
  ", (long long)expr);	\
  }
  
  BDI_SHOW(read_ahead_kb, K(bdi->ra_pages))
189d3c4a9   Peter Zijlstra   mm: bdi: allow se...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  static ssize_t min_ratio_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t count)
  {
  	struct backing_dev_info *bdi = dev_get_drvdata(dev);
  	char *end;
  	unsigned int ratio;
  	ssize_t ret = -EINVAL;
  
  	ratio = simple_strtoul(buf, &end, 10);
  	if (*buf && (end[0] == '\0' || (end[0] == '
  ' && end[1] == '\0'))) {
  		ret = bdi_set_min_ratio(bdi, ratio);
  		if (!ret)
  			ret = count;
  	}
  	return ret;
  }
  BDI_SHOW(min_ratio, bdi->min_ratio)
a42dde041   Peter Zijlstra   mm: bdi: allow se...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  static ssize_t max_ratio_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t count)
  {
  	struct backing_dev_info *bdi = dev_get_drvdata(dev);
  	char *end;
  	unsigned int ratio;
  	ssize_t ret = -EINVAL;
  
  	ratio = simple_strtoul(buf, &end, 10);
  	if (*buf && (end[0] == '\0' || (end[0] == '
  ' && end[1] == '\0'))) {
  		ret = bdi_set_max_ratio(bdi, ratio);
  		if (!ret)
  			ret = count;
  	}
  	return ret;
  }
  BDI_SHOW(max_ratio, bdi->max_ratio)
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
157
158
159
160
  #define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store)
  
  static struct device_attribute bdi_dev_attrs[] = {
  	__ATTR_RW(read_ahead_kb),
189d3c4a9   Peter Zijlstra   mm: bdi: allow se...
161
  	__ATTR_RW(min_ratio),
a42dde041   Peter Zijlstra   mm: bdi: allow se...
162
  	__ATTR_RW(max_ratio),
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
163
164
165
166
167
168
169
  	__ATTR_NULL,
  };
  
  static __init int bdi_class_init(void)
  {
  	bdi_class = class_create(THIS_MODULE, "bdi");
  	bdi_class->dev_attrs = bdi_dev_attrs;
76f1418b4   Miklos Szeredi   mm: bdi: move sta...
170
  	bdi_debug_init();
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
171
172
  	return 0;
  }
76f1418b4   Miklos Szeredi   mm: bdi: move sta...
173
  postcore_initcall(bdi_class_init);
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
174
175
176
177
  
  int bdi_register(struct backing_dev_info *bdi, struct device *parent,
  		const char *fmt, ...)
  {
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
178
179
180
  	va_list args;
  	int ret = 0;
  	struct device *dev;
69fc208be   Andrew Morton   mm/backing-dev.c:...
181
  	if (bdi->dev)	/* The driver needs to use separate queues per device */
f1d0b063d   Kay Sievers   bdi: register sys...
182
  		goto exit;
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
183
  	va_start(args, fmt);
19051c503   Greg Kroah-Hartman   mm: bdi: fix race...
184
  	dev = device_create_vargs(bdi_class, parent, MKDEV(0, 0), bdi, fmt, args);
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
185
  	va_end(args);
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
186
187
188
189
190
191
  	if (IS_ERR(dev)) {
  		ret = PTR_ERR(dev);
  		goto exit;
  	}
  
  	bdi->dev = dev;
19051c503   Greg Kroah-Hartman   mm: bdi: fix race...
192
  	bdi_debug_register(bdi, dev_name(dev));
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
193
194
  
  exit:
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
195
196
197
198
199
200
201
202
203
204
205
206
207
  	return ret;
  }
  EXPORT_SYMBOL(bdi_register);
  
  int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev)
  {
  	return bdi_register(bdi, NULL, "%u:%u", MAJOR(dev), MINOR(dev));
  }
  EXPORT_SYMBOL(bdi_register_dev);
  
  void bdi_unregister(struct backing_dev_info *bdi)
  {
  	if (bdi->dev) {
76f1418b4   Miklos Szeredi   mm: bdi: move sta...
208
  		bdi_debug_unregister(bdi);
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
209
210
211
212
213
  		device_unregister(bdi->dev);
  		bdi->dev = NULL;
  	}
  }
  EXPORT_SYMBOL(bdi_unregister);
3fcfab16c   Andrew Morton   [PATCH] separate ...
214

b2e8fb6ef   Peter Zijlstra   mm: scalable bdi ...
215
216
  int bdi_init(struct backing_dev_info *bdi)
  {
4b01a0b16   Denis Cheng   mm/backing-dev.c:...
217
  	int i;
b2e8fb6ef   Peter Zijlstra   mm: scalable bdi ...
218
  	int err;
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
219
  	bdi->dev = NULL;
189d3c4a9   Peter Zijlstra   mm: bdi: allow se...
220
  	bdi->min_ratio = 0;
a42dde041   Peter Zijlstra   mm: bdi: allow se...
221
222
  	bdi->max_ratio = 100;
  	bdi->max_prop_frac = PROP_FRAC_BASE;
189d3c4a9   Peter Zijlstra   mm: bdi: allow se...
223

b2e8fb6ef   Peter Zijlstra   mm: scalable bdi ...
224
  	for (i = 0; i < NR_BDI_STAT_ITEMS; i++) {
ea319518b   Peter Zijlstra   locking, percpu c...
225
  		err = percpu_counter_init(&bdi->bdi_stat[i], 0);
04fbfdc14   Peter Zijlstra   mm: per device di...
226
227
228
229
230
231
232
233
234
  		if (err)
  			goto err;
  	}
  
  	bdi->dirty_exceeded = 0;
  	err = prop_local_init_percpu(&bdi->completions);
  
  	if (err) {
  err:
4b01a0b16   Denis Cheng   mm/backing-dev.c:...
235
  		while (i--)
04fbfdc14   Peter Zijlstra   mm: per device di...
236
  			percpu_counter_destroy(&bdi->bdi_stat[i]);
b2e8fb6ef   Peter Zijlstra   mm: scalable bdi ...
237
238
239
240
241
242
243
244
245
  	}
  
  	return err;
  }
  EXPORT_SYMBOL(bdi_init);
  
  void bdi_destroy(struct backing_dev_info *bdi)
  {
  	int i;
cf0ca9fe5   Peter Zijlstra   mm: bdi: export B...
246
  	bdi_unregister(bdi);
b2e8fb6ef   Peter Zijlstra   mm: scalable bdi ...
247
248
  	for (i = 0; i < NR_BDI_STAT_ITEMS; i++)
  		percpu_counter_destroy(&bdi->bdi_stat[i]);
04fbfdc14   Peter Zijlstra   mm: per device di...
249
250
  
  	prop_local_destroy_percpu(&bdi->completions);
b2e8fb6ef   Peter Zijlstra   mm: scalable bdi ...
251
252
  }
  EXPORT_SYMBOL(bdi_destroy);
3fcfab16c   Andrew Morton   [PATCH] separate ...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  static wait_queue_head_t congestion_wqh[2] = {
  		__WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[0]),
  		__WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[1])
  	};
  
  
  void clear_bdi_congested(struct backing_dev_info *bdi, int rw)
  {
  	enum bdi_state bit;
  	wait_queue_head_t *wqh = &congestion_wqh[rw];
  
  	bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
  	clear_bit(bit, &bdi->state);
  	smp_mb__after_clear_bit();
  	if (waitqueue_active(wqh))
  		wake_up(wqh);
  }
  EXPORT_SYMBOL(clear_bdi_congested);
  
  void set_bdi_congested(struct backing_dev_info *bdi, int rw)
  {
  	enum bdi_state bit;
  
  	bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
  	set_bit(bit, &bdi->state);
  }
  EXPORT_SYMBOL(set_bdi_congested);
  
  /**
   * congestion_wait - wait for a backing_dev to become uncongested
   * @rw: READ or WRITE
   * @timeout: timeout in jiffies
   *
   * Waits for up to @timeout jiffies for a backing_dev (any backing_dev) to exit
   * write congestion.  If no backing_devs are congested then just wait for the
   * next write to be completed.
   */
  long congestion_wait(int rw, long timeout)
  {
  	long ret;
  	DEFINE_WAIT(wait);
  	wait_queue_head_t *wqh = &congestion_wqh[rw];
  
  	prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
  	ret = io_schedule_timeout(timeout);
  	finish_wait(wqh, &wait);
  	return ret;
  }
  EXPORT_SYMBOL(congestion_wait);
04fbfdc14   Peter Zijlstra   mm: per device di...
302