162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/blkdev.h> 462306a36Sopenharmony_ci#include <linux/wait.h> 562306a36Sopenharmony_ci#include <linux/rbtree.h> 662306a36Sopenharmony_ci#include <linux/kthread.h> 762306a36Sopenharmony_ci#include <linux/backing-dev.h> 862306a36Sopenharmony_ci#include <linux/blk-cgroup.h> 962306a36Sopenharmony_ci#include <linux/freezer.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/pagemap.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/sched/mm.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/writeback.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <trace/events/writeback.h> 1962306a36Sopenharmony_ci#include "internal.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct backing_dev_info noop_backing_dev_info; 2262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(noop_backing_dev_info); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const char *bdi_unknown_name = "(unknown)"; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * bdi_lock protects bdi_tree and updates to bdi_list. bdi_list has RCU 2862306a36Sopenharmony_ci * reader side locking. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ciDEFINE_SPINLOCK(bdi_lock); 3162306a36Sopenharmony_cistatic u64 bdi_id_cursor; 3262306a36Sopenharmony_cistatic struct rb_root bdi_tree = RB_ROOT; 3362306a36Sopenharmony_ciLIST_HEAD(bdi_list); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* bdi_wq serves all asynchronous writeback tasks */ 3662306a36Sopenharmony_cistruct workqueue_struct *bdi_wq; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 3962306a36Sopenharmony_ci#include <linux/debugfs.h> 4062306a36Sopenharmony_ci#include <linux/seq_file.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct dentry *bdi_debug_root; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void bdi_debug_init(void) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci bdi_debug_root = debugfs_create_dir("bdi", NULL); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int bdi_debug_stats_show(struct seq_file *m, void *v) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct backing_dev_info *bdi = m->private; 5262306a36Sopenharmony_ci struct bdi_writeback *wb = &bdi->wb; 5362306a36Sopenharmony_ci unsigned long background_thresh; 5462306a36Sopenharmony_ci unsigned long dirty_thresh; 5562306a36Sopenharmony_ci unsigned long wb_thresh; 5662306a36Sopenharmony_ci unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time; 5762306a36Sopenharmony_ci struct inode *inode; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0; 6062306a36Sopenharmony_ci spin_lock(&wb->list_lock); 6162306a36Sopenharmony_ci list_for_each_entry(inode, &wb->b_dirty, i_io_list) 6262306a36Sopenharmony_ci nr_dirty++; 6362306a36Sopenharmony_ci list_for_each_entry(inode, &wb->b_io, i_io_list) 6462306a36Sopenharmony_ci nr_io++; 6562306a36Sopenharmony_ci list_for_each_entry(inode, &wb->b_more_io, i_io_list) 6662306a36Sopenharmony_ci nr_more_io++; 6762306a36Sopenharmony_ci list_for_each_entry(inode, &wb->b_dirty_time, i_io_list) 6862306a36Sopenharmony_ci if (inode->i_state & I_DIRTY_TIME) 6962306a36Sopenharmony_ci nr_dirty_time++; 7062306a36Sopenharmony_ci spin_unlock(&wb->list_lock); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci global_dirty_limits(&background_thresh, &dirty_thresh); 7362306a36Sopenharmony_ci wb_thresh = wb_calc_thresh(wb, dirty_thresh); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci seq_printf(m, 7662306a36Sopenharmony_ci "BdiWriteback: %10lu kB\n" 7762306a36Sopenharmony_ci "BdiReclaimable: %10lu kB\n" 7862306a36Sopenharmony_ci "BdiDirtyThresh: %10lu kB\n" 7962306a36Sopenharmony_ci "DirtyThresh: %10lu kB\n" 8062306a36Sopenharmony_ci "BackgroundThresh: %10lu kB\n" 8162306a36Sopenharmony_ci "BdiDirtied: %10lu kB\n" 8262306a36Sopenharmony_ci "BdiWritten: %10lu kB\n" 8362306a36Sopenharmony_ci "BdiWriteBandwidth: %10lu kBps\n" 8462306a36Sopenharmony_ci "b_dirty: %10lu\n" 8562306a36Sopenharmony_ci "b_io: %10lu\n" 8662306a36Sopenharmony_ci "b_more_io: %10lu\n" 8762306a36Sopenharmony_ci "b_dirty_time: %10lu\n" 8862306a36Sopenharmony_ci "bdi_list: %10u\n" 8962306a36Sopenharmony_ci "state: %10lx\n", 9062306a36Sopenharmony_ci (unsigned long) K(wb_stat(wb, WB_WRITEBACK)), 9162306a36Sopenharmony_ci (unsigned long) K(wb_stat(wb, WB_RECLAIMABLE)), 9262306a36Sopenharmony_ci K(wb_thresh), 9362306a36Sopenharmony_ci K(dirty_thresh), 9462306a36Sopenharmony_ci K(background_thresh), 9562306a36Sopenharmony_ci (unsigned long) K(wb_stat(wb, WB_DIRTIED)), 9662306a36Sopenharmony_ci (unsigned long) K(wb_stat(wb, WB_WRITTEN)), 9762306a36Sopenharmony_ci (unsigned long) K(wb->write_bandwidth), 9862306a36Sopenharmony_ci nr_dirty, 9962306a36Sopenharmony_ci nr_io, 10062306a36Sopenharmony_ci nr_more_io, 10162306a36Sopenharmony_ci nr_dirty_time, 10262306a36Sopenharmony_ci !list_empty(&bdi->bdi_list), bdi->wb.state); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(bdi_debug_stats); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void bdi_debug_register(struct backing_dev_info *bdi, const char *name) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci debugfs_create_file("stats", 0444, bdi->debug_dir, bdi, 11362306a36Sopenharmony_ci &bdi_debug_stats_fops); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void bdi_debug_unregister(struct backing_dev_info *bdi) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci debugfs_remove_recursive(bdi->debug_dir); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci#else 12162306a36Sopenharmony_cistatic inline void bdi_debug_init(void) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_cistatic inline void bdi_debug_register(struct backing_dev_info *bdi, 12562306a36Sopenharmony_ci const char *name) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_cistatic inline void bdi_debug_unregister(struct backing_dev_info *bdi) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci#endif 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic ssize_t read_ahead_kb_store(struct device *dev, 13462306a36Sopenharmony_ci struct device_attribute *attr, 13562306a36Sopenharmony_ci const char *buf, size_t count) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 13862306a36Sopenharmony_ci unsigned long read_ahead_kb; 13962306a36Sopenharmony_ci ssize_t ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = kstrtoul(buf, 10, &read_ahead_kb); 14262306a36Sopenharmony_ci if (ret < 0) 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci bdi->ra_pages = read_ahead_kb >> (PAGE_SHIFT - 10); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return count; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#define BDI_SHOW(name, expr) \ 15162306a36Sopenharmony_cistatic ssize_t name##_show(struct device *dev, \ 15262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 15362306a36Sopenharmony_ci{ \ 15462306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); \ 15562306a36Sopenharmony_ci \ 15662306a36Sopenharmony_ci return sysfs_emit(buf, "%lld\n", (long long)expr); \ 15762306a36Sopenharmony_ci} \ 15862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(name); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciBDI_SHOW(read_ahead_kb, K(bdi->ra_pages)) 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic ssize_t min_ratio_store(struct device *dev, 16362306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 16662306a36Sopenharmony_ci unsigned int ratio; 16762306a36Sopenharmony_ci ssize_t ret; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &ratio); 17062306a36Sopenharmony_ci if (ret < 0) 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = bdi_set_min_ratio(bdi, ratio); 17462306a36Sopenharmony_ci if (!ret) 17562306a36Sopenharmony_ci ret = count; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ciBDI_SHOW(min_ratio, bdi->min_ratio / BDI_RATIO_SCALE) 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic ssize_t min_ratio_fine_store(struct device *dev, 18262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 18562306a36Sopenharmony_ci unsigned int ratio; 18662306a36Sopenharmony_ci ssize_t ret; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &ratio); 18962306a36Sopenharmony_ci if (ret < 0) 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci ret = bdi_set_min_ratio_no_scale(bdi, ratio); 19362306a36Sopenharmony_ci if (!ret) 19462306a36Sopenharmony_ci ret = count; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciBDI_SHOW(min_ratio_fine, bdi->min_ratio) 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic ssize_t max_ratio_store(struct device *dev, 20162306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 20462306a36Sopenharmony_ci unsigned int ratio; 20562306a36Sopenharmony_ci ssize_t ret; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &ratio); 20862306a36Sopenharmony_ci if (ret < 0) 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = bdi_set_max_ratio(bdi, ratio); 21262306a36Sopenharmony_ci if (!ret) 21362306a36Sopenharmony_ci ret = count; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ciBDI_SHOW(max_ratio, bdi->max_ratio / BDI_RATIO_SCALE) 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic ssize_t max_ratio_fine_store(struct device *dev, 22062306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 22362306a36Sopenharmony_ci unsigned int ratio; 22462306a36Sopenharmony_ci ssize_t ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &ratio); 22762306a36Sopenharmony_ci if (ret < 0) 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ret = bdi_set_max_ratio_no_scale(bdi, ratio); 23162306a36Sopenharmony_ci if (!ret) 23262306a36Sopenharmony_ci ret = count; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ciBDI_SHOW(max_ratio_fine, bdi->max_ratio) 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic ssize_t min_bytes_show(struct device *dev, 23962306a36Sopenharmony_ci struct device_attribute *attr, 24062306a36Sopenharmony_ci char *buf) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", bdi_get_min_bytes(bdi)); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic ssize_t min_bytes_store(struct device *dev, 24862306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 25162306a36Sopenharmony_ci u64 bytes; 25262306a36Sopenharmony_ci ssize_t ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ret = kstrtoull(buf, 10, &bytes); 25562306a36Sopenharmony_ci if (ret < 0) 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = bdi_set_min_bytes(bdi, bytes); 25962306a36Sopenharmony_ci if (!ret) 26062306a36Sopenharmony_ci ret = count; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(min_bytes); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic ssize_t max_bytes_show(struct device *dev, 26762306a36Sopenharmony_ci struct device_attribute *attr, 26862306a36Sopenharmony_ci char *buf) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", bdi_get_max_bytes(bdi)); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic ssize_t max_bytes_store(struct device *dev, 27662306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 27962306a36Sopenharmony_ci u64 bytes; 28062306a36Sopenharmony_ci ssize_t ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ret = kstrtoull(buf, 10, &bytes); 28362306a36Sopenharmony_ci if (ret < 0) 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ret = bdi_set_max_bytes(bdi, bytes); 28762306a36Sopenharmony_ci if (!ret) 28862306a36Sopenharmony_ci ret = count; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(max_bytes); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic ssize_t stable_pages_required_show(struct device *dev, 29562306a36Sopenharmony_ci struct device_attribute *attr, 29662306a36Sopenharmony_ci char *buf) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci dev_warn_once(dev, 29962306a36Sopenharmony_ci "the stable_pages_required attribute has been removed. Use the stable_writes queue attribute instead.\n"); 30062306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", 0); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(stable_pages_required); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic ssize_t strict_limit_store(struct device *dev, 30562306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 30862306a36Sopenharmony_ci unsigned int strict_limit; 30962306a36Sopenharmony_ci ssize_t ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &strict_limit); 31262306a36Sopenharmony_ci if (ret < 0) 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = bdi_set_strict_limit(bdi, strict_limit); 31662306a36Sopenharmony_ci if (!ret) 31762306a36Sopenharmony_ci ret = count; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic ssize_t strict_limit_show(struct device *dev, 32362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct backing_dev_info *bdi = dev_get_drvdata(dev); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", 32862306a36Sopenharmony_ci !!(bdi->capabilities & BDI_CAP_STRICTLIMIT)); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(strict_limit); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic struct attribute *bdi_dev_attrs[] = { 33362306a36Sopenharmony_ci &dev_attr_read_ahead_kb.attr, 33462306a36Sopenharmony_ci &dev_attr_min_ratio.attr, 33562306a36Sopenharmony_ci &dev_attr_min_ratio_fine.attr, 33662306a36Sopenharmony_ci &dev_attr_max_ratio.attr, 33762306a36Sopenharmony_ci &dev_attr_max_ratio_fine.attr, 33862306a36Sopenharmony_ci &dev_attr_min_bytes.attr, 33962306a36Sopenharmony_ci &dev_attr_max_bytes.attr, 34062306a36Sopenharmony_ci &dev_attr_stable_pages_required.attr, 34162306a36Sopenharmony_ci &dev_attr_strict_limit.attr, 34262306a36Sopenharmony_ci NULL, 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ciATTRIBUTE_GROUPS(bdi_dev); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct class bdi_class = { 34762306a36Sopenharmony_ci .name = "bdi", 34862306a36Sopenharmony_ci .dev_groups = bdi_dev_groups, 34962306a36Sopenharmony_ci}; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic __init int bdi_class_init(void) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci int ret; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci ret = class_register(&bdi_class); 35662306a36Sopenharmony_ci if (ret) 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci bdi_debug_init(); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_cipostcore_initcall(bdi_class_init); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int __init default_bdi_init(void) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_UNBOUND | 36862306a36Sopenharmony_ci WQ_SYSFS, 0); 36962306a36Sopenharmony_ci if (!bdi_wq) 37062306a36Sopenharmony_ci return -ENOMEM; 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_cisubsys_initcall(default_bdi_init); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * This function is used when the first inode for this wb is marked dirty. It 37762306a36Sopenharmony_ci * wakes-up the corresponding bdi thread which should then take care of the 37862306a36Sopenharmony_ci * periodic background write-out of dirty inodes. Since the write-out would 37962306a36Sopenharmony_ci * starts only 'dirty_writeback_interval' centisecs from now anyway, we just 38062306a36Sopenharmony_ci * set up a timer which wakes the bdi thread up later. 38162306a36Sopenharmony_ci * 38262306a36Sopenharmony_ci * Note, we wouldn't bother setting up the timer, but this function is on the 38362306a36Sopenharmony_ci * fast-path (used by '__mark_inode_dirty()'), so we save few context switches 38462306a36Sopenharmony_ci * by delaying the wake-up. 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * We have to be careful not to postpone flush work if it is scheduled for 38762306a36Sopenharmony_ci * earlier. Thus we use queue_delayed_work(). 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_civoid wb_wakeup_delayed(struct bdi_writeback *wb) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci unsigned long timeout; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci timeout = msecs_to_jiffies(dirty_writeback_interval * 10); 39462306a36Sopenharmony_ci spin_lock_irq(&wb->work_lock); 39562306a36Sopenharmony_ci if (test_bit(WB_registered, &wb->state)) 39662306a36Sopenharmony_ci queue_delayed_work(bdi_wq, &wb->dwork, timeout); 39762306a36Sopenharmony_ci spin_unlock_irq(&wb->work_lock); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void wb_update_bandwidth_workfn(struct work_struct *work) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct bdi_writeback *wb = container_of(to_delayed_work(work), 40362306a36Sopenharmony_ci struct bdi_writeback, bw_dwork); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci wb_update_bandwidth(wb); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* 40962306a36Sopenharmony_ci * Initial write bandwidth: 100 MB/s 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci#define INIT_BW (100 << (20 - PAGE_SHIFT)) 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi, 41462306a36Sopenharmony_ci gfp_t gfp) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci int i, err; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci memset(wb, 0, sizeof(*wb)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci wb->bdi = bdi; 42162306a36Sopenharmony_ci wb->last_old_flush = jiffies; 42262306a36Sopenharmony_ci INIT_LIST_HEAD(&wb->b_dirty); 42362306a36Sopenharmony_ci INIT_LIST_HEAD(&wb->b_io); 42462306a36Sopenharmony_ci INIT_LIST_HEAD(&wb->b_more_io); 42562306a36Sopenharmony_ci INIT_LIST_HEAD(&wb->b_dirty_time); 42662306a36Sopenharmony_ci spin_lock_init(&wb->list_lock); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci atomic_set(&wb->writeback_inodes, 0); 42962306a36Sopenharmony_ci wb->bw_time_stamp = jiffies; 43062306a36Sopenharmony_ci wb->balanced_dirty_ratelimit = INIT_BW; 43162306a36Sopenharmony_ci wb->dirty_ratelimit = INIT_BW; 43262306a36Sopenharmony_ci wb->write_bandwidth = INIT_BW; 43362306a36Sopenharmony_ci wb->avg_write_bandwidth = INIT_BW; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_lock_init(&wb->work_lock); 43662306a36Sopenharmony_ci INIT_LIST_HEAD(&wb->work_list); 43762306a36Sopenharmony_ci INIT_DELAYED_WORK(&wb->dwork, wb_workfn); 43862306a36Sopenharmony_ci INIT_DELAYED_WORK(&wb->bw_dwork, wb_update_bandwidth_workfn); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci err = fprop_local_init_percpu(&wb->completions, gfp); 44162306a36Sopenharmony_ci if (err) 44262306a36Sopenharmony_ci return err; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci for (i = 0; i < NR_WB_STAT_ITEMS; i++) { 44562306a36Sopenharmony_ci err = percpu_counter_init(&wb->stat[i], 0, gfp); 44662306a36Sopenharmony_ci if (err) 44762306a36Sopenharmony_ci goto out_destroy_stat; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciout_destroy_stat: 45362306a36Sopenharmony_ci while (i--) 45462306a36Sopenharmony_ci percpu_counter_destroy(&wb->stat[i]); 45562306a36Sopenharmony_ci fprop_local_destroy_percpu(&wb->completions); 45662306a36Sopenharmony_ci return err; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic void cgwb_remove_from_bdi_list(struct bdi_writeback *wb); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/* 46262306a36Sopenharmony_ci * Remove bdi from the global list and shutdown any threads we have running 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_cistatic void wb_shutdown(struct bdi_writeback *wb) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci /* Make sure nobody queues further work */ 46762306a36Sopenharmony_ci spin_lock_irq(&wb->work_lock); 46862306a36Sopenharmony_ci if (!test_and_clear_bit(WB_registered, &wb->state)) { 46962306a36Sopenharmony_ci spin_unlock_irq(&wb->work_lock); 47062306a36Sopenharmony_ci return; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci spin_unlock_irq(&wb->work_lock); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci cgwb_remove_from_bdi_list(wb); 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Drain work list and shutdown the delayed_work. !WB_registered 47762306a36Sopenharmony_ci * tells wb_workfn() that @wb is dying and its work_list needs to 47862306a36Sopenharmony_ci * be drained no matter what. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci mod_delayed_work(bdi_wq, &wb->dwork, 0); 48162306a36Sopenharmony_ci flush_delayed_work(&wb->dwork); 48262306a36Sopenharmony_ci WARN_ON(!list_empty(&wb->work_list)); 48362306a36Sopenharmony_ci flush_delayed_work(&wb->bw_dwork); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void wb_exit(struct bdi_writeback *wb) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci int i; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci WARN_ON(delayed_work_pending(&wb->dwork)); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci for (i = 0; i < NR_WB_STAT_ITEMS; i++) 49362306a36Sopenharmony_ci percpu_counter_destroy(&wb->stat[i]); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci fprop_local_destroy_percpu(&wb->completions); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci#ifdef CONFIG_CGROUP_WRITEBACK 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci#include <linux/memcontrol.h> 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* 50362306a36Sopenharmony_ci * cgwb_lock protects bdi->cgwb_tree, blkcg->cgwb_list, offline_cgwbs and 50462306a36Sopenharmony_ci * memcg->cgwb_list. bdi->cgwb_tree is also RCU protected. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(cgwb_lock); 50762306a36Sopenharmony_cistatic struct workqueue_struct *cgwb_release_wq; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic LIST_HEAD(offline_cgwbs); 51062306a36Sopenharmony_cistatic void cleanup_offline_cgwbs_workfn(struct work_struct *work); 51162306a36Sopenharmony_cistatic DECLARE_WORK(cleanup_offline_cgwbs_work, cleanup_offline_cgwbs_workfn); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void cgwb_free_rcu(struct rcu_head *rcu_head) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct bdi_writeback *wb = container_of(rcu_head, 51662306a36Sopenharmony_ci struct bdi_writeback, rcu); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci percpu_ref_exit(&wb->refcnt); 51962306a36Sopenharmony_ci kfree(wb); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic void cgwb_release_workfn(struct work_struct *work) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct bdi_writeback *wb = container_of(work, struct bdi_writeback, 52562306a36Sopenharmony_ci release_work); 52662306a36Sopenharmony_ci struct backing_dev_info *bdi = wb->bdi; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci mutex_lock(&wb->bdi->cgwb_release_mutex); 52962306a36Sopenharmony_ci wb_shutdown(wb); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci css_put(wb->memcg_css); 53262306a36Sopenharmony_ci css_put(wb->blkcg_css); 53362306a36Sopenharmony_ci mutex_unlock(&wb->bdi->cgwb_release_mutex); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* triggers blkg destruction if no online users left */ 53662306a36Sopenharmony_ci blkcg_unpin_online(wb->blkcg_css); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci fprop_local_destroy_percpu(&wb->memcg_completions); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 54162306a36Sopenharmony_ci list_del(&wb->offline_node); 54262306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci wb_exit(wb); 54562306a36Sopenharmony_ci bdi_put(bdi); 54662306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&wb->b_attached)); 54762306a36Sopenharmony_ci call_rcu(&wb->rcu, cgwb_free_rcu); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void cgwb_release(struct percpu_ref *refcnt) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct bdi_writeback *wb = container_of(refcnt, struct bdi_writeback, 55362306a36Sopenharmony_ci refcnt); 55462306a36Sopenharmony_ci queue_work(cgwb_release_wq, &wb->release_work); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void cgwb_kill(struct bdi_writeback *wb) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci lockdep_assert_held(&cgwb_lock); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci WARN_ON(!radix_tree_delete(&wb->bdi->cgwb_tree, wb->memcg_css->id)); 56262306a36Sopenharmony_ci list_del(&wb->memcg_node); 56362306a36Sopenharmony_ci list_del(&wb->blkcg_node); 56462306a36Sopenharmony_ci list_add(&wb->offline_node, &offline_cgwbs); 56562306a36Sopenharmony_ci percpu_ref_kill(&wb->refcnt); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void cgwb_remove_from_bdi_list(struct bdi_writeback *wb) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 57162306a36Sopenharmony_ci list_del_rcu(&wb->bdi_node); 57262306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic int cgwb_create(struct backing_dev_info *bdi, 57662306a36Sopenharmony_ci struct cgroup_subsys_state *memcg_css, gfp_t gfp) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct mem_cgroup *memcg; 57962306a36Sopenharmony_ci struct cgroup_subsys_state *blkcg_css; 58062306a36Sopenharmony_ci struct list_head *memcg_cgwb_list, *blkcg_cgwb_list; 58162306a36Sopenharmony_ci struct bdi_writeback *wb; 58262306a36Sopenharmony_ci unsigned long flags; 58362306a36Sopenharmony_ci int ret = 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci memcg = mem_cgroup_from_css(memcg_css); 58662306a36Sopenharmony_ci blkcg_css = cgroup_get_e_css(memcg_css->cgroup, &io_cgrp_subsys); 58762306a36Sopenharmony_ci memcg_cgwb_list = &memcg->cgwb_list; 58862306a36Sopenharmony_ci blkcg_cgwb_list = blkcg_get_cgwb_list(blkcg_css); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* look up again under lock and discard on blkcg mismatch */ 59162306a36Sopenharmony_ci spin_lock_irqsave(&cgwb_lock, flags); 59262306a36Sopenharmony_ci wb = radix_tree_lookup(&bdi->cgwb_tree, memcg_css->id); 59362306a36Sopenharmony_ci if (wb && wb->blkcg_css != blkcg_css) { 59462306a36Sopenharmony_ci cgwb_kill(wb); 59562306a36Sopenharmony_ci wb = NULL; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci spin_unlock_irqrestore(&cgwb_lock, flags); 59862306a36Sopenharmony_ci if (wb) 59962306a36Sopenharmony_ci goto out_put; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* need to create a new one */ 60262306a36Sopenharmony_ci wb = kmalloc(sizeof(*wb), gfp); 60362306a36Sopenharmony_ci if (!wb) { 60462306a36Sopenharmony_ci ret = -ENOMEM; 60562306a36Sopenharmony_ci goto out_put; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret = wb_init(wb, bdi, gfp); 60962306a36Sopenharmony_ci if (ret) 61062306a36Sopenharmony_ci goto err_free; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ret = percpu_ref_init(&wb->refcnt, cgwb_release, 0, gfp); 61362306a36Sopenharmony_ci if (ret) 61462306a36Sopenharmony_ci goto err_wb_exit; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ret = fprop_local_init_percpu(&wb->memcg_completions, gfp); 61762306a36Sopenharmony_ci if (ret) 61862306a36Sopenharmony_ci goto err_ref_exit; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci wb->memcg_css = memcg_css; 62162306a36Sopenharmony_ci wb->blkcg_css = blkcg_css; 62262306a36Sopenharmony_ci INIT_LIST_HEAD(&wb->b_attached); 62362306a36Sopenharmony_ci INIT_WORK(&wb->release_work, cgwb_release_workfn); 62462306a36Sopenharmony_ci set_bit(WB_registered, &wb->state); 62562306a36Sopenharmony_ci bdi_get(bdi); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * The root wb determines the registered state of the whole bdi and 62962306a36Sopenharmony_ci * memcg_cgwb_list and blkcg_cgwb_list's next pointers indicate 63062306a36Sopenharmony_ci * whether they're still online. Don't link @wb if any is dead. 63162306a36Sopenharmony_ci * See wb_memcg_offline() and wb_blkcg_offline(). 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci ret = -ENODEV; 63462306a36Sopenharmony_ci spin_lock_irqsave(&cgwb_lock, flags); 63562306a36Sopenharmony_ci if (test_bit(WB_registered, &bdi->wb.state) && 63662306a36Sopenharmony_ci blkcg_cgwb_list->next && memcg_cgwb_list->next) { 63762306a36Sopenharmony_ci /* we might have raced another instance of this function */ 63862306a36Sopenharmony_ci ret = radix_tree_insert(&bdi->cgwb_tree, memcg_css->id, wb); 63962306a36Sopenharmony_ci if (!ret) { 64062306a36Sopenharmony_ci list_add_tail_rcu(&wb->bdi_node, &bdi->wb_list); 64162306a36Sopenharmony_ci list_add(&wb->memcg_node, memcg_cgwb_list); 64262306a36Sopenharmony_ci list_add(&wb->blkcg_node, blkcg_cgwb_list); 64362306a36Sopenharmony_ci blkcg_pin_online(blkcg_css); 64462306a36Sopenharmony_ci css_get(memcg_css); 64562306a36Sopenharmony_ci css_get(blkcg_css); 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci spin_unlock_irqrestore(&cgwb_lock, flags); 64962306a36Sopenharmony_ci if (ret) { 65062306a36Sopenharmony_ci if (ret == -EEXIST) 65162306a36Sopenharmony_ci ret = 0; 65262306a36Sopenharmony_ci goto err_fprop_exit; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci goto out_put; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cierr_fprop_exit: 65762306a36Sopenharmony_ci bdi_put(bdi); 65862306a36Sopenharmony_ci fprop_local_destroy_percpu(&wb->memcg_completions); 65962306a36Sopenharmony_cierr_ref_exit: 66062306a36Sopenharmony_ci percpu_ref_exit(&wb->refcnt); 66162306a36Sopenharmony_cierr_wb_exit: 66262306a36Sopenharmony_ci wb_exit(wb); 66362306a36Sopenharmony_cierr_free: 66462306a36Sopenharmony_ci kfree(wb); 66562306a36Sopenharmony_ciout_put: 66662306a36Sopenharmony_ci css_put(blkcg_css); 66762306a36Sopenharmony_ci return ret; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/** 67162306a36Sopenharmony_ci * wb_get_lookup - get wb for a given memcg 67262306a36Sopenharmony_ci * @bdi: target bdi 67362306a36Sopenharmony_ci * @memcg_css: cgroup_subsys_state of the target memcg (must have positive ref) 67462306a36Sopenharmony_ci * 67562306a36Sopenharmony_ci * Try to get the wb for @memcg_css on @bdi. The returned wb has its 67662306a36Sopenharmony_ci * refcount incremented. 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * This function uses css_get() on @memcg_css and thus expects its refcnt 67962306a36Sopenharmony_ci * to be positive on invocation. IOW, rcu_read_lock() protection on 68062306a36Sopenharmony_ci * @memcg_css isn't enough. try_get it before calling this function. 68162306a36Sopenharmony_ci * 68262306a36Sopenharmony_ci * A wb is keyed by its associated memcg. As blkcg implicitly enables 68362306a36Sopenharmony_ci * memcg on the default hierarchy, memcg association is guaranteed to be 68462306a36Sopenharmony_ci * more specific (equal or descendant to the associated blkcg) and thus can 68562306a36Sopenharmony_ci * identify both the memcg and blkcg associations. 68662306a36Sopenharmony_ci * 68762306a36Sopenharmony_ci * Because the blkcg associated with a memcg may change as blkcg is enabled 68862306a36Sopenharmony_ci * and disabled closer to root in the hierarchy, each wb keeps track of 68962306a36Sopenharmony_ci * both the memcg and blkcg associated with it and verifies the blkcg on 69062306a36Sopenharmony_ci * each lookup. On mismatch, the existing wb is discarded and a new one is 69162306a36Sopenharmony_ci * created. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_cistruct bdi_writeback *wb_get_lookup(struct backing_dev_info *bdi, 69462306a36Sopenharmony_ci struct cgroup_subsys_state *memcg_css) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct bdi_writeback *wb; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (!memcg_css->parent) 69962306a36Sopenharmony_ci return &bdi->wb; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci rcu_read_lock(); 70262306a36Sopenharmony_ci wb = radix_tree_lookup(&bdi->cgwb_tree, memcg_css->id); 70362306a36Sopenharmony_ci if (wb) { 70462306a36Sopenharmony_ci struct cgroup_subsys_state *blkcg_css; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* see whether the blkcg association has changed */ 70762306a36Sopenharmony_ci blkcg_css = cgroup_get_e_css(memcg_css->cgroup, &io_cgrp_subsys); 70862306a36Sopenharmony_ci if (unlikely(wb->blkcg_css != blkcg_css || !wb_tryget(wb))) 70962306a36Sopenharmony_ci wb = NULL; 71062306a36Sopenharmony_ci css_put(blkcg_css); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci rcu_read_unlock(); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return wb; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/** 71862306a36Sopenharmony_ci * wb_get_create - get wb for a given memcg, create if necessary 71962306a36Sopenharmony_ci * @bdi: target bdi 72062306a36Sopenharmony_ci * @memcg_css: cgroup_subsys_state of the target memcg (must have positive ref) 72162306a36Sopenharmony_ci * @gfp: allocation mask to use 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * Try to get the wb for @memcg_css on @bdi. If it doesn't exist, try to 72462306a36Sopenharmony_ci * create one. See wb_get_lookup() for more details. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_cistruct bdi_writeback *wb_get_create(struct backing_dev_info *bdi, 72762306a36Sopenharmony_ci struct cgroup_subsys_state *memcg_css, 72862306a36Sopenharmony_ci gfp_t gfp) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct bdi_writeback *wb; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci might_alloc(gfp); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci do { 73562306a36Sopenharmony_ci wb = wb_get_lookup(bdi, memcg_css); 73662306a36Sopenharmony_ci } while (!wb && !cgwb_create(bdi, memcg_css, gfp)); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return wb; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic int cgwb_bdi_init(struct backing_dev_info *bdi) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci int ret; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci INIT_RADIX_TREE(&bdi->cgwb_tree, GFP_ATOMIC); 74662306a36Sopenharmony_ci mutex_init(&bdi->cgwb_release_mutex); 74762306a36Sopenharmony_ci init_rwsem(&bdi->wb_switch_rwsem); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ret = wb_init(&bdi->wb, bdi, GFP_KERNEL); 75062306a36Sopenharmony_ci if (!ret) { 75162306a36Sopenharmony_ci bdi->wb.memcg_css = &root_mem_cgroup->css; 75262306a36Sopenharmony_ci bdi->wb.blkcg_css = blkcg_root_css; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic void cgwb_bdi_unregister(struct backing_dev_info *bdi) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct radix_tree_iter iter; 76062306a36Sopenharmony_ci void **slot; 76162306a36Sopenharmony_ci struct bdi_writeback *wb; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci WARN_ON(test_bit(WB_registered, &bdi->wb.state)); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 76662306a36Sopenharmony_ci radix_tree_for_each_slot(slot, &bdi->cgwb_tree, &iter, 0) 76762306a36Sopenharmony_ci cgwb_kill(*slot); 76862306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci mutex_lock(&bdi->cgwb_release_mutex); 77162306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 77262306a36Sopenharmony_ci while (!list_empty(&bdi->wb_list)) { 77362306a36Sopenharmony_ci wb = list_first_entry(&bdi->wb_list, struct bdi_writeback, 77462306a36Sopenharmony_ci bdi_node); 77562306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 77662306a36Sopenharmony_ci wb_shutdown(wb); 77762306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 78062306a36Sopenharmony_ci mutex_unlock(&bdi->cgwb_release_mutex); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/* 78462306a36Sopenharmony_ci * cleanup_offline_cgwbs_workfn - try to release dying cgwbs 78562306a36Sopenharmony_ci * 78662306a36Sopenharmony_ci * Try to release dying cgwbs by switching attached inodes to the nearest 78762306a36Sopenharmony_ci * living ancestor's writeback. Processed wbs are placed at the end 78862306a36Sopenharmony_ci * of the list to guarantee the forward progress. 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_cistatic void cleanup_offline_cgwbs_workfn(struct work_struct *work) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci struct bdi_writeback *wb; 79362306a36Sopenharmony_ci LIST_HEAD(processed); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci while (!list_empty(&offline_cgwbs)) { 79862306a36Sopenharmony_ci wb = list_first_entry(&offline_cgwbs, struct bdi_writeback, 79962306a36Sopenharmony_ci offline_node); 80062306a36Sopenharmony_ci list_move(&wb->offline_node, &processed); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* 80362306a36Sopenharmony_ci * If wb is dirty, cleaning up the writeback by switching 80462306a36Sopenharmony_ci * attached inodes will result in an effective removal of any 80562306a36Sopenharmony_ci * bandwidth restrictions, which isn't the goal. Instead, 80662306a36Sopenharmony_ci * it can be postponed until the next time, when all io 80762306a36Sopenharmony_ci * will be likely completed. If in the meantime some inodes 80862306a36Sopenharmony_ci * will get re-dirtied, they should be eventually switched to 80962306a36Sopenharmony_ci * a new cgwb. 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_ci if (wb_has_dirty_io(wb)) 81262306a36Sopenharmony_ci continue; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (!wb_tryget(wb)) 81562306a36Sopenharmony_ci continue; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 81862306a36Sopenharmony_ci while (cleanup_offline_cgwb(wb)) 81962306a36Sopenharmony_ci cond_resched(); 82062306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci wb_put(wb); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (!list_empty(&processed)) 82662306a36Sopenharmony_ci list_splice_tail(&processed, &offline_cgwbs); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci/** 83262306a36Sopenharmony_ci * wb_memcg_offline - kill all wb's associated with a memcg being offlined 83362306a36Sopenharmony_ci * @memcg: memcg being offlined 83462306a36Sopenharmony_ci * 83562306a36Sopenharmony_ci * Also prevents creation of any new wb's associated with @memcg. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_civoid wb_memcg_offline(struct mem_cgroup *memcg) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct list_head *memcg_cgwb_list = &memcg->cgwb_list; 84062306a36Sopenharmony_ci struct bdi_writeback *wb, *next; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 84362306a36Sopenharmony_ci list_for_each_entry_safe(wb, next, memcg_cgwb_list, memcg_node) 84462306a36Sopenharmony_ci cgwb_kill(wb); 84562306a36Sopenharmony_ci memcg_cgwb_list->next = NULL; /* prevent new wb's */ 84662306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci queue_work(system_unbound_wq, &cleanup_offline_cgwbs_work); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/** 85262306a36Sopenharmony_ci * wb_blkcg_offline - kill all wb's associated with a blkcg being offlined 85362306a36Sopenharmony_ci * @css: blkcg being offlined 85462306a36Sopenharmony_ci * 85562306a36Sopenharmony_ci * Also prevents creation of any new wb's associated with @blkcg. 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_civoid wb_blkcg_offline(struct cgroup_subsys_state *css) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct bdi_writeback *wb, *next; 86062306a36Sopenharmony_ci struct list_head *list = blkcg_get_cgwb_list(css); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 86362306a36Sopenharmony_ci list_for_each_entry_safe(wb, next, list, blkcg_node) 86462306a36Sopenharmony_ci cgwb_kill(wb); 86562306a36Sopenharmony_ci list->next = NULL; /* prevent new wb's */ 86662306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic void cgwb_bdi_register(struct backing_dev_info *bdi) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci spin_lock_irq(&cgwb_lock); 87262306a36Sopenharmony_ci list_add_tail_rcu(&bdi->wb.bdi_node, &bdi->wb_list); 87362306a36Sopenharmony_ci spin_unlock_irq(&cgwb_lock); 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic int __init cgwb_init(void) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci /* 87962306a36Sopenharmony_ci * There can be many concurrent release work items overwhelming 88062306a36Sopenharmony_ci * system_wq. Put them in a separate wq and limit concurrency. 88162306a36Sopenharmony_ci * There's no point in executing many of these in parallel. 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ci cgwb_release_wq = alloc_workqueue("cgwb_release", 0, 1); 88462306a36Sopenharmony_ci if (!cgwb_release_wq) 88562306a36Sopenharmony_ci return -ENOMEM; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_cisubsys_initcall(cgwb_init); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci#else /* CONFIG_CGROUP_WRITEBACK */ 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int cgwb_bdi_init(struct backing_dev_info *bdi) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci return wb_init(&bdi->wb, bdi, GFP_KERNEL); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic void cgwb_bdi_unregister(struct backing_dev_info *bdi) { } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic void cgwb_bdi_register(struct backing_dev_info *bdi) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci list_add_tail_rcu(&bdi->wb.bdi_node, &bdi->wb_list); 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic void cgwb_remove_from_bdi_list(struct bdi_writeback *wb) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci list_del_rcu(&wb->bdi_node); 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci#endif /* CONFIG_CGROUP_WRITEBACK */ 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ciint bdi_init(struct backing_dev_info *bdi) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci bdi->dev = NULL; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci kref_init(&bdi->refcnt); 91762306a36Sopenharmony_ci bdi->min_ratio = 0; 91862306a36Sopenharmony_ci bdi->max_ratio = 100 * BDI_RATIO_SCALE; 91962306a36Sopenharmony_ci bdi->max_prop_frac = FPROP_FRAC_BASE; 92062306a36Sopenharmony_ci INIT_LIST_HEAD(&bdi->bdi_list); 92162306a36Sopenharmony_ci INIT_LIST_HEAD(&bdi->wb_list); 92262306a36Sopenharmony_ci init_waitqueue_head(&bdi->wb_waitq); 92362306a36Sopenharmony_ci bdi->last_bdp_sleep = jiffies; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return cgwb_bdi_init(bdi); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistruct backing_dev_info *bdi_alloc(int node_id) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct backing_dev_info *bdi; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci bdi = kzalloc_node(sizeof(*bdi), GFP_KERNEL, node_id); 93362306a36Sopenharmony_ci if (!bdi) 93462306a36Sopenharmony_ci return NULL; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (bdi_init(bdi)) { 93762306a36Sopenharmony_ci kfree(bdi); 93862306a36Sopenharmony_ci return NULL; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci bdi->capabilities = BDI_CAP_WRITEBACK | BDI_CAP_WRITEBACK_ACCT; 94162306a36Sopenharmony_ci bdi->ra_pages = VM_READAHEAD_PAGES; 94262306a36Sopenharmony_ci bdi->io_pages = VM_READAHEAD_PAGES; 94362306a36Sopenharmony_ci timer_setup(&bdi->laptop_mode_wb_timer, laptop_mode_timer_fn, 0); 94462306a36Sopenharmony_ci return bdi; 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ciEXPORT_SYMBOL(bdi_alloc); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic struct rb_node **bdi_lookup_rb_node(u64 id, struct rb_node **parentp) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct rb_node **p = &bdi_tree.rb_node; 95162306a36Sopenharmony_ci struct rb_node *parent = NULL; 95262306a36Sopenharmony_ci struct backing_dev_info *bdi; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci lockdep_assert_held(&bdi_lock); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci while (*p) { 95762306a36Sopenharmony_ci parent = *p; 95862306a36Sopenharmony_ci bdi = rb_entry(parent, struct backing_dev_info, rb_node); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (bdi->id > id) 96162306a36Sopenharmony_ci p = &(*p)->rb_left; 96262306a36Sopenharmony_ci else if (bdi->id < id) 96362306a36Sopenharmony_ci p = &(*p)->rb_right; 96462306a36Sopenharmony_ci else 96562306a36Sopenharmony_ci break; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (parentp) 96962306a36Sopenharmony_ci *parentp = parent; 97062306a36Sopenharmony_ci return p; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci/** 97462306a36Sopenharmony_ci * bdi_get_by_id - lookup and get bdi from its id 97562306a36Sopenharmony_ci * @id: bdi id to lookup 97662306a36Sopenharmony_ci * 97762306a36Sopenharmony_ci * Find bdi matching @id and get it. Returns NULL if the matching bdi 97862306a36Sopenharmony_ci * doesn't exist or is already unregistered. 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_cistruct backing_dev_info *bdi_get_by_id(u64 id) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct backing_dev_info *bdi = NULL; 98362306a36Sopenharmony_ci struct rb_node **p; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci spin_lock_bh(&bdi_lock); 98662306a36Sopenharmony_ci p = bdi_lookup_rb_node(id, NULL); 98762306a36Sopenharmony_ci if (*p) { 98862306a36Sopenharmony_ci bdi = rb_entry(*p, struct backing_dev_info, rb_node); 98962306a36Sopenharmony_ci bdi_get(bdi); 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci spin_unlock_bh(&bdi_lock); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return bdi; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ciint bdi_register_va(struct backing_dev_info *bdi, const char *fmt, va_list args) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci struct device *dev; 99962306a36Sopenharmony_ci struct rb_node *parent, **p; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (bdi->dev) /* The driver needs to use separate queues per device */ 100262306a36Sopenharmony_ci return 0; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci vsnprintf(bdi->dev_name, sizeof(bdi->dev_name), fmt, args); 100562306a36Sopenharmony_ci dev = device_create(&bdi_class, NULL, MKDEV(0, 0), bdi, bdi->dev_name); 100662306a36Sopenharmony_ci if (IS_ERR(dev)) 100762306a36Sopenharmony_ci return PTR_ERR(dev); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci cgwb_bdi_register(bdi); 101062306a36Sopenharmony_ci bdi->dev = dev; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci bdi_debug_register(bdi, dev_name(dev)); 101362306a36Sopenharmony_ci set_bit(WB_registered, &bdi->wb.state); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci spin_lock_bh(&bdi_lock); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci bdi->id = ++bdi_id_cursor; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci p = bdi_lookup_rb_node(bdi->id, &parent); 102062306a36Sopenharmony_ci rb_link_node(&bdi->rb_node, parent, p); 102162306a36Sopenharmony_ci rb_insert_color(&bdi->rb_node, &bdi_tree); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci list_add_tail_rcu(&bdi->bdi_list, &bdi_list); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci spin_unlock_bh(&bdi_lock); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci trace_writeback_bdi_register(bdi); 102862306a36Sopenharmony_ci return 0; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ciint bdi_register(struct backing_dev_info *bdi, const char *fmt, ...) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci va_list args; 103462306a36Sopenharmony_ci int ret; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci va_start(args, fmt); 103762306a36Sopenharmony_ci ret = bdi_register_va(bdi, fmt, args); 103862306a36Sopenharmony_ci va_end(args); 103962306a36Sopenharmony_ci return ret; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ciEXPORT_SYMBOL(bdi_register); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_civoid bdi_set_owner(struct backing_dev_info *bdi, struct device *owner) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci WARN_ON_ONCE(bdi->owner); 104662306a36Sopenharmony_ci bdi->owner = owner; 104762306a36Sopenharmony_ci get_device(owner); 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci/* 105162306a36Sopenharmony_ci * Remove bdi from bdi_list, and ensure that it is no longer visible 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_cistatic void bdi_remove_from_list(struct backing_dev_info *bdi) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci spin_lock_bh(&bdi_lock); 105662306a36Sopenharmony_ci rb_erase(&bdi->rb_node, &bdi_tree); 105762306a36Sopenharmony_ci list_del_rcu(&bdi->bdi_list); 105862306a36Sopenharmony_ci spin_unlock_bh(&bdi_lock); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci synchronize_rcu_expedited(); 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_civoid bdi_unregister(struct backing_dev_info *bdi) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci del_timer_sync(&bdi->laptop_mode_wb_timer); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* make sure nobody finds us on the bdi_list anymore */ 106862306a36Sopenharmony_ci bdi_remove_from_list(bdi); 106962306a36Sopenharmony_ci wb_shutdown(&bdi->wb); 107062306a36Sopenharmony_ci cgwb_bdi_unregister(bdi); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci * If this BDI's min ratio has been set, use bdi_set_min_ratio() to 107462306a36Sopenharmony_ci * update the global bdi_min_ratio. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci if (bdi->min_ratio) 107762306a36Sopenharmony_ci bdi_set_min_ratio(bdi, 0); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (bdi->dev) { 108062306a36Sopenharmony_ci bdi_debug_unregister(bdi); 108162306a36Sopenharmony_ci device_unregister(bdi->dev); 108262306a36Sopenharmony_ci bdi->dev = NULL; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (bdi->owner) { 108662306a36Sopenharmony_ci put_device(bdi->owner); 108762306a36Sopenharmony_ci bdi->owner = NULL; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ciEXPORT_SYMBOL(bdi_unregister); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic void release_bdi(struct kref *ref) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct backing_dev_info *bdi = 109562306a36Sopenharmony_ci container_of(ref, struct backing_dev_info, refcnt); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci WARN_ON_ONCE(test_bit(WB_registered, &bdi->wb.state)); 109862306a36Sopenharmony_ci WARN_ON_ONCE(bdi->dev); 109962306a36Sopenharmony_ci wb_exit(&bdi->wb); 110062306a36Sopenharmony_ci kfree(bdi); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_civoid bdi_put(struct backing_dev_info *bdi) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci kref_put(&bdi->refcnt, release_bdi); 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ciEXPORT_SYMBOL(bdi_put); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistruct backing_dev_info *inode_to_bdi(struct inode *inode) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci struct super_block *sb; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (!inode) 111462306a36Sopenharmony_ci return &noop_backing_dev_info; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci sb = inode->i_sb; 111762306a36Sopenharmony_ci#ifdef CONFIG_BLOCK 111862306a36Sopenharmony_ci if (sb_is_blkdev_sb(sb)) 111962306a36Sopenharmony_ci return I_BDEV(inode)->bd_disk->bdi; 112062306a36Sopenharmony_ci#endif 112162306a36Sopenharmony_ci return sb->s_bdi; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ciEXPORT_SYMBOL(inode_to_bdi); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ciconst char *bdi_dev_name(struct backing_dev_info *bdi) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci if (!bdi || !bdi->dev) 112862306a36Sopenharmony_ci return bdi_unknown_name; 112962306a36Sopenharmony_ci return bdi->dev_name; 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bdi_dev_name); 1132