Merge commit 'v2.6.30' into for-2.6.31
[safe/jmp/linux-2.6] / fs / super.c
index 5c4c94d..1943fdf 100644 (file)
@@ -15,7 +15,7 @@
  *  Added kerneld support: Jacques Gelinas and Bjorn Ekwall
  *  Added change_root: Werner Almesberger & Hans Lermen, Feb '96
  *  Added options to /proc/mounts:
- *    Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
+ *    Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
  *  Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998
  *  Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000
  */
 #include <linux/idr.h>
 #include <linux/kobject.h>
 #include <linux/mutex.h>
+#include <linux/file.h>
+#include <linux/async.h>
 #include <asm/uaccess.h>
+#include "internal.h"
 
 
-void get_filesystem(struct file_system_type *fs);
-void put_filesystem(struct file_system_type *fs);
-struct file_system_type *get_fs_type(const char *name);
-
 LIST_HEAD(super_blocks);
 DEFINE_SPINLOCK(sb_lock);
 
@@ -67,10 +66,13 @@ static struct super_block *alloc_super(struct file_system_type *type)
                }
                INIT_LIST_HEAD(&s->s_dirty);
                INIT_LIST_HEAD(&s->s_io);
+               INIT_LIST_HEAD(&s->s_more_io);
                INIT_LIST_HEAD(&s->s_files);
                INIT_LIST_HEAD(&s->s_instances);
                INIT_HLIST_HEAD(&s->s_anon);
                INIT_LIST_HEAD(&s->s_inodes);
+               INIT_LIST_HEAD(&s->s_dentry_lru);
+               INIT_LIST_HEAD(&s->s_async_list);
                init_rwsem(&s->s_umount);
                mutex_init(&s->s_lock);
                lockdep_set_class(&s->s_umount, &type->s_umount_key);
@@ -80,7 +82,22 @@ static struct super_block *alloc_super(struct file_system_type *type)
                 * lock ordering than usbfs:
                 */
                lockdep_set_class(&s->s_lock, &type->s_lock_key);
-               down_write(&s->s_umount);
+               /*
+                * sget() can have s_umount recursion.
+                *
+                * When it cannot find a suitable sb, it allocates a new
+                * one (this one), and tries again to find a suitable old
+                * one.
+                *
+                * In case that succeeds, it will acquire the s_umount
+                * lock of the old one. Since these are clearly distrinct
+                * locks, and this object isn't exposed yet, there's no
+                * risk of deadlocks.
+                *
+                * Annotate this by putting this lock in a different
+                * subclass.
+                */
+               down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING);
                s->s_count = S_BIAS;
                atomic_set(&s->s_active, 1);
                mutex_init(&s->s_vfs_rename_mutex);
@@ -107,6 +124,8 @@ out:
 static inline void destroy_super(struct super_block *s)
 {
        security_sb_free(s);
+       kfree(s->s_subtype);
+       kfree(s->s_options);
        kfree(s);
 }
 
@@ -116,7 +135,7 @@ static inline void destroy_super(struct super_block *s)
  * 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)
