1082 lines · 27.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #include <linux/wait.h>#include <linux/backing-dev.h>#include <linux/kthread.h>#include <linux/freezer.h>#include <linux/fs.h>#include <linux/pagemap.h>#include <linux/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 = {	.name		= "noop",	.capabilities	= BDI_CAP_NO_ACCT_AND_WRITEBACK,};EXPORT_SYMBOL_GPL(noop_backing_dev_info); static struct class *bdi_class; /* * bdi_lock protects updates to bdi_list. bdi_list has RCU reader side * locking. */DEFINE_SPINLOCK(bdi_lock);LIST_HEAD(bdi_list); /* bdi_wq serves all asynchronous writeback tasks */struct workqueue_struct *bdi_wq; #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); #define K(x) ((x) << (PAGE_SHIFT - 10))	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);#undef K 	return 0;}DEFINE_SHOW_ATTRIBUTE(bdi_debug_stats); static int bdi_debug_register(struct backing_dev_info *bdi, const char *name){	if (!bdi_debug_root)		return -ENOMEM; 	bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root);	if (!bdi->debug_dir)		return -ENOMEM; 	bdi->debug_stats = debugfs_create_file("stats", 0444, bdi->debug_dir,					       bdi, &bdi_debug_stats_fops);	if (!bdi->debug_stats) {		debugfs_remove(bdi->debug_dir);		bdi->debug_dir = NULL;		return -ENOMEM;	} 	return 0;} static void bdi_debug_unregister(struct backing_dev_info *bdi){	debugfs_remove(bdi->debug_stats);	debugfs_remove(bdi->debug_dir);}#elsestatic inline void bdi_debug_init(void){}static inline int bdi_debug_register(struct backing_dev_info *bdi,				      const char *name){	return 0;}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 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\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) 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) static ssize_t stable_pages_required_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, "%d\n",			bdi_cap_stable_pages_required(bdi) ? 1 : 0);}static DEVICE_ATTR_RO(stable_pages_required); static struct attribute *bdi_dev_attrs[] = {	&dev_attr_read_ahead_kb.attr,	&dev_attr_min_ratio.attr,	&dev_attr_max_ratio.attr,	&dev_attr_stable_pages_required.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 bdi_init(struct backing_dev_info *bdi); static int __init default_bdi_init(void){	int err; 	bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_FREEZABLE |					      WQ_UNBOUND | WQ_SYSFS, 0);	if (!bdi_wq)		return -ENOMEM; 	err = bdi_init(&noop_backing_dev_info); 	return err;}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_bh(&wb->work_lock);	if (test_bit(WB_registered, &wb->state))		queue_delayed_work(bdi_wq, &wb->dwork, timeout);	spin_unlock_bh(&wb->work_lock);} /* * 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,		   int blkcg_id, gfp_t gfp){	int i, err; 	memset(wb, 0, sizeof(*wb)); 	if (wb != &bdi->wb)		bdi_get(bdi);	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); 	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);	wb->dirty_sleep = jiffies; 	wb->congested = wb_congested_get_create(bdi, blkcg_id, gfp);	if (!wb->congested) {		err = -ENOMEM;		goto out_put_bdi;	} 	err = fprop_local_init_percpu(&wb->completions, gfp);	if (err)		goto out_put_cong; 	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);out_put_cong:	wb_congested_put(wb->congested);out_put_bdi:	if (wb != &bdi->wb)		bdi_put(bdi);	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_bh(&wb->work_lock);	if (!test_and_clear_bit(WB_registered, &wb->state)) {		spin_unlock_bh(&wb->work_lock);		return;	}	spin_unlock_bh(&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));} 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);	wb_congested_put(wb->congested);	if (wb != &wb->bdi->wb)		bdi_put(wb->bdi);} #ifdef CONFIG_CGROUP_WRITEBACK #include <linux/memcontrol.h> /* * cgwb_lock protects bdi->cgwb_tree, bdi->cgwb_congested_tree, * blkcg->cgwb_list, and memcg->cgwb_list.  bdi->cgwb_tree is also RCU * protected. */static DEFINE_SPINLOCK(cgwb_lock);static struct workqueue_struct *cgwb_release_wq; /** * wb_congested_get_create - get or create a wb_congested * @bdi: associated bdi * @blkcg_id: ID of the associated blkcg * @gfp: allocation mask * * Look up the wb_congested for @blkcg_id on @bdi.  If missing, create one. * The returned wb_congested has its reference count incremented.  Returns * NULL on failure. */struct bdi_writeback_congested *wb_congested_get_create(struct backing_dev_info *bdi, int blkcg_id, gfp_t gfp){	struct bdi_writeback_congested *new_congested = NULL, *congested;	struct rb_node **node, *parent;	unsigned long flags;retry:	spin_lock_irqsave(&cgwb_lock, flags); 	node = &bdi->cgwb_congested_tree.rb_node;	parent = NULL; 	while (*node != NULL) {		parent = *node;		congested = rb_entry(parent, struct bdi_writeback_congested,				     rb_node);		if (congested->blkcg_id < blkcg_id)			node = &parent->rb_left;		else if (congested->blkcg_id > blkcg_id)			node = &parent->rb_right;		else			goto found;	} 	if (new_congested) {		/* !found and storage for new one already allocated, insert */		congested = new_congested;		rb_link_node(&congested->rb_node, parent, node);		rb_insert_color(&congested->rb_node, &bdi->cgwb_congested_tree);		spin_unlock_irqrestore(&cgwb_lock, flags);		return congested;	} 	spin_unlock_irqrestore(&cgwb_lock, flags); 	/* allocate storage for new one and retry */	new_congested = kzalloc(sizeof(*new_congested), gfp);	if (!new_congested)		return NULL; 	refcount_set(&new_congested->refcnt, 1);	new_congested->__bdi = bdi;	new_congested->blkcg_id = blkcg_id;	goto retry; found:	refcount_inc(&congested->refcnt);	spin_unlock_irqrestore(&cgwb_lock, flags);	kfree(new_congested);	return congested;} /** * wb_congested_put - put a wb_congested * @congested: wb_congested to put * * Put @congested and destroy it if the refcnt reaches zero. */void wb_congested_put(struct bdi_writeback_congested *congested){	unsigned long flags; 	if (!refcount_dec_and_lock_irqsave(&congested->refcnt, &cgwb_lock, &flags))		return; 	/* bdi might already have been destroyed leaving @congested unlinked */	if (congested->__bdi) {		rb_erase(&congested->rb_node,			 &congested->__bdi->cgwb_congested_tree);		congested->__bdi = NULL;	} 	spin_unlock_irqrestore(&cgwb_lock, flags);	kfree(congested);} static void cgwb_release_workfn(struct work_struct *work){	struct bdi_writeback *wb = container_of(work, struct bdi_writeback,						release_work);	struct blkcg *blkcg = css_to_blkcg(wb->blkcg_css); 	mutex_lock(&wb->bdi->cgwb_release_mutex);	wb_shutdown(wb); 	css_put(wb->memcg_css);

Lines 1–500 of 1082.