]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/nilfs2/super.c
Merge branch 'master' into tk71
[mv-sheeva.git] / fs / nilfs2 / super.c
index 922263393c765664f6b5598f4c144093ec9e582c..1673b3d99842018206640c77ea9922840c5a2bc4 100644 (file)
 #include <linux/parser.h>
 #include <linux/random.h>
 #include <linux/crc32.h>
-#include <linux/smp_lock.h>
 #include <linux/vfs.h>
 #include <linux/writeback.h>
-#include <linux/kobject.h>
-#include <linux/exportfs.h>
 #include <linux/seq_file.h>
 #include <linux/mount.h>
 #include "nilfs.h"
+#include "export.h"
 #include "mdt.h"
 #include "alloc.h"
 #include "btree.h"
@@ -69,11 +67,12 @@ MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem "
                   "(NILFS)");
 MODULE_LICENSE("GPL");
 
-struct kmem_cache *nilfs_inode_cachep;
+static struct kmem_cache *nilfs_inode_cachep;
 struct kmem_cache *nilfs_transaction_cachep;
 struct kmem_cache *nilfs_segbuf_cachep;
 struct kmem_cache *nilfs_btree_path_cache;
 
+static int nilfs_setup_super(struct nilfs_sb_info *sbi, int is_mount);
 static int nilfs_remount(struct super_block *sb, int *flags, char *data);
 
 static void nilfs_set_error(struct nilfs_sb_info *sbi)
@@ -111,12 +110,17 @@ void nilfs_error(struct super_block *sb, const char *function,
                 const char *fmt, ...)
 {
        struct nilfs_sb_info *sbi = NILFS_SB(sb);
+       struct va_format vaf;
        va_list args;
 
        va_start(args, fmt);
-       printk(KERN_CRIT "NILFS error (device %s): %s: ", sb->s_id, function);
-       vprintk(fmt, args);
-       printk("\n");
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       printk(KERN_CRIT "NILFS error (device %s): %s: %pV\n",
+              sb->s_id, function, &vaf);
+
        va_end(args);
 
        if (!(sb->s_flags & MS_RDONLY)) {
@@ -136,18 +140,22 @@ void nilfs_error(struct super_block *sb, const char *function,
 void nilfs_warning(struct super_block *sb, const char *function,
                   const char *fmt, ...)
 {
+       struct va_format vaf;
        va_list args;
 
        va_start(args, fmt);
-       printk(KERN_WARNING "NILFS warning (device %s): %s: ",
-              sb->s_id, function);
-       vprintk(fmt, args);
-       printk("\n");
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       printk(KERN_WARNING "NILFS warning (device %s): %s: %pV\n",
+              sb->s_id, function, &vaf);
+
        va_end(args);
 }
 
 
-struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs)
+struct inode *nilfs_alloc_inode(struct super_block *sb)
 {
        struct nilfs_inode_info *ii;
 
@@ -156,19 +164,29 @@ struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs)
                return NULL;
        ii->i_bh = NULL;
        ii->i_state = 0;
+       ii->i_cno = 0;
        ii->vfs_inode.i_version = 1;
-       nilfs_btnode_cache_init(&ii->i_btnode_cache, nilfs->ns_bdi);
+       nilfs_btnode_cache_init(&ii->i_btnode_cache, sb->s_bdi);
        return &ii->vfs_inode;
 }
 
-struct inode *nilfs_alloc_inode(struct super_block *sb)
+static void nilfs_i_callback(struct rcu_head *head)
 {
-       return nilfs_alloc_inode_common(NILFS_SB(sb)->s_nilfs);
+       struct inode *inode = container_of(head, struct inode, i_rcu);
+       struct nilfs_mdt_info *mdi = NILFS_MDT(inode);
+
+       INIT_LIST_HEAD(&inode->i_dentry);
+
+       if (mdi) {
+               kfree(mdi->mi_bgl); /* kfree(NULL) is safe */
+               kfree(mdi);
+       }
+       kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode));
 }
 
 void nilfs_destroy_inode(struct inode *inode)
 {
-       kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode));
+       call_rcu(&inode->i_rcu, nilfs_i_callback);
 }
 
 static int nilfs_sync_super(struct nilfs_sb_info *sbi, int flag)
@@ -178,17 +196,9 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int flag)
 
  retry:
        set_buffer_dirty(nilfs->ns_sbh[0]);
-
        if (nilfs_test_opt(sbi, BARRIER)) {
                err = __sync_dirty_buffer(nilfs->ns_sbh[0],
-                                         WRITE_SYNC | WRITE_BARRIER);
-               if (err == -EOPNOTSUPP) {
-                       nilfs_warning(sbi->s_super, __func__,
-                                     "barrier-based sync failed. "
-                                     "disabling barriers\n");
-                       nilfs_clear_opt(sbi, BARRIER);
-                       goto retry;
-               }
+                                         WRITE_SYNC | WRITE_FLUSH_FUA);
        } else {
                err = sync_dirty_buffer(nilfs->ns_sbh[0]);
        }