+static int __put_super(struct super_block *sb)
 {
        int ret = 0;
 
@@ -178,7 +197,7 @@ void deactivate_super(struct super_block *s)
        if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {
                s->s_count -= S_BIAS-1;
                spin_unlock(&sb_lock);
-               DQUOT_OFF(s);
+               vfs_dq_off(s, 0);
                down_write(&s->s_umount);
                fs->kill_sb(s);
                put_filesystem(fs);
@@ -189,6 +208,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
  *
@@ -199,7 +246,7 @@ EXPORT_SYMBOL(deactivate_super);
  *     success, 0 if we had failed (superblock contents was already dead or
  *     dying when grab_super() had been called).
  */
-static int grab_super(struct super_block *s)
+static int grab_super(struct super_block *s) __releases(sb_lock)
 {
        s->s_count++;
        spin_unlock(&sb_lock);
@@ -220,6 +267,56 @@ static int grab_super(struct super_block *s)
        return 0;
 }
 
+/*
+ * Superblock locking.  We really ought to get rid of these two.
+ */
+void lock_super(struct super_block * sb)
+{
+       get_fs_excl();
+       mutex_lock(&sb->s_lock);
+}
+
+void unlock_super(struct super_block * sb)
+{
+       put_fs_excl();
+       mutex_unlock(&sb->s_lock);
+}
+
+EXPORT_SYMBOL(lock_super);
+EXPORT_SYMBOL(unlock_super);
+
+/*
+ * Write out and wait upon all dirty data associated with this
+ * superblock.  Filesystem data as well as the underlying block
+ * device.  Takes the superblock lock.  Requires a second blkdev
+ * flush by the caller to complete the operation.
+ */
+void __fsync_super(struct super_block *sb)
+{
+       sync_inodes_sb(sb, 0);
+       vfs_dq_sync(sb);
+       lock_super(sb);
+       if (sb->s_dirt && sb->s_op->write_super)
+               sb->s_op->write_super(sb);
+       unlock_super(sb);
+       if (sb->s_op->sync_fs)
+               sb->s_op->sync_fs(sb, 1);
+       sync_blockdev(sb->s_bdev);
+       sync_inodes_sb(sb, 1);
+}
+
+/*
+ * Write out and wait upon all dirty data associated with this
+ * superblock.  Filesystem data as well as the underlying block
+ * device.  Takes the superblock lock.
+ */
+int fsync_super(struct super_block *sb)
+{
+       __fsync_super(sb);
+       return sync_blockdev(sb->s_bdev);
+}
+EXPORT_SYMBOL_GPL(fsync_super);
+
 /**
  *     generic_shutdown_super  -       common helper for ->kill_sb()
  *     @sb: superblock to kill
@@ -229,20 +326,27 @@ static int grab_super(struct super_block *s)
  *     that need destruction out of superblock, call generic_shutdown_super()
  *     and release aforementioned objects.  Note: dentries and inodes _are_
  *     taken care of and do not need specific handling.
+ *
+ *     Upon calling this function, the filesystem may no longer alter or
+ *     rearrange the set of dentries belonging to this super_block, nor may it
+ *     change the attachments of dentries to inodes.
  */
 void generic_shutdown_super(struct super_block *sb)
 {
-       struct dentry *root = sb->s_root;
-       struct super_operations *sop = sb->s_op;
+       const struct super_operations *sop = sb->s_op;
 
-       if (root) {
-               sb->s_root = NULL;
-               shrink_dcache_parent(root);
-               shrink_dcache_sb(sb);
-               dput(root);
+
+       if (sb->s_root) {
+               shrink_dcache_for_umount(sb);
                fsync_super(sb);
                lock_super(sb);
                sb->s_flags &= ~MS_ACTIVE;
+
+               /*
+                * wait for asynchronous fs operations to finish before going further
+                */
+               async_synchronize_full_domain(&sb->s_async_list);
+
                /* bad name - it should be evict_inodes() */
                invalidate_inodes(sb);
                lock_kernel();
@@ -285,21 +389,23 @@ struct super_block *sget(struct file_system_type *type,
                        void *data)
 {
        struct super_block *s = NULL;
-       struct list_head *p;
+       struct super_block *old;
        int err;
 
 retry:
        spin_lock(&sb_lock);
-       if (test) list_for_each(p, &type->fs_supers) {
-               struct super_block *old;
-               old = list_entry(p, struct super_block, s_instances);
-               if (!test(old, data))
-                       continue;
-               if (!grab_super(old))
-                       goto retry;
-               if (s)
-                       destroy_super(s);
-               return old;
+       if (test) {
+               list_for_each_entry(old, &type->fs_supers, s_instances) {
+                       if (!test(old, data))
+                               continue;
+                       if (!grab_super(old))
+                               goto retry;
+                       if (s) {
+                               up_write(&s->s_umount);
+                               destroy_super(s);
+                       }
+                       return old;
+               }
        }
        if (!s) {
                spin_unlock(&sb_lock);
@@ -312,6 +418,7 @@ retry:
        err = set(s, data);
        if (err) {
                spin_unlock(&sb_lock);
+               up_write(&s->s_umount);
                destroy_super(s);
                return ERR_PTR(err);
        }
@@ -370,7 +477,7 @@ restart:
 }
 
 /*
- * Call the ->sync_fs super_op against all filesytems which are r/w and
+ * Call the ->sync_fs super_op against all filesystems which are r/w and
  * which implement it.
  *
  * This operation is careful to avoid the livelock which could easily happen
@@ -378,7 +485,7 @@ restart:
  * is used only here.  We set it against all filesystems and then clear it as
  * we sync them.  So redirtied filesystems are skipped.
  *
- * But if process A is currently running sync_filesytems and then process B
+ * But if process A is currently running sync_filesystems and then process B
  * calls sync_filesystems as well, process B will set all the s_need_sync_fs
  * flags again, which will cause process A to resync everything.  Fix that with
  * a local mutex.
@@ -410,6 +517,7 @@ restart:
                sb->s_count++;
                spin_unlock(&sb_lock);
                down_read(&sb->s_umount);
+               async_synchronize_full_domain(&sb->s_async_list);
                if (sb->s_root && (wait || sb->s_dirt))
                        sb->s_op->sync_fs(sb, wait);
                up_read(&sb->s_umount);
@@ -483,7 +591,7 @@ rescan:
        return NULL;
 }
 
-asmlinkage long sys_ustat(unsigned dev, struct ustat __user * ubuf)
+SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
 {
         struct super_block *s;
         struct ustat tmp;
@@ -508,21 +616,40 @@ out:
 }
 
 /**
- *     mark_files_ro
+ *     mark_files_ro - mark all files read-only
  *     @sb: superblock in question
  *
- *     All files are marked read/only.  We don't care about pending
- *     delete files so this should be used in 'force' mode only
+ *     All files are marked read-only.  We don't care about pending
+ *     delete files so this should be used in 'force' mode only.
  */
 
 static void mark_files_ro(struct super_block *sb)
 {
        struct file *f;
 
+retry:
        file_list_lock();
        list_for_each_entry(f, &sb->s_files, f_u.fu_list) {
-               if (S_ISREG(f->f_dentry->d_inode->i_mode) && file_count(f))
-                       f->f_mode &= ~FMODE_WRITE;
+               struct vfsmount *mnt;
+               if (!S_ISREG(f->f_path.dentry->d_inode->i_mode))
+                      continue;
+               if (!file_count(f))
+                       continue;
+               if (!(f->f_mode & FMODE_WRITE))
+                       continue;
+               f->f_mode &= ~FMODE_WRITE;
+               if (file_check_writeable(f) != 0)
+                       continue;
+               file_release_write(f);
+               mnt = mntget(f->f_path.mnt);
+               file_list_unlock();
+               /*
+                * This can sleep, so we can't hold
+                * the file_list_lock() spinlock.
+                */
+               mnt_drop_write(mnt);
+               mntput(mnt);
+               goto retry;
        }
        file_list_unlock();
 }
@@ -539,9 +666,12 @@ static void mark_files_ro(struct super_block *sb)
 int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
 {
        int retval;
+       int remount_rw;
        
+#ifdef CONFIG_BLOCK
        if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev))
                return -EACCES;
+#endif
        if (flags & MS_RDONLY)
                acct_auto_close(sb);
        shrink_dcache_sb(sb);
@@ -554,7 +684,11 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
                        mark_files_ro(sb);
                else if (!fs_may_remount_ro(sb))
                        return -EBUSY;
+               retval = vfs_dq_off(sb, 1);
+               if (retval < 0 && retval != -ENOSYS)
+                       return -EBUSY;
        }
+       remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
 
        if (sb->s_op->remount_fs) {
                lock_super(sb);
@@ -564,10 +698,12 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
                        return retval;
        }
        sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
+       if (remount_rw)
+               vfs_dq_quota_on_remount(sb);
        return 0;
 }
 
-static void do_emergency_remount(unsigned long foo)
+static void do_emergency_remount(struct work_struct *work)
 {
        struct super_block *sb;
 
@@ -590,12 +726,19 @@ static void do_emergency_remount(unsigned long foo)
                spin_lock(&sb_lock);
        }
        spin_unlock(&sb_lock);
+       kfree(work);
        printk("Emergency Remount complete\n");
 }
 
 void emergency_remount(void)
 {
-       pdflush_operation(do_emergency_remount, 0);
+       struct work_struct *work;
+
+       work = kmalloc(sizeof(*work), GFP_ATOMIC);
+       if (work) {
+               INIT_WORK(work, do_emergency_remount);
+               schedule_work(work);
+       }
 }
 
 /*
@@ -603,7 +746,7 @@ void emergency_remount(void)
  * filesystems which don't use real block-devices.  -- jrs
  */
 
-static struct idr unnamed_dev_idr;
+static DEFINE_IDA(unnamed_dev_ida);
 static DEFINE_SPINLOCK(unnamed_dev_lock);/* protects the above */
 
 int set_anon_super(struct super_block *s, void *data)
@@ -612,10 +755,10 @@ int set_anon_super(struct super_block *s, void *data)
        int error;
 
  retry:
-       if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0)
+       if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0)
                return -ENOMEM;
        spin_lock(&unnamed_dev_lock);
-       error = idr_get_new(&unnamed_dev_idr, NULL, &dev);
+       error = ida_get_new(&unnamed_dev_ida, &dev);
        spin_unlock(&unnamed_dev_lock);
        if (error == -EAGAIN)
                /* We raced and lost with another CPU. */
@@ -625,7 +768,7 @@ int set_anon_super(struct super_block *s, void *data)
 
        if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
                spin_lock(&unnamed_dev_lock);
-               idr_remove(&unnamed_dev_idr, dev);
+               ida_remove(&unnamed_dev_ida, dev);
                spin_unlock(&unnamed_dev_lock);
                return -EMFILE;
        }
