1122 lines · 27.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #include <linux/blkdev.h>#include <linux/wait.h>#include <linux/rbtree.h>#include <linux/kthread.h>#include <linux/backing-dev.h>#include <linux/blk-cgroup.h>#include <linux/freezer.h>#include <linux/fs.h>#include <linux/pagemap.h>#include <linux/mm.h>#include <linux/sched/mm.h>#include <linux/sched.h>#include <linux/module.h>#include <linux/writeback.h>#include <linux/device.h>#include <trace/events/writeback.h> struct backing_dev_info noop_backing_dev_info;EXPORT_SYMBOL_GPL(noop_backing_dev_info); static struct class *bdi_class;static const char *bdi_unknown_name = "(unknown)"; /* * bdi_lock protects bdi_tree and updates to bdi_list. bdi_list has RCU * reader side locking. */DEFINE_SPINLOCK(bdi_lock);static u64 bdi_id_cursor;static struct rb_root bdi_tree = RB_ROOT;LIST_HEAD(bdi_list); /* bdi_wq serves all asynchronous writeback tasks */struct workqueue_struct *bdi_wq; #define K(x) ((x) << (PAGE_SHIFT - 10)) #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;	struct bdi_writeback *wb = &bdi->wb;	unsigned long background_thresh;	unsigned long dirty_thresh;	unsigned long wb_thresh;	unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time;	struct inode *inode; 	nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0;	spin_lock(&wb->list_lock);	list_for_each_entry(inode, &wb->b_dirty, i_io_list)		nr_dirty++;	list_for_each_entry(inode, &wb->b_io, i_io_list)		nr_io++;	list_for_each_entry(inode, &wb->b_more_io, i_io_list)		nr_more_io++;	list_for_each_entry(inode, &wb->b_dirty_time, i_io_list)		if (inode->i_state & I_DIRTY_TIME)			nr_dirty_time++;	spin_unlock(&wb->list_lock); 	global_dirty_limits(&background_thresh, &dirty_thresh);	wb_thresh = wb_calc_thresh(wb, dirty_thresh); 	seq_printf(m,		   "BdiWriteback:       %10lu kB\n"		   "BdiReclaimable:     %10lu kB\n"		   "BdiDirtyThresh:     %10lu kB\n"		   "DirtyThresh:        %10lu kB\n"		   "BackgroundThresh:   %10lu kB\n"		   "BdiDirtied:         %10lu kB\n"		   "BdiWritten:         %10lu kB\n"		   "BdiWriteBandwidth:  %10lu kBps\n"		   "b_dirty:            %10lu\n"		   "b_io:               %10lu\n"		   "b_more_io:          %10lu\n"		   "b_dirty_time:       %10lu\n"		   "bdi_list:           %10u\n"		   "state:              %10lx\n",		   (unsigned long) K(wb_stat(wb, WB_WRITEBACK)),		   (unsigned long) K(wb_stat(wb, WB_RECLAIMABLE)),		   K(wb_thresh),		   K(dirty_thresh),		   K(background_thresh),		   (unsigned long) K(wb_stat(wb, WB_DIRTIED)),		   (unsigned long) K(wb_stat(wb, WB_WRITTEN)),		   (unsigned long) K(wb->write_bandwidth),		   nr_dirty,		   nr_io,		   nr_more_io,		   nr_dirty_time,		   !list_empty(&bdi->bdi_list), bdi->wb.state); 	return 0;}DEFINE_SHOW_ATTRIBUTE(bdi_debug_stats); static void bdi_debug_register(struct backing_dev_info *bdi, const char *name){	bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root); 	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_recursive(bdi->debug_dir);}#elsestatic 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 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);	unsigned long read_ahead_kb;	ssize_t ret; 	ret = kstrtoul(buf, 10, &read_ahead_kb);	if (ret < 0)		return ret; 	bdi->ra_pages = read_ahead_kb >> (PAGE_SHIFT - 10); 	return count;} #define BDI_SHOW(name, expr)						\static ssize_t name##_show(struct device *dev,				\			   struct device_attribute *attr, char *buf)	\{									\	struct backing_dev_info *bdi = dev_get_drvdata(dev);		\									\	return sysfs_emit(buf, "%lld\n", (long long)expr);		\}									\static DEVICE_ATTR_RW(name); BDI_SHOW(read_ahead_kb, K(bdi->ra_pages)) 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);	unsigned int ratio;	ssize_t ret; 	ret = kstrtouint(buf, 10, &ratio);	if (ret < 0)		return ret; 	ret = bdi_set_min_ratio(bdi, ratio);	if (!ret)		ret = count; 	return ret;}BDI_SHOW(min_ratio, bdi->min_ratio / BDI_RATIO_SCALE) static ssize_t min_ratio_fine_store(struct device *dev,		struct device_attribute *attr, const char *buf, size_t count){	struct backing_dev_info *bdi = dev_get_drvdata(dev);	unsigned int ratio;	ssize_t ret; 	ret = kstrtouint(buf, 10, &ratio);	if (ret < 0)		return ret; 	ret = bdi_set_min_ratio_no_scale(bdi, ratio);	if (!ret)		ret = count; 	return ret;}BDI_SHOW(min_ratio_fine, bdi->min_ratio) 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);	unsigned int ratio;	ssize_t ret; 	ret = kstrtouint(buf, 10, &ratio);	if (ret < 0)		return ret; 	ret = bdi_set_max_ratio(bdi, ratio);	if (!ret)		ret = count; 	return ret;}BDI_SHOW(max_ratio, bdi->max_ratio / BDI_RATIO_SCALE) static ssize_t max_ratio_fine_store(struct device *dev,		struct device_attribute *attr, const char *buf, size_t count){	struct backing_dev_info *bdi = dev_get_drvdata(dev);	unsigned int ratio;	ssize_t ret; 	ret = kstrtouint(buf, 10, &ratio);	if (ret < 0)		return ret; 	ret = bdi_set_max_ratio_no_scale(bdi, ratio);	if (!ret)		ret = count; 	return ret;}BDI_SHOW(max_ratio_fine, bdi->max_ratio) static ssize_t min_bytes_show(struct device *dev,			      struct device_attribute *attr,			      char *buf){	struct backing_dev_info *bdi = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%llu\n", bdi_get_min_bytes(bdi));} static ssize_t min_bytes_store(struct device *dev,		struct device_attribute *attr, const char *buf, size_t count){	struct backing_dev_info *bdi = dev_get_drvdata(dev);	u64 bytes;	ssize_t ret; 	ret = kstrtoull(buf, 10, &bytes);	if (ret < 0)		return ret; 	ret = bdi_set_min_bytes(bdi, bytes);	if (!ret)		ret = count; 	return ret;}DEVICE_ATTR_RW(min_bytes); static ssize_t max_bytes_show(struct device *dev,			      struct device_attribute *attr,			      char *buf){	struct backing_dev_info *bdi = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%llu\n", bdi_get_max_bytes(bdi));} static ssize_t max_bytes_store(struct device *dev,		struct device_attribute *attr, const char *buf, size_t count){	struct backing_dev_info *bdi = dev_get_drvdata(dev);	u64 bytes;	ssize_t ret; 	ret = kstrtoull(buf, 10, &bytes);	if (ret < 0)		return ret; 	ret = bdi_set_max_bytes(bdi, bytes);	if (!ret)		ret = count; 	return ret;}DEVICE_ATTR_RW(max_bytes); static ssize_t stable_pages_required_show(struct device *dev,					  struct device_attribute *attr,					  char *buf){	dev_warn_once(dev,		"the stable_pages_required attribute has been removed. Use the stable_writes queue attribute instead.\n");	return sysfs_emit(buf, "%d\n", 0);}static DEVICE_ATTR_RO(stable_pages_required); static ssize_t strict_limit_store(struct device *dev,		struct device_attribute *attr, const char *buf, size_t count){	struct backing_dev_info *bdi = dev_get_drvdata(dev);	unsigned int strict_limit;	ssize_t ret; 	ret = kstrtouint(buf, 10, &strict_limit);	if (ret < 0)		return ret; 	ret = bdi_set_strict_limit(bdi, strict_limit);	if (!ret)		ret = count; 	return ret;} static ssize_t strict_limit_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct backing_dev_info *bdi = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%d\n",			!!(bdi->capabilities & BDI_CAP_STRICTLIMIT));}static DEVICE_ATTR_RW(strict_limit); static struct attribute *bdi_dev_attrs[] = {	&dev_attr_read_ahead_kb.attr,	&dev_attr_min_ratio.attr,	&dev_attr_min_ratio_fine.attr,	&dev_attr_max_ratio.attr,	&dev_attr_max_ratio_fine.attr,	&dev_attr_min_bytes.attr,	&dev_attr_max_bytes.attr,	&dev_attr_stable_pages_required.attr,	&dev_attr_strict_limit.attr,	NULL,};ATTRIBUTE_GROUPS(bdi_dev); static __init int bdi_class_init(void){	bdi_class = class_create(THIS_MODULE, "bdi");	if (IS_ERR(bdi_class))		return PTR_ERR(bdi_class); 	bdi_class->dev_groups = bdi_dev_groups;	bdi_debug_init(); 	return 0;}postcore_initcall(bdi_class_init); static int __init default_bdi_init(void){	bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_UNBOUND |				 WQ_SYSFS, 0);	if (!bdi_wq)		return -ENOMEM;	return 0;}subsys_initcall(default_bdi_init); /* * This function is used when the first inode for this wb is marked dirty. It * wakes-up the corresponding bdi thread which should then take care of the * periodic background write-out of dirty inodes. Since the write-out would * starts only 'dirty_writeback_interval' centisecs from now anyway, we just * set up a timer which wakes the bdi thread up later. * * Note, we wouldn't bother setting up the timer, but this function is on the * fast-path (used by '__mark_inode_dirty()'), so we save few context switches * by delaying the wake-up. * * We have to be careful not to postpone flush work if it is scheduled for * earlier. Thus we use queue_delayed_work(). */void wb_wakeup_delayed(struct bdi_writeback *wb){	unsigned long timeout; 	timeout = msecs_to_jiffies(dirty_writeback_interval * 10);	spin_lock_irq(&wb->work_lock);	if (test_bit(WB_registered, &wb->state))		queue_delayed_work(bdi_wq, &wb->dwork, timeout);	spin_unlock_irq(&wb->work_lock);} static void wb_update_bandwidth_workfn(struct work_struct *work){	struct bdi_writeback *wb = container_of(to_delayed_work(work),						struct bdi_writeback, bw_dwork); 	wb_update_bandwidth(wb);} /* * Initial write bandwidth: 100 MB/s */#define INIT_BW		(100 << (20 - PAGE_SHIFT)) static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi,		   gfp_t gfp){	int i, err; 	memset(wb, 0, sizeof(*wb)); 	wb->bdi = bdi;	wb->last_old_flush = jiffies;	INIT_LIST_HEAD(&wb->b_dirty);	INIT_LIST_HEAD(&wb->b_io);	INIT_LIST_HEAD(&wb->b_more_io);	INIT_LIST_HEAD(&wb->b_dirty_time);	spin_lock_init(&wb->list_lock); 	atomic_set(&wb->writeback_inodes, 0);	wb->bw_time_stamp = jiffies;	wb->balanced_dirty_ratelimit = INIT_BW;	wb->dirty_ratelimit = INIT_BW;	wb->write_bandwidth = INIT_BW;	wb->avg_write_bandwidth = INIT_BW; 	spin_lock_init(&wb->work_lock);	INIT_LIST_HEAD(&wb->work_list);	INIT_DELAYED_WORK(&wb->dwork, wb_workfn);	INIT_DELAYED_WORK(&wb->bw_dwork, wb_update_bandwidth_workfn);	wb->dirty_sleep = jiffies; 	err = fprop_local_init_percpu(&wb->completions, gfp);	if (err)		return err; 	for (i = 0; i < NR_WB_STAT_ITEMS; i++) {		err = percpu_counter_init(&wb->stat[i], 0, gfp);		if (err)			goto out_destroy_stat;	} 	return 0; out_destroy_stat:	while (i--)		percpu_counter_destroy(&wb->stat[i]);	fprop_local_destroy_percpu(&wb->completions);	return err;} static void cgwb_remove_from_bdi_list(struct bdi_writeback *wb); /* * Remove bdi from the global list and shutdown any threads we have running */static void wb_shutdown(struct bdi_writeback *wb){	/* Make sure nobody queues further work */	spin_lock_irq(&wb->work_lock);	if (!test_and_clear_bit(WB_registered, &wb->state)) {		spin_unlock_irq(&wb->work_lock);		return;	}	spin_unlock_irq(&wb->work_lock); 	cgwb_remove_from_bdi_list(wb);	/*	 * Drain work list and shutdown the delayed_work.  !WB_registered	 * tells wb_workfn() that @wb is dying and its work_list needs to	 * be drained no matter what.	 */	mod_delayed_work(bdi_wq, &wb->dwork, 0);	flush_delayed_work(&wb->dwork);	WARN_ON(!list_empty(&wb->work_list));	flush_delayed_work(&wb->bw_dwork);} static void wb_exit(struct bdi_writeback *wb){	int i; 	WARN_ON(delayed_work_pending(&wb->dwork)); 	for (i = 0; i < NR_WB_STAT_ITEMS; i++)		percpu_counter_destroy(&wb->stat[i]); 	fprop_local_destroy_percpu(&wb->completions);} #ifdef CONFIG_CGROUP_WRITEBACK #include <linux/memcontrol.h> /* * cgwb_lock protects bdi->cgwb_tree, blkcg->cgwb_list, offline_cgwbs and

Lines 1–500 of 1122.