@@ -342,8 +352,6 @@ static void nilfs_put_super(struct super_block *sb)
        struct nilfs_sb_info *sbi = NILFS_SB(sb);
        struct the_nilfs *nilfs = sbi->s_nilfs;
 
-       lock_kernel();
-
        nilfs_detach_segment_constructor(sbi);
 
        if (!(sb->s_flags & MS_RDONLY)) {
@@ -351,18 +359,15 @@ static void nilfs_put_super(struct super_block *sb)
                nilfs_cleanup_super(sbi);
                up_write(&nilfs->ns_sem);
        }
-       down_write(&nilfs->ns_super_sem);
-       if (nilfs->ns_current == sbi)
-               nilfs->ns_current = NULL;
-       up_write(&nilfs->ns_super_sem);
 
-       nilfs_detach_checkpoint(sbi);
-       put_nilfs(sbi->s_nilfs);
+       iput(nilfs->ns_sufile);
+       iput(nilfs->ns_cpfile);
+       iput(nilfs->ns_dat);
+
+       destroy_nilfs(nilfs);
        sbi->s_super = NULL;
        sb->s_fs_info = NULL;
-       nilfs_put_sbinfo(sbi);
-
-       unlock_kernel();
+       kfree(sbi);
 }
 
 static int nilfs_sync_fs(struct super_block *sb, int wait)
@@ -389,21 +394,22 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
        return err;
 }
 
-int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
+int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt,
+                           struct nilfs_root **rootp)
 {
        struct the_nilfs *nilfs = sbi->s_nilfs;
+       struct nilfs_root *root;
        struct nilfs_checkpoint *raw_cp;
        struct buffer_head *bh_cp;
-       int err;
+       int err = -ENOMEM;
 
-       down_write(&nilfs->ns_super_sem);
-       list_add(&sbi->s_list, &nilfs->ns_supers);
-       up_write(&nilfs->ns_super_sem);
+       root = nilfs_find_or_create_root(
+               nilfs, curr_mnt ? NILFS_CPTREE_CURRENT_CNO : cno);
+       if (!root)
+               return err;
 
-       err = -ENOMEM;
-       sbi->s_ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size);
-       if (!sbi->s_ifile)
-               goto delist;
+       if (root->ifile)
+               goto reuse; /* already attached checkpoint */
 
        down_read(&nilfs->ns_segctor_sem);
        err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, cno, 0, &raw_cp,
@@ -419,45 +425,64 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
                }
                goto failed;
        }
-       err = nilfs_read_inode_common(sbi->s_ifile, &raw_cp->cp_ifile_inode);
-       if (unlikely(err))
+
+       err = nilfs_ifile_read(sbi->s_super, root, nilfs->ns_inode_size,
+                              &raw_cp->cp_ifile_inode, &root->ifile);
+       if (err)
                goto failed_bh;
-       atomic_set(&sbi->s_inodes_count, le64_to_cpu(raw_cp->cp_inodes_count));
-       atomic_set(&sbi->s_blocks_count, le64_to_cpu(raw_cp->cp_blocks_count));
+
+       atomic_set(&root->inodes_count, le64_to_cpu(raw_cp->cp_inodes_count));
+       atomic_set(&root->blocks_count, le64_to_cpu(raw_cp->cp_blocks_count));
 
        nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp);
+
+ reuse:
+       *rootp = root;
        return 0;
 
  failed_bh:
        nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp);
  failed:
-       nilfs_mdt_destroy(sbi->s_ifile);
-       sbi->s_ifile = NULL;
+       nilfs_put_root(root);
 
- delist:
-       down_write(&nilfs->ns_super_sem);
-       list_del_init(&sbi->s_list);
-       up_write(&nilfs->ns_super_sem);
+       return err;
+}
 
+static int nilfs_freeze(struct super_block *sb)
+{
+       struct nilfs_sb_info *sbi = NILFS_SB(sb);
+       struct the_nilfs *nilfs = sbi->s_nilfs;
+       int err;
+
+       if (sb->s_flags & MS_RDONLY)
+               return 0;
+
+       /* Mark super block clean */
+       down_write(&nilfs->ns_sem);
+       err = nilfs_cleanup_super(sbi);
+       up_write(&nilfs->ns_sem);
        return err;
 }
 