@@ -641,17 +784,12 @@ void kill_anon_super(struct super_block *sb)
 
        generic_shutdown_super(sb);
        spin_lock(&unnamed_dev_lock);
-       idr_remove(&unnamed_dev_idr, slot);
+       ida_remove(&unnamed_dev_ida, slot);
        spin_unlock(&unnamed_dev_lock);
 }
 
 EXPORT_SYMBOL(kill_anon_super);
 
-void __init unnamed_dev_init(void)
-{
-       idr_init(&unnamed_dev_idr);
-}
-
 void kill_litter_super(struct super_block *sb)
 {
        if (sb->s_root)
@@ -661,6 +799,46 @@ void kill_litter_super(struct super_block *sb)
 
 EXPORT_SYMBOL(kill_litter_super);
 
+static int ns_test_super(struct super_block *sb, void *data)
+{
+       return sb->s_fs_info == data;
+}
+
+static int ns_set_super(struct super_block *sb, void *data)
+{
+       sb->s_fs_info = data;
+       return set_anon_super(sb, NULL);
+}
+
+int get_sb_ns(struct file_system_type *fs_type, int flags, void *data,
+       int (*fill_super)(struct super_block *, void *, int),
+       struct vfsmount *mnt)
+{
+       struct super_block *sb;
+
+       sb = sget(fs_type, ns_test_super, ns_set_super, data);
+       if (IS_ERR(sb))
+               return PTR_ERR(sb);
+
+       if (!sb->s_root) {
+               int err;
+               sb->s_flags = flags;
+               err = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
+               if (err) {
+                       deactivate_locked_super(sb);
+                       return err;
+               }
+
+               sb->s_flags |= MS_ACTIVE;
+       }
+
+       simple_set_mnt(mnt, sb);
+       return 0;
+}
+
+EXPORT_SYMBOL(get_sb_ns);
+
+#ifdef CONFIG_BLOCK
 static int set_bdev_super(struct super_block *s, void *data)
 {
        s->s_bdev = data;
@@ -673,16 +851,6 @@ static int test_bdev_super(struct super_block *s, void *data)
        return (void *)s->s_bdev == data;
 }
 
-static void bdev_uevent(struct block_device *bdev, enum kobject_action action)
-{
-       if (bdev->bd_disk) {
-               if (bdev->bd_part)
-                       kobject_uevent(&bdev->bd_part->kobj, action);
-               else
-                       kobject_uevent(&bdev->bd_disk->kobj, action);
-       }
-}
-
 int get_sb_bdev(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data,
        int (*fill_super)(struct super_block *, void *, int),
@@ -690,9 +858,13 @@ int get_sb_bdev(struct file_system_type *fs_type,
 {
        struct block_device *bdev;
        struct super_block *s;
+       fmode_t mode = FMODE_READ;
        int error = 0;
 
-       bdev = open_bdev_excl(dev_name, flags, fs_type);
+       if (!(flags & MS_RDONLY))
+               mode |= FMODE_WRITE;
+
+       bdev = open_bdev_exclusive(dev_name, mode, fs_type);
        if (IS_ERR(bdev))
                return PTR_ERR(bdev);
 
@@ -701,44 +873,44 @@ int get_sb_bdev(struct file_system_type *fs_type,
         * will protect the lockfs code from trying to start a snapshot
         * while we are mounting
         */
-       mutex_lock(&bdev->bd_mount_mutex);
+       down(&bdev->bd_mount_sem);
        s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
-       mutex_unlock(&bdev->bd_mount_mutex);
+       up(&bdev->bd_mount_sem);
        if (IS_ERR(s))
                goto error_s;
 
        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;
                }
 
-               close_bdev_excl(bdev);
+               close_bdev_exclusive(bdev, mode);
        } else {
                char b[BDEVNAME_SIZE];
 
                s->s_flags = flags;
+               s->s_mode = mode;
                strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
                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;
                }
 
                s->s_flags |= MS_ACTIVE;
-               bdev_uevent(bdev, KOBJ_MOUNT);
+               bdev->bd_super = s;
        }
 
-       return simple_set_mnt(mnt, s);
+       simple_set_mnt(mnt, s);
+       return 0;
 
 error_s:
        error = PTR_ERR(s);
 error_bdev:
-       close_bdev_excl(bdev);
+       close_bdev_exclusive(bdev, mode);
 error:
        return error;
 }
@@ -748,14 +920,16 @@ EXPORT_SYMBOL(get_sb_bdev);
 void kill_block_super(struct super_block *sb)
 {
        struct block_device *bdev = sb->s_bdev;
+       fmode_t mode = sb->s_mode;
 
-       bdev_uevent(bdev, KOBJ_UMOUNT);
+       bdev->bd_super = NULL;
        generic_shutdown_super(sb);
        sync_blockdev(bdev);
-       close_bdev_excl(bdev);
+       close_bdev_exclusive(bdev, mode);
 }
 
 EXPORT_SYMBOL(kill_block_super);
+#endif
 
 int get_sb_nodev(struct file_system_type *fs_type,
        int flags, void *data,
@@ -772,12 +946,12 @@ 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;
-       return simple_set_mnt(mnt, s);
+       simple_set_mnt(mnt, s);
+       return 0;
 }
 
 EXPORT_SYMBOL(get_sb_nodev);
@@ -802,14 +976,14 @@ 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;
        }
        do_remount_sb(s, flags, data, 0);
