nilfs2: shorten freeze period due to GC in write operation v3
authorJiro SEKIBA <jir@unicus.jp>
Thu, 3 Sep 2009 13:24:17 +0000 (22:24 +0900)
committerRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Mon, 14 Sep 2009 09:27:15 +0000 (18:27 +0900)
This is a re-revised patch to shorten freeze period.
This version include a fix of the bug Konishi-san mentioned last time.

When GC is runnning, GC moves live block to difference segments.
Copying live blocks into memory is done in a transaction,
however it is not necessarily to be in the transaction.
This patch will get the nilfs_ioctl_move_blocks() out from
transaction lock and put it before the transaction.

I ran sysbench fileio test against nilfs partition.
I copied some DVD/CD images and created snapshot to create live blocks
before starting the benchmark.

Followings are summary of rc8 and rc8 w/ the patch of per-request
statistics, which is min/max and avg.  I ran each test three times and
bellow is average of those numers.

According to this benchmark result, average time is slightly degrated.
However, worstcase (max) result is significantly improved.
This can address a few seconds write freeze.

- random write per-request performance of rc8
 min   0.843ms
 max 680.406ms
 avg   3.050ms
- random write per-request performance of rc8 w/ this patch
 min   0.843ms -> 100.00%
 max 380.490ms ->  55.90%
 avg   3.233ms -> 106.00%

- sequential write per-request performance of rc8
 min   0.736ms
 max 774.343ms
 avg   2.883ms
- sequential write per-request performance of rc8 w/ this patch
 min   0.720ms ->  97.80%
 max  644.280ms->  83.20%
 avg   3.130ms -> 108.50%

-----8<-----8<-----nilfs_cleanerd.conf-----8<-----8<-----
protection_period       150
selection_policy        timestamp       # timestamp in ascend order
nsegments_per_clean     2
cleaning_interval       2
retry_interval          60
use_mmap
log_priority            info
-----8<-----8<-----nilfs_cleanerd.conf-----8<-----8<-----

Signed-off-by: Jiro SEKIBA <jir@unicus.jp>
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
fs/nilfs2/ioctl.c
fs/nilfs2/the_nilfs.h

index 6ea5f872e2de041cb5ba028f2b6f2ff731ed90e4..6572ea4bc4df70498dc62b29b93968d0396c7775 100644 (file)
@@ -442,12 +442,6 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
        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) {
                /*
@@ -548,7 +542,25 @@ 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);
+
+       clear_nilfs_gc_running(nilfs);
 
  out_free:
        while (--n >= 0)
index 68e9626e3d44d1c59fb0812f9be0f159e2b0c1e3..20abd55881e0adc7903682c4fb15a3a057230a97 100644 (file)
@@ -37,6 +37,7 @@ enum {
        THE_NILFS_LOADED,       /* Roll-back/roll-forward has done and
                                   the latest checkpoint was loaded */
        THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */
+       THE_NILFS_GC_RUNNING,   /* gc process is running */
 };
 
 /**
@@ -195,6 +196,7 @@ static inline int nilfs_##name(struct the_nilfs *nilfs)                     \
 THE_NILFS_FNS(INIT, init)
 THE_NILFS_FNS(LOADED, loaded)
 THE_NILFS_FNS(DISCONTINUED, discontinued)
+THE_NILFS_FNS(GC_RUNNING, gc_running)
 
 /* Minimum interval of periodical update of superblocks (in seconds) */
 #define NILFS_SB_FREQ          10