nilfs2: get rid of sget use for acquiring nilfs object
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Sun, 7 Jun 2009 16:39:29 +0000 (01:39 +0900)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 12 Jun 2009 01:36:17 +0000 (21:36 -0400)
This will change the way to obtain nilfs object in nilfs_get_sb()
function.

Previously, a preliminary sget() call was performed, and the nilfs
object was acquired from a super block instance found by the sget()
call.

This patch, instead, instroduces a new dedicated function
find_or_create_nilfs(); as the name implies, the function finds an
existent nilfs object from a global list or creates a new one if no
object is found on the device.

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/nilfs2/super.c
fs/nilfs2/the_nilfs.c
fs/nilfs2/the_nilfs.h

index 1c505d0..3c9833e 100644 (file)
@@ -1061,13 +1061,6 @@ static int nilfs_set_bdev_super(struct super_block *s, void *data)
 static int nilfs_test_bdev_super(struct super_block *s, void *data)
 {
        struct nilfs_super_data *sd = data;
-
-       return s->s_bdev == sd->bdev;
-}
-
-static int nilfs_test_bdev_super2(struct super_block *s, void *data)
-{
-       struct nilfs_super_data *sd = data;
        int ret;
 
        if (s->s_bdev != sd->bdev)
@@ -1096,8 +1089,8 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
             const char *dev_name, void *data, struct vfsmount *mnt)
 {
        struct nilfs_super_data sd;
-       struct super_block *s, *s2;
-       struct the_nilfs *nilfs = NULL;
+       struct super_block *s;
+       struct the_nilfs *nilfs;
        int err, need_to_close = 1;
 
        sd.bdev = open_bdev_exclusive(dev_name, flags, fs_type);
@@ -1118,11 +1111,12 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
                goto failed;
        }
 
-       /*
-        * 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
-        */
+       nilfs = find_or_create_nilfs(sd.bdev);
+       if (!nilfs) {
+               err = -ENOMEM;
+               goto failed;
+       }
+
        down(&sd.bdev->bd_mount_sem);
        if (!sd.cno &&
            (err = test_exclusive_mount(fs_type, sd.bdev, flags ^ MS_RDONLY))) {
@@ -1131,51 +1125,22 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
        }
 
        /*
-        * Phase-1: search any existent instance and get the_nilfs
+        * Search specified snapshot or R/W mode super_block
         */
-       s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, &sd);
-       if (IS_ERR(s))
-               goto error_s;
-
-       if (!s->s_root) {
-               err = -ENOMEM;
-               nilfs = alloc_nilfs(sd.bdev);
-               if (!nilfs)
-                       goto cancel_new;
-       } else {
-               struct nilfs_sb_info *sbi = NILFS_SB(s);
+       if (!sd.cno)
+               /* trying to get the latest checkpoint.  */
+               sd.cno = nilfs_last_cno(nilfs);
 
-               /*
-                * s_umount protects super_block from unmount process;
-                * It covers pointers of nilfs_sb_info and the_nilfs.
-                */
-               nilfs = sbi->s_nilfs;
-               get_nilfs(nilfs);
-               up_write(&s->s_umount);
-
-               /*
-                * Phase-2: search specified snapshot or R/W mode super_block
-                */
-               if (!sd.cno)
-                       /* trying to get the latest checkpoint.  */
-                       sd.cno = nilfs_last_cno(nilfs);
-
-               s2 = sget(fs_type, nilfs_test_bdev_super2,
-                         nilfs_set_bdev_super, &sd);
-               deactivate_super(s);
-               /*
-                * Although deactivate_super() invokes close_bdev_exclusive() at
-                * kill_block_super().  Here, s is an existent mount; we need
-                * one more close_bdev_exclusive() call.
-                */
-               s = s2;
-               if (IS_ERR(s))
-                       goto error_s;
+       s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, &sd);
+       if (IS_ERR(s)) {
+               err = PTR_ERR(s);
+               goto failed_unlock;
        }
 
        if (!s->s_root) {
                char b[BDEVNAME_SIZE];
 
+               /* New superblock instance created */
                s->s_flags = flags;
                strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id));
                sb_set_blocksize(s, block_size(sd.bdev));
@@ -1195,15 +1160,9 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
        simple_set_mnt(mnt, s);
        return 0;
 
- error_s:
-       up(&sd.bdev->bd_mount_sem);
-       if (nilfs)
-               put_nilfs(nilfs);
-       close_bdev_exclusive(sd.bdev, flags);
-       return PTR_ERR(s);
-
  failed_unlock:
        up(&sd.bdev->bd_mount_sem);
+       put_nilfs(nilfs);
  failed:
        close_bdev_exclusive(sd.bdev, flags);
 
@@ -1212,8 +1171,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
  cancel_new:
        /* Abandoning the newly allocated superblock */
        up(&sd.bdev->bd_mount_sem);
-       if (nilfs)
-               put_nilfs(nilfs);
+       put_nilfs(nilfs);
        up_write(&s->s_umount);
        deactivate_super(s);
        /*
index a91f15b..45dbf6a 100644 (file)
 #include "seglist.h"
 #include "segbuf.h"
 
+
+static LIST_HEAD(nilfs_objects);
+static DEFINE_SPINLOCK(nilfs_lock);
+
 void nilfs_set_last_segment(struct the_nilfs *nilfs,
                            sector_t start_blocknr, u64 seq, __u64 cno)
 {
@@ -55,7 +59,7 @@ void nilfs_set_last_segment(struct the_nilfs *nilfs,
  * Return Value: On success, pointer to the_nilfs is returned.
  * On error, NULL is returned.
  */
-struct the_nilfs *alloc_nilfs(struct block_device *bdev)
+static struct the_nilfs *alloc_nilfs(struct block_device *bdev)
 {
        struct the_nilfs *nilfs;
 
@@ -69,6 +73,7 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
        atomic_set(&nilfs->ns_ndirtyblks, 0);
        init_rwsem(&nilfs->ns_sem);
        mutex_init(&nilfs->ns_writer_mutex);
+       INIT_LIST_HEAD(&nilfs->ns_list);
        INIT_LIST_HEAD(&nilfs->ns_supers);
        spin_lock_init(&nilfs->ns_last_segment_lock);
        nilfs->ns_gc_inodes_h = NULL;
@@ -78,6 +83,45 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
 }
 
 /**
+ * find_or_create_nilfs - find or create nilfs object
+ * @bdev: block device to which the_nilfs is related
+ *
+ * find_nilfs() looks up an existent nilfs object created on the
+ * device and gets the reference count of the object.  If no nilfs object
+ * is found on the device, a new nilfs object is allocated.
+ *
+ * Return Value: On success, pointer to the nilfs object is returned.
+ * On error, NULL is returned.
+ */
+struct the_nilfs *find_or_create_nilfs(struct block_device *bdev)
+{
+       struct the_nilfs *nilfs, *new = NULL;
+
+ retry:
+       spin_lock(&nilfs_lock);
+       list_for_each_entry(nilfs, &nilfs_objects, ns_list) {
+               if (nilfs->ns_bdev == bdev) {
+                       get_nilfs(nilfs);
+                       spin_unlock(&nilfs_lock);
+                       if (new)
+                               put_nilfs(new);
+                       return nilfs; /* existing object */
+               }
+       }
+       if (new) {
+               list_add_tail(&new->ns_list, &nilfs_objects);
+               spin_unlock(&nilfs_lock);
+               return new; /* new object */
+       }
+       spin_unlock(&nilfs_lock);
+
+       new = alloc_nilfs(bdev);
+       if (new)
+               goto retry;
+       return NULL; /* insufficient memory */
+}
+
+/**
  * put_nilfs - release a reference to the_nilfs
  * @nilfs: the_nilfs structure to be released
  *
@@ -86,13 +130,20 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev)
  */
 void put_nilfs(struct the_nilfs *nilfs)
 {
-       if (!atomic_dec_and_test(&nilfs->ns_count))
+       spin_lock(&nilfs_lock);
+       if (!atomic_dec_and_test(&nilfs->ns_count)) {
+               spin_unlock(&nilfs_lock);
                return;
+       }
+       list_del_init(&nilfs->ns_list);
+       spin_unlock(&nilfs_lock);
+
        /*
-        * Increment of ns_count never occur below because the caller
+        * Increment of ns_count never occurs below because the caller
         * of get_nilfs() holds at least one reference to the_nilfs.
         * Thus its exclusion control is not required here.
         */
+
        might_sleep();
        if (nilfs_loaded(nilfs)) {
                nilfs_mdt_clear(nilfs->ns_sufile);
index 30fe587..116caf9 100644 (file)
@@ -43,6 +43,7 @@ enum {
  * struct the_nilfs - struct to supervise multiple nilfs mount points
  * @ns_flags: flags
  * @ns_count: reference count
+ * @ns_list: list head for nilfs_list
  * @ns_bdev: block device
  * @ns_bdi: backing dev info
  * @ns_writer: back pointer to writable nilfs_sb_info
@@ -88,6 +89,7 @@ enum {
 struct the_nilfs {
        unsigned long           ns_flags;
        atomic_t                ns_count;
+       struct list_head        ns_list;
 
        struct block_device    *ns_bdev;
        struct backing_dev_info *ns_bdi;
@@ -191,7 +193,7 @@ THE_NILFS_FNS(DISCONTINUED, discontinued)
 #define NILFS_ALTSB_FREQ       60  /* spare superblock */
 
 void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64);
-struct the_nilfs *alloc_nilfs(struct block_device *);
+struct the_nilfs *find_or_create_nilfs(struct block_device *);
 void put_nilfs(struct the_nilfs *);
 int init_nilfs(struct the_nilfs *, struct nilfs_sb_info *, char *);
 int load_nilfs(struct the_nilfs *, struct nilfs_sb_info *);