-       return simple_set_mnt(mnt, s);
+       simple_set_mnt(mnt, s);
+       return 0;
 }
 
 EXPORT_SYMBOL(get_sb_single);
@@ -829,12 +1003,12 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
        if (!mnt)
                goto out;
 
-       if (data) {
+       if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
                secdata = alloc_secdata();
                if (!secdata)
                        goto out_mnt;
 
-               error = security_sb_copy_data(type, data, secdata);
+               error = security_sb_copy_data(data, secdata);
                if (error)
                        goto out_free_secdata;
        }
@@ -842,8 +1016,9 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
        error = type->get_sb(type, flags, name, data, mnt);
        if (error < 0)
                goto out_free_secdata;
+       BUG_ON(!mnt->mnt_sb);
 
-       error = security_sb_kern_mount(mnt->mnt_sb, secdata);
+       error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
        if (error)
                goto out_sb;
 
@@ -854,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:
@@ -866,6 +1040,29 @@ out:
 
 EXPORT_SYMBOL_GPL(vfs_kern_mount);
 
+static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
+{
+       int err;
+       const char *subtype = strchr(fstype, '.');
+       if (subtype) {
+               subtype++;
+               err = -EINVAL;
+               if (!subtype[0])
+                       goto err;
+       } else
+               subtype = "";
+
+       mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
+       err = -ENOMEM;
+       if (!mnt->mnt_sb->s_subtype)
+               goto err;
+       return mnt;
+
+ err:
+       mntput(mnt);
+       return ERR_PTR(err);
+}
+
 struct vfsmount *
 do_kern_mount(const char *fstype, int flags, const char *name, void *data)
 {
@@ -874,13 +1071,17 @@ do_kern_mount(const char *fstype, int flags, const char *name, void *data)
        if (!type)
                return ERR_PTR(-ENODEV);
        mnt = vfs_kern_mount(type, flags, name, data);
+       if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
+           !mnt->mnt_sb->s_subtype)
+               mnt = fs_set_subtype(mnt, fstype);
        put_filesystem(type);
        return mnt;
 }
+EXPORT_SYMBOL_GPL(do_kern_mount);
 
-struct vfsmount *kern_mount(struct file_system_type *type)
+struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
 {
-       return vfs_kern_mount(type, 0, type->name, NULL);
+       return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
 }
 
-EXPORT_SYMBOL(kern_mount);
+EXPORT_SYMBOL_GPL(kern_mount_data);