Merge tag 'v4.1-rockchip-socfixes2' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / 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);