Fix bug identified by Chris Mason. If writeback_inodes is left holding a ref on the superblock's last inode then the superblock list walk can race with umount and the superblock can be released. Take and put a ref against the superblock to fix that. --- 25-akpm/fs/fs-writeback.c | 4 ++++ 25-akpm/fs/super.c | 21 ++++++++++++++++++--- 25-akpm/include/linux/fs.h | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff -puN fs/fs-writeback.c~writeback_inodes-fix fs/fs-writeback.c --- 25/fs/fs-writeback.c~writeback_inodes-fix 2004-05-12 20:31:01.292112456 -0700 +++ 25-akpm/fs/fs-writeback.c 2004-05-12 20:31:01.298111544 -0700 @@ -397,12 +397,16 @@ writeback_inodes(struct writeback_contro spin_lock(&inode_lock); spin_lock(&sb_lock); +restart: sb = sb_entry(super_blocks.prev); for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.prev)) { if (!list_empty(&sb->s_dirty) || !list_empty(&sb->s_io)) { + sb->s_count++; spin_unlock(&sb_lock); sync_sb_inodes(sb, wbc); spin_lock(&sb_lock); + if (__put_super(sb)) + goto restart; } if (wbc->nr_to_write <= 0) break; diff -puN fs/super.c~writeback_inodes-fix fs/super.c --- 25/fs/super.c~writeback_inodes-fix 2004-05-12 20:31:01.293112304 -0700 +++ 25-akpm/fs/super.c 2004-05-12 20:31:01.299111392 -0700 @@ -101,6 +101,21 @@ static inline void destroy_super(struct /* Superblock refcounting */ +/* + * Drop a superblock's refcount. Returns non-zero if the superblock was + * destroyed. The caller must hold sb_lock. + */ +int __put_super(struct super_block *sb) +{ + int ret = 0; + + if (!--sb->s_count) { + destroy_super(sb); + ret = 1; + } + return ret; +} + /** * put_super - drop a temporary reference to superblock * @s: superblock in question @@ -108,14 +123,14 @@ static inline void destroy_super(struct * Drops a temporary reference, frees superblock if there's no * references left. */ -static inline void put_super(struct super_block *s) +static void put_super(struct super_block *sb) { spin_lock(&sb_lock); - if (!--s->s_count) - destroy_super(s); + __put_super(sb); spin_unlock(&sb_lock); } + /** * deactivate_super - drop an active reference to superblock * @s: superblock to deactivate diff -puN include/linux/fs.h~writeback_inodes-fix include/linux/fs.h --- 25/include/linux/fs.h~writeback_inodes-fix 2004-05-12 20:31:01.295112000 -0700 +++ 25-akpm/include/linux/fs.h 2004-05-12 20:31:01.300111240 -0700 @@ -1114,6 +1114,7 @@ struct super_block *sget(struct file_sys void *data); struct super_block *get_sb_pseudo(struct file_system_type *, char *, struct super_operations *ops, unsigned long); +int __put_super(struct super_block *sb); void unnamed_dev_init(void); /* Alas, no aliases. Too much hassle with bringing module.h everywhere */ _