nilfs2: improve execution time of NILFS_IOCTL_GET_CPINFO ioctl
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Thu, 16 Apr 2015 19:46:42 +0000 (12:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 17 Apr 2015 13:04:04 +0000 (09:04 -0400)
The older a filesystem gets, the slower lscp command becomes.  This is
because nilfs_cpfile_do_get_cpinfo() function meets more hole blocks
as the start offset of valid checkpoint numbers gets bigger.

This reduces the overhead by skipping hole blocks efficiently with
nilfs_mdt_find_block() helper.

A measurement result of this patch is as follows:

Before:
$ time lscp
                 CNO        DATE     TIME  MODE  FLG      BLKCNT       ICNT
             5769303  2015-02-22 19:31:33   cp    -          108          1
             5769304  2015-02-22 19:38:54   cp    -          108          1

real    0m0.182s
user    0m0.003s
sys     0m0.180s

After:
$ time lscp
                 CNO        DATE     TIME  MODE  FLG      BLKCNT       ICNT
             5769303  2015-02-22 19:31:33   cp    -          108          1
             5769304  2015-02-22 19:38:54   cp    -          108          1

real    0m0.003s
user    0m0.001s
sys     0m0.002s

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/nilfs2/cpfile.c

index 0d58075f34e29da6f666b008afdcaa74596b0255..b6596cab9e99e4a2e680b54750b9da09e8946c86 100644 (file)
@@ -53,6 +53,13 @@ nilfs_cpfile_get_offset(const struct inode *cpfile, __u64 cno)
        return do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
 }
 
+static __u64 nilfs_cpfile_first_checkpoint_in_block(const struct inode *cpfile,
+                                                   unsigned long blkoff)
+{
+       return (__u64)nilfs_cpfile_checkpoints_per_block(cpfile) * blkoff
+               + 1 - NILFS_MDT(cpfile)->mi_first_entry_offset;
+}
+
 static unsigned long
 nilfs_cpfile_checkpoints_in_block(const struct inode *cpfile,
                                  __u64 curr,
@@ -146,6 +153,44 @@ static inline int nilfs_cpfile_get_checkpoint_block(struct inode *cpfile,
                                   create, nilfs_cpfile_block_init, bhp);
 }
 
+/**
+ * nilfs_cpfile_find_checkpoint_block - find and get a buffer on cpfile
+ * @cpfile: inode of cpfile
+ * @start_cno: start checkpoint number (inclusive)
+ * @end_cno: end checkpoint number (inclusive)
+ * @cnop: place to store the next checkpoint number
+ * @bhp: place to store a pointer to buffer_head struct
+ *
+ * Return Value: On success, it returns 0. On error, the following negative
+ * error code is returned.
+ *
+ * %-ENOMEM - Insufficient memory available.
+ *
+ * %-EIO - I/O error
+ *
+ * %-ENOENT - no block exists in the range.
+ */
+static int nilfs_cpfile_find_checkpoint_block(struct inode *cpfile,
+                                             __u64 start_cno, __u64 end_cno,
+                                             __u64 *cnop,
+                                             struct buffer_head **bhp)
+{
+       unsigned long start, end, blkoff;
+       int ret;
+
+       if (unlikely(start_cno > end_cno))
+               return -ENOENT;
+
+       start = nilfs_cpfile_get_blkoff(cpfile, start_cno);
+       end = nilfs_cpfile_get_blkoff(cpfile, end_cno);
+
+       ret = nilfs_mdt_find_block(cpfile, start, end, &blkoff, bhp);
+       if (!ret)
+               *cnop = (blkoff == start) ? start_cno :
+                       nilfs_cpfile_first_checkpoint_in_block(cpfile, blkoff);
+       return ret;
+}
+
 static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile,
                                                       __u64 cno)
 {
@@ -403,14 +448,15 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
                return -ENOENT; /* checkpoint number 0 is invalid */
        down_read(&NILFS_MDT(cpfile)->mi_sem);
 
-       for (n = 0; cno < cur_cno && n < nci; cno += ncps) {
-               ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno);
-               ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh);
+       for (n = 0; n < nci; cno += ncps) {
+               ret = nilfs_cpfile_find_checkpoint_block(
+                       cpfile, cno, cur_cno - 1, &cno, &bh);
                if (ret < 0) {
-                       if (ret != -ENOENT)
-                               goto out;
-                       continue; /* skip hole */
+                       if (likely(ret == -ENOENT))
+                               break;
+                       goto out;
                }
+               ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno);
 
                kaddr = kmap_atomic(bh->b_page);
                cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);