-void nilfs_detach_checkpoint(struct nilfs_sb_info *sbi)
+static int nilfs_unfreeze(struct super_block *sb)
 {
+       struct nilfs_sb_info *sbi = NILFS_SB(sb);
        struct the_nilfs *nilfs = sbi->s_nilfs;
 
-       nilfs_mdt_destroy(sbi->s_ifile);
-       sbi->s_ifile = NULL;
-       down_write(&nilfs->ns_super_sem);
-       list_del_init(&sbi->s_list);
-       up_write(&nilfs->ns_super_sem);
+       if (sb->s_flags & MS_RDONLY)
+               return 0;
+
+       down_write(&nilfs->ns_sem);
+       nilfs_setup_super(sbi, false);
+       up_write(&nilfs->ns_sem);
+       return 0;
 }
 
 static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        struct super_block *sb = dentry->d_sb;
-       struct nilfs_sb_info *sbi = NILFS_SB(sb);
-       struct the_nilfs *nilfs = sbi->s_nilfs;
+       struct nilfs_root *root = NILFS_I(dentry->d_inode)->i_root;
+       struct the_nilfs *nilfs = root->nilfs;
        u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
        unsigned long long blocks;
        unsigned long overhead;
@@ -493,7 +518,7 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_bfree = nfreeblocks;
        buf->f_bavail = (buf->f_bfree >= nrsvblocks) ?
                (buf->f_bfree - nrsvblocks) : 0;
-       buf->f_files = atomic_read(&sbi->s_inodes_count);
+       buf->f_files = atomic_read(&root->inodes_count);
        buf->f_ffree = 0; /* nilfs_count_free_inodes(sb); */
        buf->f_namelen = NILFS_NAME_LEN;
        buf->f_fsid.val[0] = (u32)id;
@@ -506,12 +531,12 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
 {
        struct super_block *sb = vfs->mnt_sb;
        struct nilfs_sb_info *sbi = NILFS_SB(sb);
+       struct nilfs_root *root = NILFS_I(vfs->mnt_root->d_inode)->i_root;
 
        if (!nilfs_test_opt(sbi, BARRIER))
                seq_puts(seq, ",nobarrier");
-       if (nilfs_test_opt(sbi, SNAPSHOT))
-               seq_printf(seq, ",cp=%llu",
-                          (unsigned long long int)sbi->s_snapshot_cno);
+       if (root->cno != NILFS_CPTREE_CURRENT_CNO)
+               seq_printf(seq, ",cp=%llu", (unsigned long long)root->cno);
        if (nilfs_test_opt(sbi, ERRORS_PANIC))
                seq_puts(seq, ",errors=panic");
        if (nilfs_test_opt(sbi, ERRORS_CONT))
@@ -537,6 +562,8 @@ static const struct super_operations nilfs_sops = {
        .put_super      = nilfs_put_super,
        /* .write_super    = nilfs_write_super, */
        .sync_fs        = nilfs_sync_fs,
+       .freeze_fs      = nilfs_freeze,
+       .unfreeze_fs    = nilfs_unfreeze,
        /* .write_super_lockfs */
        /* .unlockfs */
        .statfs         = nilfs_statfs,
@@ -545,48 +572,6 @@ static const struct super_operations nilfs_sops = {
        .show_options = nilfs_show_options
 };
 
-static struct inode *
-nilfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation)
-{
-       struct inode *inode;
-
-       if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO &&
-           ino != NILFS_SKETCH_INO)
-               return ERR_PTR(-ESTALE);
-
-       inode = nilfs_iget(sb, ino);
-       if (IS_ERR(inode))
-               return ERR_CAST(inode);
-       if (generation && inode->i_generation != generation) {
-               iput(inode);
-               return ERR_PTR(-ESTALE);
-       }
-
-       return inode;
-}
-
-static struct dentry *
-nilfs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
-                  int fh_type)
-{
-       return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
-                                   nilfs_nfs_get_inode);
-}
-
-static struct dentry *
-nilfs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len,
-                  int fh_type)
-{
-       return generic_fh_to_parent(sb, fid, fh_len, fh_type,
-                                   nilfs_nfs_get_inode);
-}
-
-static const struct export_operations nilfs_export_ops = {
-       .fh_to_dentry = nilfs_fh_to_dentry,
-       .fh_to_parent = nilfs_fh_to_parent,
-       .get_parent = nilfs_get_parent,
-};
-
 enum {
        Opt_err_cont, Opt_err_panic, Opt_err_ro,
        Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
@@ -612,7 +597,6 @@ static int parse_options(char *options, struct super_block *sb, int is_remount)
        struct nilfs_sb_info *sbi = NILFS_SB(sb);
        char *p;
        substring_t args[MAX_OPT_ARGS];
-       int option;
 
        if (!options)
                return 1;
@@ -650,30 +634,12 @@ static int parse_options(char *options, struct super_block *sb, int is_remount)
                        nilfs_write_opt(sbi, ERROR_MODE, ERRORS_CONT);
                        break;
                case Opt_snapshot:
-                       if (match_int(&args[0], &option) || option <= 0)
-                               return 0;
                        if (is_remount) {
-                               if (!nilfs_test_opt(sbi, SNAPSHOT)) {
-                                       printk(KERN_ERR
-                                              "NILFS: cannot change regular "
-                                              "mount to snapshot.\n");
-                                       return 0;
-                               } else if (option != sbi->s_snapshot_cno) {
-                                       printk(KERN_ERR
-                                              "NILFS: cannot remount to a "
-                                              "different snapshot.\n");
-                                       return 0;
-                               }
-                               break;
-                       }
-                       if (!(sb->s_flags & MS_RDONLY)) {
-                               printk(KERN_ERR "NILFS: cannot mount snapshot "
-                                      "read/write.  A read-only option is "
-                                      "required.\n");
+                               printk(KERN_ERR
+                                      "NILFS: \"%s\" option is invalid "
+                                      "for remount.\n", p);
                                return 0;
                        }
-                       sbi->s_snapshot_cno = option;
-                       nilfs_set_opt(sbi, SNAPSHOT);
                        break;
                case Opt_norecovery:
                        nilfs_set_opt(sbi, NORECOVERY);
@@ -701,7 +667,7 @@ nilfs_set_default_options(struct nilfs_sb_info *sbi,
                NILFS_MOUNT_ERRORS_RO | NILFS_MOUNT_BARRIER;
 }
 
-static int nilfs_setup_super(struct nilfs_sb_info *sbi)
+static int nilfs_setup_super(struct nilfs_sb_info *sbi, int is_mount)
 {
        struct the_nilfs *nilfs = sbi->s_nilfs;
        struct nilfs_super_block **sbp;
@@ -713,6 +679,9 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi)
        if (!sbp)
                return -EIO;
 
+       if (!is_mount)
+               goto skip_mount_setup;
+
        max_mnt_count = le16_to_cpu(sbp[0]->s_max_mnt_count);
        mnt_count = le16_to_cpu(sbp[0]->s_mnt_count);
 
@@ -729,11 +698,14 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi)
                sbp[0]->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT);
 
        sbp[0]->s_mnt_count = cpu_to_le16(mnt_count + 1);
