@@ -1465,6 +1465,31 @@ static long writeback_chunk_size(struct bdi_writeback *wb,
return pages;
}
+static long writeback_sb_metadata(struct super_block *sb,
+ struct bdi_writeback *wb,
+ struct wb_writeback_work *work)
+{
+ struct writeback_control wbc = {
+ .sync_mode = work->sync_mode,
+ .tagged_writepages = work->tagged_writepages,
+ .for_kupdate = work->for_kupdate,
+ .for_background = work->for_background,
+ .for_sync = work->for_sync,
+ .range_cyclic = work->range_cyclic,
+ .range_start = 0,
+ .range_end = LLONG_MAX,
+ };
+ long write_chunk;
+
+ write_chunk = writeback_chunk_size(wb, work);
+ wbc.nr_to_write = write_chunk;
+ sb->s_op->write_metadata(sb, &wbc);
+ work->nr_pages -= write_chunk - wbc.nr_to_write;
+
+ return write_chunk - wbc.nr_to_write;
+}
+
+
/*
* Write a portion of b_io inodes which belong to @sb.
*
@@ -1491,6 +1516,7 @@ static long writeback_sb_inodes(struct super_block *sb,
unsigned long start_time = jiffies;
long write_chunk;
long wrote = 0; /* count both pages and inodes */
+ bool done = false;
while (!list_empty(&wb->b_io)) {
struct inode *inode = wb_inode(wb->b_io.prev);
@@ -1607,12 +1633,18 @@ static long writeback_sb_inodes(struct super_block *sb,
* background threshold and other termination conditions.
*/
if (wrote) {
- if (time_is_before_jiffies(start_time + HZ / 10UL))
- break;
- if (work->nr_pages <= 0)
+ if (time_is_before_jiffies(start_time + HZ / 10UL) ||
+ work->nr_pages <= 0) {
+ done = true;
break;
+ }
}
}
+ if (!done && sb->s_op->write_metadata) {
+ spin_unlock(&wb->list_lock);
+ wrote += writeback_sb_metadata(sb, wb, work);
+ spin_lock(&wb->list_lock);
+ }
return wrote;
}
@@ -1621,6 +1653,7 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
{
unsigned long start_time = jiffies;
long wrote = 0;
+ bool done = false;
while (!list_empty(&wb->b_io)) {
struct inode *inode = wb_inode(wb->b_io.prev);
@@ -1640,12 +1673,39 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
/* refer to the same tests at the end of writeback_sb_inodes */
if (wrote) {
- if (time_is_before_jiffies(start_time + HZ / 10UL))
- break;
- if (work->nr_pages <= 0)
+ if (time_is_before_jiffies(start_time + HZ / 10UL) ||
+ work->nr_pages <= 0) {
+ done = true;
break;
+ }
}
}
+
+ if (!done && wb_stat(wb, WB_METADATA_DIRTY_BYTES)) {
+ LIST_HEAD(list);
+
+ spin_unlock(&wb->list_lock);
+ spin_lock(&wb->bdi->sb_list_lock);
+ list_splice_init(&wb->bdi->dirty_sb_list, &list);
+ while (!list_empty(&list)) {
+ struct super_block *sb;
+
+ sb = list_first_entry(&list, struct super_block,
+ s_bdi_dirty_list);
+ list_move_tail(&sb->s_bdi_dirty_list,
+ &wb->bdi->dirty_sb_list);
+ if (!sb->s_op->write_metadata)
+ continue;
+ if (!trylock_super(sb))
+ continue;
+ spin_unlock(&wb->bdi->sb_list_lock);
+ wrote += writeback_sb_metadata(sb, wb, work);
+ spin_lock(&wb->bdi->sb_list_lock);
+ up_read(&sb->s_umount);
+ }
+ spin_unlock(&wb->bdi->sb_list_lock);
+ spin_lock(&wb->list_lock);
+ }
/* Leave any unwritten inodes on b_io */
return wrote;
}
@@ -215,6 +215,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
spin_lock_init(&s->s_inode_list_lock);
INIT_LIST_HEAD(&s->s_inodes_wb);
spin_lock_init(&s->s_inode_wblist_lock);
+ INIT_LIST_HEAD(&s->s_bdi_dirty_list);
if (list_lru_init_memcg(&s->s_dentry_lru))
goto fail;
@@ -305,6 +306,8 @@ void deactivate_locked_super(struct super_block *s)
{
struct file_system_type *fs = s->s_type;
if (atomic_dec_and_test(&s->s_active)) {
+ struct backing_dev_info *bdi = s->s_bdi;
+
cleancache_invalidate_fs(s);
unregister_shrinker(&s->s_shrink);
fs->kill_sb(s);
@@ -317,6 +320,10 @@ void deactivate_locked_super(struct super_block *s)
list_lru_destroy(&s->s_dentry_lru);
list_lru_destroy(&s->s_inode_lru);
+ spin_lock(&bdi->sb_list_lock);
+ list_del_init(&s->s_bdi_dirty_list);
+ spin_unlock(&bdi->sb_list_lock);
+
put_filesystem(fs);
put_super(s);
} else {
@@ -169,6 +169,8 @@ struct backing_dev_info {
struct timer_list laptop_mode_wb_timer;
+ spinlock_t sb_list_lock;
+ struct list_head dirty_sb_list;
#ifdef CONFIG_DEBUG_FS
struct dentry *debug_dir;
struct dentry *debug_stats;
@@ -1431,6 +1431,8 @@ struct super_block {
spinlock_t s_inode_wblist_lock;
struct list_head s_inodes_wb; /* writeback inodes */
+
+ struct list_head s_bdi_dirty_list;
};
/* Helper functions so that in most cases filesystems will
@@ -1806,6 +1808,8 @@ struct super_operations {
struct shrink_control *);
long (*free_cached_objects)(struct super_block *,
struct shrink_control *);
+ void (*write_metadata)(struct super_block *sb,
+ struct writeback_control *wbc);
};
/*
@@ -785,6 +785,8 @@ int bdi_init(struct backing_dev_info *bdi)
bdi->max_prop_frac = FPROP_FRAC_BASE;
INIT_LIST_HEAD(&bdi->bdi_list);
INIT_LIST_HEAD(&bdi->wb_list);
+ INIT_LIST_HEAD(&bdi->dirty_sb_list);
+ spin_lock_init(&bdi->sb_list_lock);
init_waitqueue_head(&bdi->wb_waitq);
ret = cgwb_bdi_init(bdi);