New helper: deactivate_locked_super()
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 6 May 2009 05:07:50 +0000 (01:07 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 9 May 2009 14:49:39 +0000 (10:49 -0400)
Does equivalent of up_write(&s->s_umount); deactivate_super(s);
However, it does not does not unlock it until it's all over.
As the result, it's safe to use to dispose of new superblock on ->get_sb()
failure exits - nobody will see the sucker until it's all over.
Equivalent using up_write/deactivate_super is safe for that purpose
if superblock is either safe to use or has NULL ->s_root when we unlock.
Normally filesystems take the required precautions, but
a) we do have bugs in that area in some of them.
b) up_write/deactivate_super sequence is extremely common,
so the helper makes sense anyway.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/super.c
include/linux/fs.h

index 786fe7d72790ac5646197224fc2af88222dade0a..a9dc4c33ef4dd6d1c1e2aa3ffda48192ce0967d5 100644 (file)
@@ -207,6 +207,34 @@ void deactivate_super(struct super_block *s)
 
 EXPORT_SYMBOL(deactivate_super);
 
+/**
+ *     deactivate_locked_super -       drop an active reference to superblock
+ *     @s: superblock to deactivate
+ *
+ *     Equivalent of up_write(&s->s_umount); deactivate_super(s);, except that
+ *     it does not unlock it until it's all over.  As the result, it's safe to
+ *     use to dispose of new superblock on ->get_sb() failure exits - nobody
+ *     will see the sucker until it's all over.  Equivalent using up_write +
+ *     deactivate_super is safe for that purpose only if superblock is either
+ *     safe to use or has NULL ->s_root when we unlock.
+ */
+void deactivate_locked_super(struct super_block *s)
+{
+       struct file_system_type *fs = s->s_type;
+       if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {
+               s->s_count -= S_BIAS-1;
+               spin_unlock(&sb_lock);
+               vfs_dq_off(s, 0);
+               fs->kill_sb(s);
+               put_filesystem(fs);
+               put_super(s);
+       } else {
+               up_write(&s->s_umount);
+       }
+}
+
+EXPORT_SYMBOL(deactivate_locked_super);
+
 /**
  *     grab_super - acquire an active reference
  *     @s: reference we are trying to make active
@@ -797,8 +825,7 @@ int get_sb_ns(struct file_system_type *fs_type, int flags, void *data,
                sb->s_flags = flags;
                err = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
                if (err) {
-                       up_write(&sb->s_umount);
-                       deactivate_super(sb);
+                       deactivate_locked_super(sb);
                        return err;
                }
 
@@ -854,8 +881,7 @@ int get_sb_bdev(struct file_system_type *fs_type,
 
        if (s->s_root) {
                if ((flags ^ s->s_flags) & MS_RDONLY) {
-                       up_write(&s->s_umount);
-                       deactivate_super(s);
+                       deactivate_locked_super(s);
                        error = -EBUSY;
                        goto error_bdev;
                }
@@ -870,8 +896,7 @@ int get_sb_bdev(struct file_system_type *fs_type,
                sb_set_blocksize(s, block_size(bdev));
                error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
                if (error) {
-                       up_write(&s->s_umount);
-                       deactivate_super(s);
+                       deactivate_locked_super(s);
                        goto error;
                }
 
@@ -921,8 +946,7 @@ int get_sb_nodev(struct file_system_type *fs_type,
 
        error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
        if (error) {
-               up_write(&s->s_umount);
-               deactivate_super(s);
+               deactivate_locked_super(s);
                return error;
        }
        s->s_flags |= MS_ACTIVE;
@@ -952,8 +976,7 @@ int get_sb_single(struct file_system_type *fs_type,
                s->s_flags = flags;
                error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
                if (error) {
-                       up_write(&s->s_umount);
-                       deactivate_super(s);
+                       deactivate_locked_super(s);
                        return error;
                }
                s->s_flags |= MS_ACTIVE;
@@ -1006,8 +1029,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
        return mnt;
 out_sb:
        dput(mnt->mnt_root);
-       up_write(&mnt->mnt_sb->s_umount);
-       deactivate_super(mnt->mnt_sb);
+       deactivate_locked_super(mnt->mnt_sb);
 out_free_secdata:
        free_secdata(secdata);
 out_mnt:
index 5bed436f4353978d8cbc19c3efa0ce2c11d87083..11484d08042c4952883c1197e17e870998de2055 100644 (file)
@@ -1775,6 +1775,7 @@ void kill_block_super(struct super_block *sb);
 void kill_anon_super(struct super_block *sb);
 void kill_litter_super(struct super_block *sb);
 void deactivate_super(struct super_block *sb);
+void deactivate_locked_super(struct super_block *sb);
 int set_anon_super(struct super_block *s, void *data);
 struct super_block *sget(struct file_system_type *type,
                        int (*test)(struct super_block *,void *),