|
@@ -207,6 +207,34 @@ void deactivate_super(struct super_block *s)
|
|
|
|
|
|
EXPORT_SYMBOL(deactivate_super);
|
|
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
|
|
* grab_super - acquire an active reference
|
|
* @s: reference we are trying to make active
|
|
* @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;
|
|
sb->s_flags = flags;
|
|
err = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
|
|
err = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
|
|
if (err) {
|
|
if (err) {
|
|
- up_write(&sb->s_umount);
|
|
|
|
- deactivate_super(sb);
|
|
|
|
|
|
+ deactivate_locked_super(sb);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -854,8 +881,7 @@ int get_sb_bdev(struct file_system_type *fs_type,
|
|
|
|
|
|
if (s->s_root) {
|
|
if (s->s_root) {
|
|
if ((flags ^ s->s_flags) & MS_RDONLY) {
|
|
if ((flags ^ s->s_flags) & MS_RDONLY) {
|
|
- up_write(&s->s_umount);
|
|
|
|
- deactivate_super(s);
|
|
|
|
|
|
+ deactivate_locked_super(s);
|
|
error = -EBUSY;
|
|
error = -EBUSY;
|
|
goto error_bdev;
|
|
goto error_bdev;
|
|
}
|
|
}
|
|
@@ -870,8 +896,7 @@ int get_sb_bdev(struct file_system_type *fs_type,
|
|
sb_set_blocksize(s, block_size(bdev));
|
|
sb_set_blocksize(s, block_size(bdev));
|
|
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
|
|
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
|
|
if (error) {
|
|
if (error) {
|
|
- up_write(&s->s_umount);
|
|
|
|
- deactivate_super(s);
|
|
|
|
|
|
+ deactivate_locked_super(s);
|
|
goto error;
|
|
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);
|
|
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
|
|
if (error) {
|
|
if (error) {
|
|
- up_write(&s->s_umount);
|
|
|
|
- deactivate_super(s);
|
|
|
|
|
|
+ deactivate_locked_super(s);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
s->s_flags |= MS_ACTIVE;
|
|
s->s_flags |= MS_ACTIVE;
|
|
@@ -952,8 +976,7 @@ int get_sb_single(struct file_system_type *fs_type,
|
|
s->s_flags = flags;
|
|
s->s_flags = flags;
|
|
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
|
|
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
|
|
if (error) {
|
|
if (error) {
|
|
- up_write(&s->s_umount);
|
|
|
|
- deactivate_super(s);
|
|
|
|
|
|
+ deactivate_locked_super(s);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
s->s_flags |= MS_ACTIVE;
|
|
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;
|
|
return mnt;
|
|
out_sb:
|
|
out_sb:
|
|
dput(mnt->mnt_root);
|
|
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:
|
|
out_free_secdata:
|
|
free_secdata(secdata);
|
|
free_secdata(secdata);
|
|
out_mnt:
|
|
out_mnt:
|