From 3d48f700203a312ee4840d9fffaef788342dbdfa Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 14 May 2004 20:12:33 -0700 Subject: [PATCH] [PATCH] Fix writeback_inodes-vs-umount race 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. --- fs/fs-writeback.c | 4 ++++ fs/super.c | 21 ++++++++++++++++++--- include/linux/fs.h | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index ff91893977e42..aa414468bda71 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -397,12 +397,16 @@ writeback_inodes(struct writeback_control *wbc) 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 --git a/fs/super.c b/fs/super.c index 12427a6e7f9a6..8d9bff178bcac 100644 --- a/fs/super.c +++ b/fs/super.c @@ -101,6 +101,21 @@ static inline void destroy_super(struct super_block *s) /* 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 super_block *s) * 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 --git a/include/linux/fs.h b/include/linux/fs.h index 8971ae34dbdf4..b1734b045c6b4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1114,6 +1114,7 @@ struct super_block *sget(struct file_system_type *type, 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 */ -- 2.53.0