From e7bb91b46ab5b396715053185d66600d048fd785 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 25 Jul 2010 14:38:53 +1000 Subject: [PATCH] New mount infrastructure for snapshot. Using the new s_sb_info structure, we add a snapshot number so we can uniquely identify a snapshot from the superblock and the 'sget' can be used to find an existing or new superblock. If it is new, set it up properly as before. No need to fiddle with 'primary_sb' - we have a ref into it from the path lookup so it cannot go away, and it shouldn't really matter if it does. Signed-off-by: NeilBrown --- snapshot.c | 114 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/snapshot.c b/snapshot.c index 07a53d7..2cfc1dd 100644 --- a/snapshot.c +++ b/snapshot.c @@ -14,20 +14,11 @@ /* * Mounting a snapshot is very different from mounting a new * filesystem. - * The 'dev' passed is really a patch to the mountpoint of the + * The 'dev' passed is really a path to the mountpoint of the * original - or atleast a file within the original. * We find that filesystem, make sure it is a LaFS, find the * named snapshot, and mount it read_only */ -static int match_one(struct super_block *sb, void *target) -{ - return sb == target; -} - -static int discard_one(struct super_block *sb, void *data) -{ - return -ENOENT; -} struct options { char *snapshot; @@ -62,6 +53,26 @@ static int parse_opts(struct options *op, const char *dev_name, char *data) return 0; } +/* s_fs_info for snapshots contains the snapshot number as well */ +struct snap_key { + struct sb_key k; + int ssnum; +}; + +static int snap_test(struct super_block *sb, void *data) +{ + struct snap_key *ptn = data; + struct snap_key *sk = container_of(sb->s_fs_info, struct snap_key, k); + return sk->k.fs == ptn->k.fs && sk->ssnum == ptn->ssnum; +} + +static int snap_set(struct super_block *sb, void *data) +{ + struct snap_key *sk = data; + sb->s_fs_info = &sk->k; + return set_anon_super(sb, NULL); +} + static int lafs_snap_get_sb(struct file_system_type *fstype, int flags, const char *dev_name, void *data, @@ -75,12 +86,13 @@ lafs_snap_get_sb(struct file_system_type *fstype, char *cdata = data; struct nameidata nd; int err; - struct super_block *sb, *sb2; + struct super_block *sb; struct fs *fs; int s; struct la_inode *lai; struct page *p; struct options op; + struct snap_key *sk; err = parse_opts(&op, dev_name, cdata); if (err) @@ -102,17 +114,13 @@ lafs_snap_get_sb(struct file_system_type *fstype, fs = fs_from_sb(sb); for (s = 1; s < fs->maxsnapshot; s++) { - struct datablock *b; - struct inode *rootdir; - struct sb_key *k; if (fs->ss[s].root_addr == 0) continue; if (fs->ss[s].root) { struct lafs_inode *li = LAFSI(fs->ss[s].root); - if (strcmp(li->md.fs.name, op.snapshot) == 0) { - /* Already mounted... what to do? FIXME */ - return -EBUSY; - } + if (strcmp(li->md.fs.name, op.snapshot) == 0) + /* found it */ + break; } err = lafs_load_page(fs, p, fs->ss[s].root_addr, 1); if (err) @@ -122,18 +130,38 @@ lafs_snap_get_sb(struct file_system_type *fstype, if (strncmp(lai->metadata[0].fs.name, op.snapshot, 64) != 0) continue; /* FIXME more checks? */ - - /* Ok, we have the right snapshot... now we need a superblock */ - sb = sget(&lafs_snap_fs_type, NULL, set_anon_super, NULL); - if (IS_ERR(sb)) - return PTR_ERR(sb); + /* Found it */ + break; + } + if (s == fs->maxsnapshot) { + err = -ENOENT; + goto fail; + } + /* Ok, we have the right snapshot... now we need a superblock */ + sk = kzalloc(sizeof(*sk), GFP_KERNEL); + if (!sk) { + err = -ENOMEM; + goto fail; + } + /* FIXME Inc refcount here?? */ + sk->k.fs = fs; + sk->ssnum = s; + + sb = sget(&lafs_snap_fs_type, snap_test, snap_set, NULL); + if (IS_ERR(sb)) { + kfree(sk); + err = PTR_ERR(sb); + goto fail; + } + if (sb->s_root) { + /* already existed */ + kfree(sk); + } else { + struct inode *rootdir; + struct datablock *b; sb->s_flags = flags | MS_RDONLY; - fs->ss[s].root = iget_locked(sb, 1); - k = kmalloc(sizeof(*k), GFP_KERNEL); - /* FIXME Inc refcount here?? */ - k->fs = fs; - k->root = fs->ss[s].root; - sb->s_fs_info = k; + + fs->ss[s].root = sk->k.root = iget_locked(sb, 0); b = lafs_get_block(fs->ss[s].root, 0, NULL, GFP_KERNEL, MKREF(snap)); b->b.physaddr = fs->ss[s].root_addr; @@ -146,30 +174,21 @@ lafs_snap_get_sb(struct file_system_type *fstype, if (!err) err = lafs_import_inode(fs->ss[s].root, b); putdref(b, MKREF(snap)); + unlock_new_inode(fs->ss[s].root); if (err) { - /* FIXME what to do with a locked inode? */ - deactivate_super(sb); + deactivate_locked_super(sb); goto fail; } - unlock_new_inode(fs->ss[s].root); rootdir = lafs_iget(sb, 2, SYNC); sb->s_root = d_alloc_root(rootdir); sb->s_op = fs->prime_sb->s_op; - sb2 = sget(&lafs_fs_type, match_one, discard_one, fs->prime_sb); - if (sb2 != fs->prime_sb) { - /* must be racing with umount */ - deactivate_super(sb2); - deactivate_super(sb); - err = -EBUSY; - goto fail; - } - up_write(&fs->prime_sb->s_umount); - path_put(&nd.path); - put_page(p); - simple_set_mnt(mnt, sb); - return 0; + sb->s_flags |= MS_ACTIVE; } - err = -ENOENT; + up_write(&sb->s_umount); + path_put(&nd.path); + put_page(p); + simple_set_mnt(mnt, sb); + return 0; fail: put_page(p); @@ -182,10 +201,9 @@ static void lafs_snap_kill_sb(struct super_block *sb) struct fs *fs = fs_from_sb(sb); printk("Generic_shutdown_super being called....\n"); - generic_shutdown_super(sb); + kill_anon_super(sb); kfree(sb->s_fs_info); printk("Generic_shutdown_super called\n"); - deactivate_super(fs->prime_sb); } struct file_system_type lafs_snap_fs_type = { -- 2.39.5