

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 */

_
