From: Russ Weight This patch changes all cases where the MS_ACTIVE bit gets set. This is done to eliminate a race condition that can occur if an inode is allocated and then released (using iput) during the ->fill_super functions. The race condition is between kswapd and mount. For most filesystems this can only happen in an error path when kswapd is running concurrently. For isofs, however, the error can occur in a more common code path (which is how the bug was found). The changes are fairly straight forward. There are a couple of unique cases: ext3_orphan_cleanup() was setting MS_ACTIVE for the CONFIG_QUOTA case. Since ext3_orphan_cleanup() is only called by ext3_fill_super(), this is no longer necessary since the MS_ACTIVE will already be set. Reiserfs: For the CONFIG_QUOTA case, finish_unfinished() ensures that MS_ACTIVE is set, and then conditionally turns it off depending on its initial state. finish_unfinished() is called by two functions: reiserfs_remount and reiserfs_fill_super. The MS_ACTIVE flag was previously NOT set for reiserfs_fill_super, but that has changed now - it will always be set. The MS_ACTIVE flag is also set in the reiserfs_remount case, so the manipulation of the MS_ACTIVE in finish_unfinished() is no longer necessary. Signed-off-by: Andrew Morton --- fs/afs/super.c | 4 ++-- fs/cifs/cifsfs.c | 4 ++-- fs/ext3/super.c | 2 -- fs/jffs2/super.c | 4 ++-- fs/libfs.c | 4 ++-- fs/nfs/inode.c | 8 ++++---- fs/reiserfs/super.c | 11 ----------- fs/super.c | 12 ++++++------ 8 files changed, 18 insertions(+), 31 deletions(-) diff -puN fs/afs/super.c~set-ms_active-bit-before-calling-fill_super fs/afs/super.c --- 25/fs/afs/super.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.711887560 -0700 +++ 25-akpm/fs/afs/super.c 2005-04-26 01:24:21.724885584 -0700 @@ -339,15 +339,15 @@ static struct super_block *afs_get_sb(st if (IS_ERR(sb)) goto error; - sb->s_flags = flags; + sb->s_flags = flags | MS_ACTIVE; ret = afs_fill_super(sb, ¶ms, flags & MS_VERBOSE ? 1 : 0); if (ret < 0) { + sb->s_flags &= ~MS_ACTIVE; up_write(&sb->s_umount); deactivate_super(sb); goto error; } - sb->s_flags |= MS_ACTIVE; afs_put_volume(params.volume); afs_put_cell(params.default_cell); diff -puN fs/cifs/cifsfs.c~set-ms_active-bit-before-calling-fill_super fs/cifs/cifsfs.c --- 25/fs/cifs/cifsfs.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.712887408 -0700 +++ 25-akpm/fs/cifs/cifsfs.c 2005-04-26 01:24:21.725885432 -0700 @@ -431,15 +431,15 @@ cifs_get_sb(struct file_system_type *fs_ if (IS_ERR(sb)) return sb; - sb->s_flags = flags; + sb->s_flags = flags | MS_ACTIVE; rc = cifs_read_super(sb, data, dev_name, flags & MS_VERBOSE ? 1 : 0); if (rc) { + sb->s_flags &= ~MS_ACTIVE; up_write(&sb->s_umount); deactivate_super(sb); return ERR_PTR(rc); } - sb->s_flags |= MS_ACTIVE; return sb; } diff -puN fs/ext3/super.c~set-ms_active-bit-before-calling-fill_super fs/ext3/super.c --- 25/fs/ext3/super.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.714887104 -0700 +++ 25-akpm/fs/ext3/super.c 2005-04-26 01:24:21.727885128 -0700 @@ -1124,8 +1124,6 @@ static void ext3_orphan_cleanup (struct sb->s_flags &= ~MS_RDONLY; } #ifdef CONFIG_QUOTA - /* Needed for iput() to work correctly and not trash data */ - sb->s_flags |= MS_ACTIVE; /* Turn on quotas so that they are updated correctly */ for (i = 0; i < MAXQUOTAS; i++) { if (EXT3_SB(sb)->s_qf_names[i]) { diff -puN fs/jffs2/super.c~set-ms_active-bit-before-calling-fill_super fs/jffs2/super.c --- 25/fs/jffs2/super.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.715886952 -0700 +++ 25-akpm/fs/jffs2/super.c 2005-04-26 01:24:21.728884976 -0700 @@ -141,18 +141,18 @@ static struct super_block *jffs2_get_sb_ mtd->index, mtd->name)); sb->s_op = &jffs2_super_operations; - sb->s_flags = flags | MS_NOATIME; + sb->s_flags = flags | MS_NOATIME | MS_ACTIVE; ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); if (ret) { /* Failure case... */ + sb->s_flags &= ~MS_ACTIVE; up_write(&sb->s_umount); deactivate_super(sb); return ERR_PTR(ret); } - sb->s_flags |= MS_ACTIVE; return sb; out_put: diff -puN fs/libfs.c~set-ms_active-bit-before-calling-fill_super fs/libfs.c --- 25/fs/libfs.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.716886800 -0700 +++ 25-akpm/fs/libfs.c 2005-04-26 01:24:21.728884976 -0700 @@ -206,7 +206,7 @@ get_sb_pseudo(struct file_system_type *f if (IS_ERR(s)) return s; - s->s_flags = MS_NOUSER; + s->s_flags = MS_NOUSER | MS_ACTIVE; s->s_maxbytes = ~0ULL; s->s_blocksize = 1024; s->s_blocksize_bits = 10; @@ -228,10 +228,10 @@ get_sb_pseudo(struct file_system_type *f dentry->d_parent = dentry; d_instantiate(dentry, root); s->s_root = dentry; - s->s_flags |= MS_ACTIVE; return s; Enomem: + s->s_flags &= ~MS_ACTIVE; up_write(&s->s_umount); deactivate_super(s); return ERR_PTR(-ENOMEM); diff -puN fs/nfs/inode.c~set-ms_active-bit-before-calling-fill_super fs/nfs/inode.c --- 25/fs/nfs/inode.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.718886496 -0700 +++ 25-akpm/fs/nfs/inode.c 2005-04-26 01:24:21.731884520 -0700 @@ -1519,15 +1519,15 @@ static struct super_block *nfs_get_sb(st if (IS_ERR(s) || s->s_root) goto out_rpciod_down; - s->s_flags = flags; + s->s_flags = flags|MS_ACTIVE; error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { + s->s_flags &= ~MS_ACTIVE; up_write(&s->s_umount); deactivate_super(s); return ERR_PTR(error); } - s->s_flags |= MS_ACTIVE; return s; out_rpciod_down: rpciod_down(); @@ -1875,15 +1875,15 @@ static struct super_block *nfs4_get_sb(s if (IS_ERR(s) || s->s_root) goto out_free; - s->s_flags = flags; + s->s_flags = flags|MS_ACTIVE; error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { + s->s_flags &= ~MS_ACTIVE; up_write(&s->s_umount); deactivate_super(s); return ERR_PTR(error); } - s->s_flags |= MS_ACTIVE; return s; out_err: s = (struct super_block *)p; diff -puN fs/reiserfs/super.c~set-ms_active-bit-before-calling-fill_super fs/reiserfs/super.c --- 25/fs/reiserfs/super.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.719886344 -0700 +++ 25-akpm/fs/reiserfs/super.c 2005-04-26 01:24:21.733884216 -0700 @@ -158,7 +158,6 @@ static int finish_unfinished (struct sup int truncate; #ifdef CONFIG_QUOTA int i; - int ms_active_set; #endif @@ -168,13 +167,6 @@ static int finish_unfinished (struct sup max_cpu_key.key_length = 3; #ifdef CONFIG_QUOTA - /* Needed for iput() to work correctly and not trash data */ - if (s->s_flags & MS_ACTIVE) { - ms_active_set = 0; - } else { - ms_active_set = 1; - s->s_flags |= MS_ACTIVE; - } /* Turn on quotas so that they are updated correctly */ for (i = 0; i < MAXQUOTAS; i++) { if (REISERFS_SB(s)->s_qf_names[i]) { @@ -282,9 +274,6 @@ static int finish_unfinished (struct sup if (sb_dqopt(s)->files[i]) vfs_quota_off_mount(s, i); } - if (ms_active_set) - /* Restore the flag back */ - s->s_flags &= ~MS_ACTIVE; #endif pathrelse (&path); if (done) diff -puN fs/super.c~set-ms_active-bit-before-calling-fill_super fs/super.c --- 25/fs/super.c~set-ms_active-bit-before-calling-fill_super 2005-04-26 01:24:21.721886040 -0700 +++ 25-akpm/fs/super.c 2005-04-26 01:24:21.734884064 -0700 @@ -702,17 +702,17 @@ struct super_block *get_sb_bdev(struct f } else { char b[BDEVNAME_SIZE]; - s->s_flags = flags; + s->s_flags = flags | MS_ACTIVE; strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id)); s->s_old_blocksize = block_size(bdev); sb_set_blocksize(s, s->s_old_blocksize); error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { + s->s_flags &= ~MS_ACTIVE; up_write(&s->s_umount); deactivate_super(s); s = ERR_PTR(error); } else { - s->s_flags |= MS_ACTIVE; bdev_uevent(bdev, KOBJ_MOUNT); } } @@ -748,15 +748,15 @@ struct super_block *get_sb_nodev(struct if (IS_ERR(s)) return s; - s->s_flags = flags; + s->s_flags = flags | MS_ACTIVE; error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { + s->s_flags &= ~MS_ACTIVE; up_write(&s->s_umount); deactivate_super(s); return ERR_PTR(error); } - s->s_flags |= MS_ACTIVE; return s; } @@ -778,14 +778,14 @@ struct super_block *get_sb_single(struct if (IS_ERR(s)) return s; if (!s->s_root) { - s->s_flags = flags; + s->s_flags = flags | MS_ACTIVE; error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { + s->s_flags &= ~MS_ACTIVE; up_write(&s->s_umount); deactivate_super(s); return ERR_PTR(error); } - s->s_flags |= MS_ACTIVE; } do_remount_sb(s, flags, data, 0); return s; _