+       sbp[0]->s_mtime = cpu_to_le64(get_seconds());
+
+skip_mount_setup:
        sbp[0]->s_state =
                cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS);
-       sbp[0]->s_mtime = cpu_to_le64(get_seconds());
        /* synchronize sbp[1] with sbp[0] */
-       memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
+       if (sbp[1])
+               memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
        return nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL);
 }
 
@@ -798,22 +770,156 @@ int nilfs_check_feature_compatibility(struct super_block *sb,
        return 0;
 }
 
+static int nilfs_get_root_dentry(struct super_block *sb,
+                                struct nilfs_root *root,
+                                struct dentry **root_dentry)
+{
+       struct inode *inode;
+       struct dentry *dentry;
+       int ret = 0;
+
+       inode = nilfs_iget(sb, root, NILFS_ROOT_INO);
+       if (IS_ERR(inode)) {
+               printk(KERN_ERR "NILFS: get root inode failed\n");
+               ret = PTR_ERR(inode);
+               goto out;
+       }
+       if (!S_ISDIR(inode->i_mode) || !inode->i_blocks || !inode->i_size) {
+               iput(inode);
+               printk(KERN_ERR "NILFS: corrupt root inode.\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (root->cno == NILFS_CPTREE_CURRENT_CNO) {
+               dentry = d_find_alias(inode);
+               if (!dentry) {
+                       dentry = d_alloc_root(inode);
+                       if (!dentry) {
+                               iput(inode);
+                               ret = -ENOMEM;
+                               goto failed_dentry;
+                       }
+               } else {
+                       iput(inode);
+               }
+       } else {
+               dentry = d_obtain_alias(inode);
+               if (IS_ERR(dentry)) {
+                       ret = PTR_ERR(dentry);
+                       goto failed_dentry;
+               }
+       }
+       *root_dentry = dentry;
+ out:
+       return ret;
+
+ failed_dentry:
+       printk(KERN_ERR "NILFS: get root dentry failed\n");
+       goto out;
+}
+
+static int nilfs_attach_snapshot(struct super_block *s, __u64 cno,
+                                struct dentry **root_dentry)
+{
+       struct the_nilfs *nilfs = NILFS_SB(s)->s_nilfs;
+       struct nilfs_root *root;
+       int ret;
+
+       down_read(&nilfs->ns_segctor_sem);
+       ret = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, cno);
+       up_read(&nilfs->ns_segctor_sem);
+       if (ret < 0) {
+               ret = (ret == -ENOENT) ? -EINVAL : ret;
+               goto out;
+       } else if (!ret) {
+               printk(KERN_ERR "NILFS: The specified checkpoint is "
+                      "not a snapshot (checkpoint number=%llu).\n",
+                      (unsigned long long)cno);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = nilfs_attach_checkpoint(NILFS_SB(s), cno, false, &root);
+       if (ret) {
+               printk(KERN_ERR "NILFS: error loading snapshot "
+                      "(checkpoint number=%llu).\n",
+              (unsigned long long)cno);
+               goto out;
+       }
+       ret = nilfs_get_root_dentry(s, root, root_dentry);
+       nilfs_put_root(root);
+ out:
+       return ret;
+}
+
+static int nilfs_tree_was_touched(struct dentry *root_dentry)
+{
+       return root_dentry->d_count > 1;
+}
+
+/**
+ * nilfs_try_to_shrink_tree() - try to shrink dentries of a checkpoint
+ * @root_dentry: root dentry of the tree to be shrunk
+ *
+ * This function returns true if the tree was in-use.
+ */
+static int nilfs_try_to_shrink_tree(struct dentry *root_dentry)
+{
+       if (have_submounts(root_dentry))
+               return true;
+       shrink_dcache_parent(root_dentry);
+       return nilfs_tree_was_touched(root_dentry);
+}
+
+int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno)
+{
+       struct the_nilfs *nilfs = NILFS_SB(sb)->s_nilfs;
+       struct nilfs_root *root;
+       struct inode *inode;
+       struct dentry *dentry;
+       int ret;
+
+       if (cno < 0 || cno > nilfs->ns_cno)
+               return false;
+
+       if (cno >= nilfs_last_cno(nilfs))
+               return true;    /* protect recent checkpoints */
+
+       ret = false;
+       root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs, cno);
+       if (root) {
+               inode = nilfs_ilookup(sb, root, NILFS_ROOT_INO);
+               if (inode) {
+                       dentry = d_find_alias(inode);
+                       if (dentry) {
+                               if (nilfs_tree_was_touched(dentry))
+                                       ret = nilfs_try_to_shrink_tree(dentry);
+                               dput(dentry);
+                       }
+                       iput(inode);
+               }
+               nilfs_put_root(root);
+       }
+       return ret;
+}
+
 /**
  * nilfs_fill_super() - initialize a super block instance
  * @sb: super_block
  * @data: mount options
  * @silent: silent mode flag
- * @nilfs: the_nilfs struct
  *
  * This function is called exclusively by nilfs->ns_mount_mutex.
  * So, the recovery process is protected from other simultaneous mounts.
  */
 static int
-nilfs_fill_super(struct super_block *sb, void *data, int silent,
-                struct the_nilfs *nilfs)
+nilfs_fill_super(struct super_block *sb, void *data, int silent)
 {
+       struct the_nilfs *nilfs;
        struct nilfs_sb_info *sbi;
-       struct inode *root;
+       struct nilfs_root *fsroot;
+       struct backing_dev_info *bdi;
        __u64 cno;
        int err;
 
@@ -822,19 +928,21 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
                return -ENOMEM;
 
        sb->s_fs_info = sbi;
+       sbi->s_super = sb;
 
-       get_nilfs(nilfs);
+       nilfs = alloc_nilfs(sb->s_bdev);
+       if (!nilfs) {
+               err = -ENOMEM;
+               goto failed_sbi;
+       }
        sbi->s_nilfs = nilfs;
-       sbi->s_super = sb;
-       atomic_set(&sbi->s_count, 1);
 
        err = init_nilfs(nilfs, sbi, (char *)data);
        if (err)
-               goto failed_sbi;
+               goto failed_nilfs;
 
        spin_lock_init(&sbi->s_inode_lock);
        INIT_LIST_HEAD(&sbi->s_dirty_files);
-       INIT_LIST_HEAD(&sbi->s_list);
 
        /*
         * Following initialization is overlapped because
@@ -850,94 +958,59 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
        sb->s_export_op = &nilfs_export_ops;
        sb->s_root = NULL;
        sb->s_time_gran = 1;
-       sb->s_bdi = nilfs->ns_bdi;
+
+       bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
+       sb->s_bdi = bdi ? : &default_backing_dev_info;
 
        err = load_nilfs(nilfs, sbi);
        if (err)
-               goto failed_sbi;
+               goto failed_nilfs;
 
        cno = nilfs_last_cno(nilfs);
-
-       if (sb->s_flags & MS_RDONLY) {
-               if (nilfs_test_opt(sbi, SNAPSHOT)) {
-                       down_read(&nilfs->ns_segctor_sem);
-                       err = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile,
-                                                      sbi->s_snapshot_cno);
-                       up_read(&nilfs->ns_segctor_sem);
-                       if (err < 0) {
-                               if (err == -ENOENT)
-                                       err = -EINVAL;
-                               goto failed_sbi;
-                       }
-                       if (!err) {
-                               printk(KERN_ERR
-                                      "NILFS: The specified checkpoint is "
-                                      "not a snapshot "
-                                      "(checkpoint number=%llu).\n",
-                                      (unsigned long long)sbi->s_snapshot_cno);
-                               err = -EINVAL;
-                               goto failed_sbi;
-                       }
-                       cno = sbi->s_snapshot_cno;
-               }
-       }
-
-       err = nilfs_attach_checkpoint(sbi, cno);
+       err = nilfs_attach_checkpoint(sbi, cno, true, &fsroot);
        if (err) {
-               printk(KERN_ERR "NILFS: error loading a checkpoint"
-                      " (checkpoint number=%llu).\n", (unsigned long long)cno);
-               goto failed_sbi;
+               printk(KERN_ERR "NILFS: error loading last checkpoint "
+                      "(checkpoint number=%llu).\n", (unsigned long long)cno);
+               goto failed_unload;
        }
 
        if (!(sb->s_flags & MS_RDONLY)) {
-               err = nilfs_attach_segment_constructor(sbi);
+               err = nilfs_attach_segment_constructor(sbi, fsroot);
                if (err)
                        goto failed_checkpoint;
        }
 
-       root = nilfs_iget(sb, NILFS_ROOT_INO);
-       if (IS_ERR(root)) {
-               printk(KERN_ERR "NILFS: get root inode failed\n");
-               err = PTR_ERR(root);
-               goto failed_segctor;
-       }
-       if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
-               iput(root);
-               printk(KERN_ERR "NILFS: corrupt root inode.\n");
-               err = -EINVAL;
-               goto failed_segctor;
-       }
-       sb->s_root = d_alloc_root(root);
-       if (!sb->s_root) {
-               iput(root);
-               printk(KERN_ERR "NILFS: get root dentry failed\n");
-               err = -ENOMEM;
+       err = nilfs_get_root_dentry(sb, fsroot, &sb->s_root);
+       if (err)
                goto failed_segctor;
-       }
+
+       nilfs_put_root(fsroot);
 
        if (!(sb->s_flags & MS_RDONLY)) {
                down_write(&nilfs->ns_sem);
-               nilfs_setup_super(sbi);
+               nilfs_setup_super(sbi, true);
                up_write(&nilfs->ns_sem);
        }
 
-       down_write(&nilfs->ns_super_sem);
-       if (!nilfs_test_opt(sbi, SNAPSHOT))
-               nilfs->ns_current = sbi;
-       up_write(&nilfs->ns_super_sem);
-
        return 0;
 
  failed_segctor:
        nilfs_detach_segment_constructor(sbi);
 
  failed_checkpoint:
-       nilfs_detach_checkpoint(sbi);
+       nilfs_put_root(fsroot);
+
+ failed_unload:
+       iput(nilfs->ns_sufile);
+       iput(nilfs->ns_cpfile);
+       iput(nilfs->ns_dat);
+
+ failed_nilfs:
+       destroy_nilfs(nilfs);
 
  failed_sbi:
-       put_nilfs(nilfs);
        sb->s_fs_info = NULL;
-       nilfs_put_sbinfo(sbi);
+       kfree(sbi);
        return err;
 }
 
@@ -946,16 +1019,11 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
        struct nilfs_sb_info *sbi = NILFS_SB(sb);
        struct the_nilfs *nilfs = sbi->s_nilfs;
        unsigned long old_sb_flags;
-       struct nilfs_mount_options old_opts;
-       int was_snapshot, err;
-
-       lock_kernel();
+       unsigned long old_mount_opt;
+       int err;
 
-       down_write(&nilfs->ns_super_sem);
        old_sb_flags = sb->s_flags;
-       old_opts.mount_opt = sbi->s_mount_opt;
-       old_opts.snapshot_cno = sbi->s_snapshot_cno;
-       was_snapshot = nilfs_test_opt(sbi, SNAPSHOT);
+       old_mount_opt = sbi->s_mount_opt;
 
        if (!parse_options(data, sb, 1)) {
                err = -EINVAL;
@@ -964,11 +1032,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL);
 
        err = -EINVAL;
-       if (was_snapshot && !(*flags & MS_RDONLY)) {
-               printk(KERN_ERR "NILFS (device %s): cannot remount snapshot "
-                      "read/write.\n", sb->s_id);
-               goto restore_opts;
-       }
 
        if (!nilfs_valid_fs(nilfs)) {
                printk(KERN_WARNING "NILFS (device %s): couldn't "
@@ -993,6 +1056,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
                up_write(&nilfs->ns_sem);
        } else {
                __u64 features;
+               struct nilfs_root *root;
 
                /*
                 * Mounting a RDONLY partition read-write, so reread and
@@ -1014,25 +1078,21 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
 
                sb->s_flags &= ~MS_RDONLY;
 
-               err = nilfs_attach_segment_constructor(sbi);
+               root = NILFS_I(sb->s_root->d_inode)->i_root;
+               err = nilfs_attach_segment_constructor(sbi, root);
                if (err)
                        goto restore_opts;
 
                down_write(&nilfs->ns_sem);
-               nilfs_setup_super(sbi);
+               nilfs_setup_super(sbi, true);
                up_write(&nilfs->ns_sem);
        }
  out:
-       up_write(&nilfs->ns_super_sem);
-       unlock_kernel();
        return 0;
 
  restore_opts:
        sb->s_flags = old_sb_flags;
-       sbi->s_mount_opt = old_opts.mount_opt;
-       sbi->s_snapshot_cno = old_opts.snapshot_cno;
-       up_write(&nilfs->ns_super_sem);
-       unlock_kernel();
+       sbi->s_mount_opt = old_mount_opt;
        return err;
 }
 
@@ -1052,7 +1112,7 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd)
 {
        char *p, *options = data;
        substring_t args[MAX_OPT_ARGS];
-       int option, token;
+       int token;
        int ret = 0;
 
        do {
@@ -1060,16 +1120,18 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd)
                if (p != NULL && *p) {
                        token = match_token(p, tokens, args);
                        if (token == Opt_snapshot) {
-                               if (!(sd->flags & MS_RDONLY))
+                               if (!(sd->flags & MS_RDONLY)) {
                                        ret++;
-                               else {
-                                       ret = match_int(&args[0], &option);
-                                       if (!ret) {
-                                               if (option > 0)
-                                                       sd->cno = option;
-                                               else
-                                                       ret++;
-                                       }
+                               } else {
+                                       sd->cno = simple_strtoull(args[0].from,
+                                                                 NULL, 0);
+                                       /*
+                                        * No need to see the end pointer;
+                                        * match_token() has done syntax
+                                        * checking.
+                                        */
+                                       if (sd->cno == 0)
+                                               ret++;
                                }
                        }
                        if (ret)
@@ -1086,43 +1148,33 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd)
 
 static int nilfs_set_bdev_super(struct super_block *s, void *data)
 {
-       struct nilfs_super_data *sd = data;
-
-       s->s_bdev = sd->bdev;
+       s->s_bdev = data;
        s->s_dev = s->s_bdev->bd_dev;
        return 0;
 }
 
 static int nilfs_test_bdev_super(struct super_block *s, void *data)
 {
-       struct nilfs_super_data *sd = data;
-
-       return sd->sbi && s->s_fs_info == (void *)sd->sbi;
+       return (void *)s->s_bdev == data;
 }
 
-static int
-nilfs_get_sb(struct file_system_type *fs_type, int flags,
-            const char *dev_name, void *data, struct vfsmount *mnt)
+static struct dentry *
+nilfs_mount(struct file_system_type *fs_type, int flags,
+            const char *dev_name, void *data)
 {
        struct nilfs_super_data sd;
        struct super_block *s;
-       fmode_t mode = FMODE_READ;
-       struct the_nilfs *nilfs;
-       int err, need_to_close = 1;
+       fmode_t mode = FMODE_READ | FMODE_EXCL;
+       struct dentry *root_dentry;
+       int err, s_new = false;
 
        if (!(flags & MS_RDONLY))
                mode |= FMODE_WRITE;
 
-       sd.bdev = open_bdev_exclusive(dev_name, mode, fs_type);
+       sd.bdev = blkdev_get_by_path(dev_name, mode, fs_type);
        if (IS_ERR(sd.bdev))
-               return PTR_ERR(sd.bdev);
+               return ERR_CAST(sd.bdev);
 
-       /*
-        * To get mount instance using sget() vfs-routine, NILFS needs
-        * much more information than normal filesystems to identify mount
-        * instance.  For snapshot mounts, not only a mount type (ro-mount
-        * or rw-mount) but also a checkpoint number is required.
-        */
        sd.cno = 0;
        sd.flags = flags;
        if (nilfs_identify((char *)data, &sd)) {
@@ -1130,101 +1182,91 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
                goto failed;
        }
 
-       nilfs = find_or_create_nilfs(sd.bdev);
-       if (!nilfs) {
-               err = -ENOMEM;
-               goto failed;
-       }
-
-       mutex_lock(&nilfs->ns_mount_mutex);
-
-       if (!sd.cno) {
-               /*
-                * Check if an exclusive mount exists or not.
-                * Snapshot mounts coexist with a current mount
-                * (i.e. rw-mount or ro-mount), whereas rw-mount and
-                * ro-mount are mutually exclusive.
-                */
-               down_read(&nilfs->ns_super_sem);
-               if (nilfs->ns_current &&
-                   ((nilfs->ns_current->s_super->s_flags ^ flags)
-                    & MS_RDONLY)) {
-                       up_read(&nilfs->ns_super_sem);
-                       err = -EBUSY;
-                       goto failed_unlock;
-               }
-               up_read(&nilfs->ns_super_sem);
-       }
-
-       /*
-        * Find existing nilfs_sb_info struct
-        */
-       sd.sbi = nilfs_find_sbinfo(nilfs, !(flags & MS_RDONLY), sd.cno);
-
        /*
-        * Get super block instance holding the nilfs_sb_info struct.
-        * A new instance is allocated if no existing mount is present or
-        * existing instance has been unmounted.
+        * once the super is inserted into the list by sget, s_umount
+        * will protect the lockfs code from trying to start a snapshot
+        * while we are mounting
         */
-       s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, &sd);
-       if (sd.sbi)
-               nilfs_put_sbinfo(sd.sbi);
-
+       mutex_lock(&sd.bdev->bd_fsfreeze_mutex);
+       if (sd.bdev->bd_fsfreeze_count > 0) {
+               mutex_unlock(&sd.bdev->bd_fsfreeze_mutex);
+               err = -EBUSY;
+               goto failed;
+       }
+       s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, sd.bdev);
+       mutex_unlock(&sd.bdev->bd_fsfreeze_mutex);
        if (IS_ERR(s)) {
                err = PTR_ERR(s);
-               goto failed_unlock;
+               goto failed;
        }
 
        if (!s->s_root) {
                char b[BDEVNAME_SIZE];
 
+               s_new = true;
+
                /* New superblock instance created */
                s->s_flags = flags;
                s->s_mode = mode;
                strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id));
                sb_set_blocksize(s, block_size(sd.bdev));
 
-               err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0,
-                                      nilfs);
+               err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
                if (err)
-                       goto cancel_new;
+                       goto failed_super;
 
                s->s_flags |= MS_ACTIVE;
-               need_to_close = 0;
+       } else if (!sd.cno) {
+               int busy = false;
+
+               if (nilfs_tree_was_touched(s->s_root)) {
+                       busy = nilfs_try_to_shrink_tree(s->s_root);
+                       if (busy && (flags ^ s->s_flags) & MS_RDONLY) {
+                               printk(KERN_ERR "NILFS: the device already "
+                                      "has a %s mount.\n",
+                                      (s->s_flags & MS_RDONLY) ?
+                                      "read-only" : "read/write");
+                               err = -EBUSY;
+                               goto failed_super;
+                       }
+               }
+               if (!busy) {
+                       /*
+                        * Try remount to setup mount states if the current
+                        * tree is not mounted and only snapshots use this sb.
+                        */
+                       err = nilfs_remount(s, &flags, data);
+                       if (err)
+                               goto failed_super;
+               }
        }
 
-       mutex_unlock(&nilfs->ns_mount_mutex);
-       put_nilfs(nilfs);
-       if (need_to_close)
-               close_bdev_exclusive(sd.bdev, mode);
-       simple_set_mnt(mnt, s);
-       return 0;
+       if (sd.cno) {
+               err = nilfs_attach_snapshot(s, sd.cno, &root_dentry);
+               if (err)
+                       goto failed_super;
+       } else {
+               root_dentry = dget(s->s_root);
+       }
 
- failed_unlock:
-       mutex_unlock(&nilfs->ns_mount_mutex);
-       put_nilfs(nilfs);
- failed:
-       close_bdev_exclusive(sd.bdev, mode);
+       if (!s_new)
+               blkdev_put(sd.bdev, mode);
 
-       return err;
+       return root_dentry;
 
- cancel_new:
-       /* Abandoning the newly allocated superblock */
-       mutex_unlock(&nilfs->ns_mount_mutex);
-       put_nilfs(nilfs);
+ failed_super:
        deactivate_locked_super(s);
-       /*
-        * deactivate_locked_super() invokes close_bdev_exclusive().
-        * We must finish all post-cleaning before this call;
-        * put_nilfs() needs the block device.
-        */
-       return err;
+
+ failed:
+       if (!s_new)
+               blkdev_put(sd.bdev, mode);
+       return ERR_PTR(err);
 }
 
 struct file_system_type nilfs_fs_type = {
        .owner    = THIS_MODULE,
        .name     = "nilfs2",
-       .get_sb   = nilfs_get_sb,
+       .mount    = nilfs_mount,
        .kill_sb  = kill_block_super,
        .fs_flags = FS_REQUIRES_DEV,
 };
@@ -1237,7 +1279,7 @@ static void nilfs_inode_init_once(void *obj)
 #ifdef CONFIG_NILFS_XATTR
        init_rwsem(&ii->xattr_sem);
 #endif
-       nilfs_btnode_cache_init_once(&ii->i_btnode_cache);
+       address_space_init_once(&ii->i_btnode_cache);
        ii->i_bmap = &ii->i_bmap_data;
        inode_init_once(&ii->vfs_inode);
 }