nilfs2: add local variable to cache the number of clean segments
[safe/jmp/linux-2.6] / fs / nilfs2 / ioctl.c
index d6759b9..f6af760 100644 (file)
@@ -99,7 +99,8 @@ static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
 static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
                                     unsigned int cmd, void __user *argp)
 {
-       struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile;
+       struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
+       struct inode *cpfile = nilfs->ns_cpfile;
        struct nilfs_transaction_info ti;
        struct nilfs_cpmode cpmode;
        int ret;
@@ -109,14 +110,17 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
        if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
                return -EFAULT;
 
+       mutex_lock(&nilfs->ns_mount_mutex);
        nilfs_transaction_begin(inode->i_sb, &ti, 0);
        ret = nilfs_cpfile_change_cpmode(
                cpfile, cpmode.cm_cno, cpmode.cm_mode);
        if (unlikely(ret < 0)) {
                nilfs_transaction_abort(inode->i_sb);
+               mutex_unlock(&nilfs->ns_mount_mutex);
                return ret;
        }
        nilfs_transaction_commit(inode->i_sb); /* never fails */
+       mutex_unlock(&nilfs->ns_mount_mutex);
        return ret;
 }
 
@@ -152,7 +156,7 @@ nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
 
        down_read(&nilfs->ns_segctor_sem);
        ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
-                                     nmembs);
+                                     size, nmembs);
        up_read(&nilfs->ns_segctor_sem);
        return ret;
 }
@@ -182,7 +186,8 @@ nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
        int ret;
 
        down_read(&nilfs->ns_segctor_sem);
-       ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs);
+       ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size,
+                                     nmembs);
        up_read(&nilfs->ns_segctor_sem);
        return ret;
 }
@@ -212,7 +217,7 @@ nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
        int ret;
 
        down_read(&nilfs->ns_segctor_sem);
-       ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs);
+       ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, size, nmembs);
        up_read(&nilfs->ns_segctor_sem);
        return ret;
 }
@@ -296,7 +301,18 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode,
                               (unsigned long long)vdesc->vd_vblocknr);
                return ret;
        }
-       bh->b_private = vdesc;
+       if (unlikely(!list_empty(&bh->b_assoc_buffers))) {
+               printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, "
+                      "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n",
+                      __func__, vdesc->vd_flags ? "node" : "data",
+                      (unsigned long long)vdesc->vd_ino,
+                      (unsigned long long)vdesc->vd_cno,
+                      (unsigned long long)vdesc->vd_offset,
+                      (unsigned long long)vdesc->vd_blocknr,
+                      (unsigned long long)vdesc->vd_vblocknr);
+               brelse(bh);
+               return -EEXIST;
+       }
        list_add_tail(&bh->b_assoc_buffers, buffers);
        return 0;
 }
@@ -334,24 +350,10 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
        list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
                ret = nilfs_gccache_wait_and_mark_dirty(bh);
                if (unlikely(ret < 0)) {
-                       if (ret == -EEXIST) {
-                               vdesc = bh->b_private;
-                               printk(KERN_CRIT
-                                      "%s: conflicting %s buffer: "
-                                      "ino=%llu, cno=%llu, offset=%llu, "
-                                      "blocknr=%llu, vblocknr=%llu\n",
-                                      __func__,
-                                      vdesc->vd_flags ? "node" : "data",
-                                      (unsigned long long)vdesc->vd_ino,
-                                      (unsigned long long)vdesc->vd_cno,
-                                      (unsigned long long)vdesc->vd_offset,
-                                      (unsigned long long)vdesc->vd_blocknr,
-                                      (unsigned long long)vdesc->vd_vblocknr);
-                       }
+                       WARN_ON(ret == -EEXIST);
                        goto failed;
                }
                list_del_init(&bh->b_assoc_buffers);
-               bh->b_private = NULL;
                brelse(bh);
        }
        return nmembs;
@@ -359,7 +361,6 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
  failed:
        list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
                list_del_init(&bh->b_assoc_buffers);
-               bh->b_private = NULL;
                brelse(bh);
        }
        return ret;
@@ -435,36 +436,12 @@ static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs,
        return nmembs;
 }
 
-static int nilfs_ioctl_free_segments(struct the_nilfs *nilfs,
-                                    struct nilfs_argv *argv, void *buf)
-{
-       size_t nmembs = argv->v_nmembs;
-       struct nilfs_sb_info *sbi = nilfs->ns_writer;
-       int ret;
-
-       if (unlikely(!sbi)) {
-               /* never happens because called for a writable mount */
-               WARN_ON(1);
-               return -EROFS;
-       }
-       ret = nilfs_segctor_add_segments_to_be_freed(
-               NILFS_SC(sbi), buf, nmembs);
-
-       return (ret < 0) ? ret : nmembs;
-}
-
 int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
                                       struct nilfs_argv *argv, void **kbufs)
 {
        const char *msg;
        int ret;
 
-       ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]);
-       if (ret < 0) {
-               msg = "cannot read source blocks";
-               goto failed;
-       }
-
        ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]);
        if (ret < 0) {
                /*
@@ -491,18 +468,9 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
                msg = "cannot mark copying blocks dirty";
                goto failed;
        }
-       ret = nilfs_ioctl_free_segments(nilfs, &argv[4], kbufs[4]);
-       if (ret < 0) {
-               /*
-                * can safely abort because this operation is atomic.
-                */
-               msg = "cannot set segments to be freed";
-               goto failed;
-       }
        return 0;
 
  failed:
-       nilfs_remove_all_gcinode(nilfs);
        printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n",
               msg, ret);
        return ret;
@@ -573,7 +541,27 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
                }
        }
 
-       ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
+       /*
+        * nilfs_ioctl_move_blocks() will call nilfs_gc_iget(),
+        * which will operates an inode list without blocking.
+        * To protect the list from concurrent operations,
+        * nilfs_ioctl_move_blocks should be atomic operation.
+        */
+       if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) {
+               ret = -EBUSY;
+               goto out_free;
+       }
+
+       ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]);
+       if (ret < 0)
+               printk(KERN_ERR "NILFS: GC failed during preparation: "
+                       "cannot read source blocks: err=%d\n", ret);
+       else
+               ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
+
+       if (ret < 0)
+               nilfs_remove_all_gcinode(nilfs);
+       clear_nilfs_gc_running(nilfs);
 
  out_free:
        while (--n >= 0)
@@ -615,7 +603,7 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
        if (copy_from_user(&argv, argp, sizeof(argv)))
                return -EFAULT;
 
-       if (argv.v_size != membsz)
+       if (argv.v_size < membsz)
                return -EINVAL;
 
        ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc);