]> git.neil.brown.name Git - LaFS.git/commitdiff
New mount infrastructure for snapshot.
authorNeilBrown <neilb@suse.de>
Sun, 25 Jul 2010 04:38:53 +0000 (14:38 +1000)
committerNeilBrown <neilb@suse.de>
Mon, 9 Aug 2010 01:58:12 +0000 (11:58 +1000)
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 <neilb@suse.de>
snapshot.c

index 07a53d74ec2c9cf6b1ab524c694b5f4e93abff72..2cfc1dd285eaa341e23bf850a455b045d30e7d54 100644 (file)
 /*
  * 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 = {