]> Pileus Git - ~andy/linux/blobdiff - fs/nilfs2/cpfile.c
Merge branch 'for-rmk' of git://linux-arm.org/linux-2.6 into devel
[~andy/linux] / fs / nilfs2 / cpfile.c
index a4c9550fd7740522ee9f03a4dc24d036f3b787ad..300f1cdfa8620ab27cbc247d42f0850b011b4ce8 100644 (file)
@@ -40,10 +40,7 @@ nilfs_cpfile_checkpoints_per_block(const struct inode *cpfile)
 static unsigned long
 nilfs_cpfile_get_blkoff(const struct inode *cpfile, __u64 cno)
 {
-       __u64 tcno;
-
-       BUG_ON(cno == 0); /* checkpoint number 0 is invalid */
-       tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
+       __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
        do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
        return (unsigned long)tcno;
 }
@@ -96,7 +93,7 @@ nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile,
        struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);
        unsigned int count;
 
-       BUG_ON(le32_to_cpu(cp->cp_checkpoints_count) < n);
+       WARN_ON(le32_to_cpu(cp->cp_checkpoints_count) < n);
        count = le32_to_cpu(cp->cp_checkpoints_count) - n;
        cp->cp_checkpoints_count = cpu_to_le32(count);
        return count;
@@ -178,6 +175,8 @@ static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile,
  * %-ENOMEM - Insufficient amount of memory available.
  *
  * %-ENOENT - No such checkpoint.
+ *
+ * %-EINVAL - invalid checkpoint.
  */
 int nilfs_cpfile_get_checkpoint(struct inode *cpfile,
                                __u64 cno,
@@ -191,8 +190,9 @@ int nilfs_cpfile_get_checkpoint(struct inode *cpfile,
        void *kaddr;
        int ret;
 
-       BUG_ON(cno < 1 || cno > nilfs_mdt_cno(cpfile) ||
-              (cno < nilfs_mdt_cno(cpfile) && create));
+       if (unlikely(cno < 1 || cno > nilfs_mdt_cno(cpfile) ||
+                    (cno < nilfs_mdt_cno(cpfile) && create)))
+               return -EINVAL;
 
        down_write(&NILFS_MDT(cpfile)->mi_sem);
 
@@ -288,12 +288,11 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
        unsigned long tnicps;
        int ret, ncps, nicps, count, i;
 
-       if ((start == 0) || (start > end)) {
-               printk(KERN_CRIT "%s: start = %llu, end = %llu\n",
-                      __func__,
-                      (unsigned long long)start,
-                      (unsigned long long)end);
-               BUG();
+       if (unlikely(start == 0 || start > end)) {
+               printk(KERN_ERR "%s: invalid range of checkpoint numbers: "
+                      "[%llu, %llu)\n", __func__,
+                      (unsigned long long)start, (unsigned long long)end);
+               return -EINVAL;
        }
 
        /* cannot delete the latest checkpoint */
@@ -312,7 +311,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
                ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
                if (ret < 0) {
                        if (ret != -ENOENT)
-                               goto out_sem;
+                               goto out_header;
                        /* skip hole */
                        ret = 0;
                        continue;
@@ -323,7 +322,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
                        cpfile, cno, cp_bh, kaddr);
                nicps = 0;
                for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {
-                       BUG_ON(nilfs_checkpoint_snapshot(cp));
+                       WARN_ON(nilfs_checkpoint_snapshot(cp));
                        if (!nilfs_checkpoint_invalid(cp)) {
                                nilfs_checkpoint_set_invalid(cp);
                                nicps++;
@@ -345,7 +344,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
                                        continue;
                                printk(KERN_ERR "%s: cannot delete block\n",
                                       __func__);
-                               goto out_sem;
+                               goto out_header;
                        }
                }
 
@@ -362,6 +361,8 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
                nilfs_mdt_mark_dirty(cpfile);
                kunmap_atomic(kaddr, KM_USER0);
        }
+
+ out_header:
        brelse(header_bh);
 
  out_sem:
@@ -382,17 +383,19 @@ static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile,
        ci->ci_next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
 }
 
-static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 cno,
+static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
                                          struct nilfs_cpinfo *ci, size_t nci)
 {
        struct nilfs_checkpoint *cp;
        struct buffer_head *bh;
        size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
-       __u64 cur_cno = nilfs_mdt_cno(cpfile);
+       __u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop;
        void *kaddr;
        int n, ret;
        int ncps, i;
 
+       if (cno == 0)
+               return -ENOENT; /* checkpoint number 0 is invalid */
        down_read(&NILFS_MDT(cpfile)->mi_sem);
 
        for (n = 0; cno < cur_cno && n < nci; cno += ncps) {
@@ -416,6 +419,8 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 cno,
        }
 
        ret = n;
+       if (n > 0)
+               *cnop = ci[n - 1].ci_cno + 1;
 
  out:
        up_read(&NILFS_MDT(cpfile)->mi_sem);
@@ -431,7 +436,7 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
        __u64 curr = *cnop, next;
        unsigned long curr_blkoff, next_blkoff;
        void *kaddr;
-       int n, ret;
+       int n = 0, ret;
 
        down_read(&NILFS_MDT(cpfile)->mi_sem);
 
@@ -455,27 +460,33 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
 
        curr_blkoff = nilfs_cpfile_get_blkoff(cpfile, curr);
        ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &bh);
