diff options
Diffstat (limited to 'fs/nilfs2/super.c')
-rw-r--r-- | fs/nilfs2/super.c | 629 |
1 files changed, 328 insertions, 301 deletions
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 922263393c76..35ae03c0db86 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -45,14 +45,13 @@ #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 +68,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) @@ -147,7 +147,7 @@ void nilfs_warning(struct super_block *sb, const char *function, } -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,18 +156,20 @@ 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) -{ - return nilfs_alloc_inode_common(NILFS_SB(sb)->s_nilfs); -} - void nilfs_destroy_inode(struct inode *inode) { + struct nilfs_mdt_info *mdi = NILFS_MDT(inode); + + if (mdi) { + kfree(mdi->mi_bgl); /* kfree(NULL) is safe */ + kfree(mdi); + } kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode)); } @@ -178,17 +180,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 +336,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 +343,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 +378,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 +409,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 +502,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 +515,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 +546,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 +556,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 +581,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 +618,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 +651,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 +663,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,9 +682,11 @@ 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); return nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL); @@ -798,22 +753,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 atomic_read(&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 +911,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 +941,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; } @@ -947,15 +1003,10 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) struct the_nilfs *nilfs = sbi->s_nilfs; unsigned long old_sb_flags; struct nilfs_mount_options old_opts; - int was_snapshot, err; - - lock_kernel(); + 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); if (!parse_options(data, sb, 1)) { err = -EINVAL; @@ -964,11 +1015,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 +1039,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 +1061,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(); return err; } @@ -1052,7 +1095,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 +1103,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,18 +1131,14 @@ 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 @@ -1107,8 +1148,8 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, struct nilfs_super_data sd; struct super_block *s; fmode_t mode = FMODE_READ; - struct the_nilfs *nilfs; - int err, need_to_close = 1; + struct dentry *root_dentry; + int err, s_new = false; if (!(flags & MS_RDONLY)) mode |= FMODE_WRITE; @@ -1117,12 +1158,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (IS_ERR(sd.bdev)) return PTR_ERR(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,94 +1165,86 @@ 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) + close_bdev_exclusive(sd.bdev, mode); - return err; + mnt->mnt_sb = s; + mnt->mnt_root = root_dentry; + return 0; - 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. - */ + + failed: + if (!s_new) + close_bdev_exclusive(sd.bdev, mode); return err; } |