-       if (ret < 0)
+       if (unlikely(ret < 0)) {
+               if (ret == -ENOENT)
+                       ret = 0; /* No snapshots (started from a hole block) */
                goto out;
+       }
        kaddr = kmap_atomic(bh->b_page, KM_USER0);
-       for (n = 0; n < nci; n++) {
-               cp = nilfs_cpfile_block_get_checkpoint(
-                       cpfile, curr, bh, kaddr);
-               nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, &ci[n]);
-               next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
-               if (next == 0) {
-                       curr = ~(__u64)0; /* Terminator */
-                       n++;
+       while (n < nci) {
+               cp = nilfs_cpfile_block_get_checkpoint(cpfile, curr, bh, kaddr);
+               curr = ~(__u64)0; /* Terminator */
+               if (unlikely(nilfs_checkpoint_invalid(cp) ||
+                            !nilfs_checkpoint_snapshot(cp)))
                        break;
-               }
+               nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, &ci[n++]);
+               next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
+               if (next == 0)
+                       break; /* reach end of the snapshot list */
+
                next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next);
                if (curr_blkoff != next_blkoff) {
                        kunmap_atomic(kaddr, KM_USER0);
                        brelse(bh);
                        ret = nilfs_cpfile_get_checkpoint_block(cpfile, next,
                                                                0, &bh);
-                       if (ret < 0)
+                       if (unlikely(ret < 0)) {
+                               WARN_ON(ret == -ENOENT);
                                goto out;
+                       }
                        kaddr = kmap_atomic(bh->b_page, KM_USER0);
                }
                curr = next;
@@ -504,7 +515,7 @@ ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,
 {
        switch (mode) {
        case NILFS_CHECKPOINT:
-               return nilfs_cpfile_do_get_cpinfo(cpfile, *cnop, ci, nci);
+               return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, ci, nci);
        case NILFS_SNAPSHOT:
                return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, ci, nci);
        default:
@@ -520,13 +531,11 @@ ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,
 int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno)
 {
        struct nilfs_cpinfo ci;
+       __u64 tcno = cno;
        ssize_t nci;
        int ret;
 
-       /* checkpoint number 0 is invalid */
-       if (cno == 0)
-               return -ENOENT;
-       nci = nilfs_cpfile_do_get_cpinfo(cpfile, cno, &ci, 1);
+       nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, 1);
        if (nci < 0)
                return nci;
        else if (nci == 0 || ci.ci_cno != cno)
@@ -573,6 +582,8 @@ static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno)
        void *kaddr;
        int ret;
 
+       if (cno == 0)
+               return -ENOENT; /* checkpoint number 0 is invalid */
        down_write(&NILFS_MDT(cpfile)->mi_sem);
 
        ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
@@ -689,6 +700,8 @@ static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno)
        void *kaddr;
        int ret;
 
+       if (cno == 0)
+               return -ENOENT; /* checkpoint number 0 is invalid */
        down_write(&NILFS_MDT(cpfile)->mi_sem);
 
        ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
@@ -804,6 +817,8 @@ int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno)
        void *kaddr;
        int ret;
 
+       if (cno == 0)
+               return -ENOENT; /* checkpoint number 0 is invalid */
        down_read(&NILFS_MDT(cpfile)->mi_sem);
 
        ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh);