Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * NILFS checkpoint file.
0004  *
0005  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
0006  *
0007  * Written by Koji Sato.
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/fs.h>
0012 #include <linux/string.h>
0013 #include <linux/buffer_head.h>
0014 #include <linux/errno.h>
0015 #include "mdt.h"
0016 #include "cpfile.h"
0017 
0018 
0019 static inline unsigned long
0020 nilfs_cpfile_checkpoints_per_block(const struct inode *cpfile)
0021 {
0022     return NILFS_MDT(cpfile)->mi_entries_per_block;
0023 }
0024 
0025 /* block number from the beginning of the file */
0026 static unsigned long
0027 nilfs_cpfile_get_blkoff(const struct inode *cpfile, __u64 cno)
0028 {
0029     __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
0030 
0031     do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
0032     return (unsigned long)tcno;
0033 }
0034 
0035 /* offset in block */
0036 static unsigned long
0037 nilfs_cpfile_get_offset(const struct inode *cpfile, __u64 cno)
0038 {
0039     __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
0040 
0041     return do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
0042 }
0043 
0044 static __u64 nilfs_cpfile_first_checkpoint_in_block(const struct inode *cpfile,
0045                             unsigned long blkoff)
0046 {
0047     return (__u64)nilfs_cpfile_checkpoints_per_block(cpfile) * blkoff
0048         + 1 - NILFS_MDT(cpfile)->mi_first_entry_offset;
0049 }
0050 
0051 static unsigned long
0052 nilfs_cpfile_checkpoints_in_block(const struct inode *cpfile,
0053                   __u64 curr,
0054                   __u64 max)
0055 {
0056     return min_t(__u64,
0057              nilfs_cpfile_checkpoints_per_block(cpfile) -
0058              nilfs_cpfile_get_offset(cpfile, curr),
0059              max - curr);
0060 }
0061 
0062 static inline int nilfs_cpfile_is_in_first(const struct inode *cpfile,
0063                        __u64 cno)
0064 {
0065     return nilfs_cpfile_get_blkoff(cpfile, cno) == 0;
0066 }
0067 
0068 static unsigned int
0069 nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile,
0070                      struct buffer_head *bh,
0071                      void *kaddr,
0072                      unsigned int n)
0073 {
0074     struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);
0075     unsigned int count;
0076 
0077     count = le32_to_cpu(cp->cp_checkpoints_count) + n;
0078     cp->cp_checkpoints_count = cpu_to_le32(count);
0079     return count;
0080 }
0081 
0082 static unsigned int
0083 nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile,
0084                      struct buffer_head *bh,
0085                      void *kaddr,
0086                      unsigned int n)
0087 {
0088     struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);
0089     unsigned int count;
0090 
0091     WARN_ON(le32_to_cpu(cp->cp_checkpoints_count) < n);
0092     count = le32_to_cpu(cp->cp_checkpoints_count) - n;
0093     cp->cp_checkpoints_count = cpu_to_le32(count);
0094     return count;
0095 }
0096 
0097 static inline struct nilfs_cpfile_header *
0098 nilfs_cpfile_block_get_header(const struct inode *cpfile,
0099                   struct buffer_head *bh,
0100                   void *kaddr)
0101 {
0102     return kaddr + bh_offset(bh);
0103 }
0104 
0105 static struct nilfs_checkpoint *
0106 nilfs_cpfile_block_get_checkpoint(const struct inode *cpfile, __u64 cno,
0107                   struct buffer_head *bh,
0108                   void *kaddr)
0109 {
0110     return kaddr + bh_offset(bh) + nilfs_cpfile_get_offset(cpfile, cno) *
0111         NILFS_MDT(cpfile)->mi_entry_size;
0112 }
0113 
0114 static void nilfs_cpfile_block_init(struct inode *cpfile,
0115                     struct buffer_head *bh,
0116                     void *kaddr)
0117 {
0118     struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);
0119     size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
0120     int n = nilfs_cpfile_checkpoints_per_block(cpfile);
0121 
0122     while (n-- > 0) {
0123         nilfs_checkpoint_set_invalid(cp);
0124         cp = (void *)cp + cpsz;
0125     }
0126 }
0127 
0128 static inline int nilfs_cpfile_get_header_block(struct inode *cpfile,
0129                         struct buffer_head **bhp)
0130 {
0131     return nilfs_mdt_get_block(cpfile, 0, 0, NULL, bhp);
0132 }
0133 
0134 static inline int nilfs_cpfile_get_checkpoint_block(struct inode *cpfile,
0135                             __u64 cno,
0136                             int create,
0137                             struct buffer_head **bhp)
0138 {
0139     return nilfs_mdt_get_block(cpfile,
0140                    nilfs_cpfile_get_blkoff(cpfile, cno),
0141                    create, nilfs_cpfile_block_init, bhp);
0142 }
0143 
0144 /**
0145  * nilfs_cpfile_find_checkpoint_block - find and get a buffer on cpfile
0146  * @cpfile: inode of cpfile
0147  * @start_cno: start checkpoint number (inclusive)
0148  * @end_cno: end checkpoint number (inclusive)
0149  * @cnop: place to store the next checkpoint number
0150  * @bhp: place to store a pointer to buffer_head struct
0151  *
0152  * Return Value: On success, it returns 0. On error, the following negative
0153  * error code is returned.
0154  *
0155  * %-ENOMEM - Insufficient memory available.
0156  *
0157  * %-EIO - I/O error
0158  *
0159  * %-ENOENT - no block exists in the range.
0160  */
0161 static int nilfs_cpfile_find_checkpoint_block(struct inode *cpfile,
0162                           __u64 start_cno, __u64 end_cno,
0163                           __u64 *cnop,
0164                           struct buffer_head **bhp)
0165 {
0166     unsigned long start, end, blkoff;
0167     int ret;
0168 
0169     if (unlikely(start_cno > end_cno))
0170         return -ENOENT;
0171 
0172     start = nilfs_cpfile_get_blkoff(cpfile, start_cno);
0173     end = nilfs_cpfile_get_blkoff(cpfile, end_cno);
0174 
0175     ret = nilfs_mdt_find_block(cpfile, start, end, &blkoff, bhp);
0176     if (!ret)
0177         *cnop = (blkoff == start) ? start_cno :
0178             nilfs_cpfile_first_checkpoint_in_block(cpfile, blkoff);
0179     return ret;
0180 }
0181 
0182 static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile,
0183                                __u64 cno)
0184 {
0185     return nilfs_mdt_delete_block(cpfile,
0186                       nilfs_cpfile_get_blkoff(cpfile, cno));
0187 }
0188 
0189 /**
0190  * nilfs_cpfile_get_checkpoint - get a checkpoint
0191  * @cpfile: inode of checkpoint file
0192  * @cno: checkpoint number
0193  * @create: create flag
0194  * @cpp: pointer to a checkpoint
0195  * @bhp: pointer to a buffer head
0196  *
0197  * Description: nilfs_cpfile_get_checkpoint() acquires the checkpoint
0198  * specified by @cno. A new checkpoint will be created if @cno is the current
0199  * checkpoint number and @create is nonzero.
0200  *
0201  * Return Value: On success, 0 is returned, and the checkpoint and the
0202  * buffer head of the buffer on which the checkpoint is located are stored in
0203  * the place pointed by @cpp and @bhp, respectively. On error, one of the
0204  * following negative error codes is returned.
0205  *
0206  * %-EIO - I/O error.
0207  *
0208  * %-ENOMEM - Insufficient amount of memory available.
0209  *
0210  * %-ENOENT - No such checkpoint.
0211  *
0212  * %-EINVAL - invalid checkpoint.
0213  */
0214 int nilfs_cpfile_get_checkpoint(struct inode *cpfile,
0215                 __u64 cno,
0216                 int create,
0217                 struct nilfs_checkpoint **cpp,
0218                 struct buffer_head **bhp)
0219 {
0220     struct buffer_head *header_bh, *cp_bh;
0221     struct nilfs_cpfile_header *header;
0222     struct nilfs_checkpoint *cp;
0223     void *kaddr;
0224     int ret;
0225 
0226     if (unlikely(cno < 1 || cno > nilfs_mdt_cno(cpfile) ||
0227              (cno < nilfs_mdt_cno(cpfile) && create)))
0228         return -EINVAL;
0229 
0230     down_write(&NILFS_MDT(cpfile)->mi_sem);
0231 
0232     ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
0233     if (ret < 0)
0234         goto out_sem;
0235     ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, create, &cp_bh);
0236     if (ret < 0)
0237         goto out_header;
0238     kaddr = kmap(cp_bh->b_page);
0239     cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
0240     if (nilfs_checkpoint_invalid(cp)) {
0241         if (!create) {
0242             kunmap(cp_bh->b_page);
0243             brelse(cp_bh);
0244             ret = -ENOENT;
0245             goto out_header;
0246         }
0247         /* a newly-created checkpoint */
0248         nilfs_checkpoint_clear_invalid(cp);
0249         if (!nilfs_cpfile_is_in_first(cpfile, cno))
0250             nilfs_cpfile_block_add_valid_checkpoints(cpfile, cp_bh,
0251                                  kaddr, 1);
0252         mark_buffer_dirty(cp_bh);
0253 
0254         kaddr = kmap_atomic(header_bh->b_page);
0255         header = nilfs_cpfile_block_get_header(cpfile, header_bh,
0256                                kaddr);
0257         le64_add_cpu(&header->ch_ncheckpoints, 1);
0258         kunmap_atomic(kaddr);
0259         mark_buffer_dirty(header_bh);
0260         nilfs_mdt_mark_dirty(cpfile);
0261     }
0262 
0263     if (cpp != NULL)
0264         *cpp = cp;
0265     *bhp = cp_bh;
0266 
0267  out_header:
0268     brelse(header_bh);
0269 
0270  out_sem:
0271     up_write(&NILFS_MDT(cpfile)->mi_sem);
0272     return ret;
0273 }
0274 
0275 /**
0276  * nilfs_cpfile_put_checkpoint - put a checkpoint
0277  * @cpfile: inode of checkpoint file
0278  * @cno: checkpoint number
0279  * @bh: buffer head
0280  *
0281  * Description: nilfs_cpfile_put_checkpoint() releases the checkpoint
0282  * specified by @cno. @bh must be the buffer head which has been returned by
0283  * a previous call to nilfs_cpfile_get_checkpoint() with @cno.
0284  */
0285 void nilfs_cpfile_put_checkpoint(struct inode *cpfile, __u64 cno,
0286                  struct buffer_head *bh)
0287 {
0288     kunmap(bh->b_page);
0289     brelse(bh);
0290 }
0291 
0292 /**
0293  * nilfs_cpfile_delete_checkpoints - delete checkpoints
0294  * @cpfile: inode of checkpoint file
0295  * @start: start checkpoint number
0296  * @end: end checkpoint number
0297  *
0298  * Description: nilfs_cpfile_delete_checkpoints() deletes the checkpoints in
0299  * the period from @start to @end, excluding @end itself. The checkpoints
0300  * which have been already deleted are ignored.
0301  *
0302  * Return Value: On success, 0 is returned. On error, one of the following
0303  * negative error codes is returned.
0304  *
0305  * %-EIO - I/O error.
0306  *
0307  * %-ENOMEM - Insufficient amount of memory available.
0308  *
0309  * %-EINVAL - invalid checkpoints.
0310  */
0311 int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
0312                     __u64 start,
0313                     __u64 end)
0314 {
0315     struct buffer_head *header_bh, *cp_bh;
0316     struct nilfs_cpfile_header *header;
0317     struct nilfs_checkpoint *cp;
0318     size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
0319     __u64 cno;
0320     void *kaddr;
0321     unsigned long tnicps;
0322     int ret, ncps, nicps, nss, count, i;
0323 
0324     if (unlikely(start == 0 || start > end)) {
0325         nilfs_err(cpfile->i_sb,
0326               "cannot delete checkpoints: invalid range [%llu, %llu)",
0327               (unsigned long long)start, (unsigned long long)end);
0328         return -EINVAL;
0329     }
0330 
0331     down_write(&NILFS_MDT(cpfile)->mi_sem);
0332 
0333     ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
0334     if (ret < 0)
0335         goto out_sem;
0336     tnicps = 0;
0337     nss = 0;
0338 
0339     for (cno = start; cno < end; cno += ncps) {
0340         ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end);
0341         ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
0342         if (ret < 0) {
0343             if (ret != -ENOENT)
0344                 break;
0345             /* skip hole */
0346             ret = 0;
0347             continue;
0348         }
0349 
0350         kaddr = kmap_atomic(cp_bh->b_page);
0351         cp = nilfs_cpfile_block_get_checkpoint(
0352             cpfile, cno, cp_bh, kaddr);
0353         nicps = 0;
0354         for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {
0355             if (nilfs_checkpoint_snapshot(cp)) {
0356                 nss++;
0357             } else if (!nilfs_checkpoint_invalid(cp)) {
0358                 nilfs_checkpoint_set_invalid(cp);
0359                 nicps++;
0360             }
0361         }
0362         if (nicps > 0) {
0363             tnicps += nicps;
0364             mark_buffer_dirty(cp_bh);
0365             nilfs_mdt_mark_dirty(cpfile);
0366             if (!nilfs_cpfile_is_in_first(cpfile, cno)) {
0367                 count =
0368                   nilfs_cpfile_block_sub_valid_checkpoints(
0369                         cpfile, cp_bh, kaddr, nicps);
0370                 if (count == 0) {
0371                     /* make hole */
0372                     kunmap_atomic(kaddr);
0373                     brelse(cp_bh);
0374                     ret =
0375                       nilfs_cpfile_delete_checkpoint_block(
0376                                    cpfile, cno);
0377                     if (ret == 0)
0378                         continue;
0379                     nilfs_err(cpfile->i_sb,
0380                           "error %d deleting checkpoint block",
0381                           ret);
0382                     break;
0383                 }
0384             }
0385         }
0386 
0387         kunmap_atomic(kaddr);
0388         brelse(cp_bh);
0389     }
0390 
0391     if (tnicps > 0) {
0392         kaddr = kmap_atomic(header_bh->b_page);
0393         header = nilfs_cpfile_block_get_header(cpfile, header_bh,
0394                                kaddr);
0395         le64_add_cpu(&header->ch_ncheckpoints, -(u64)tnicps);
0396         mark_buffer_dirty(header_bh);
0397         nilfs_mdt_mark_dirty(cpfile);
0398         kunmap_atomic(kaddr);
0399     }
0400 
0401     brelse(header_bh);
0402     if (nss > 0)
0403         ret = -EBUSY;
0404 
0405  out_sem:
0406     up_write(&NILFS_MDT(cpfile)->mi_sem);
0407     return ret;
0408 }
0409 
0410 static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile,
0411                           struct nilfs_checkpoint *cp,
0412                           struct nilfs_cpinfo *ci)
0413 {
0414     ci->ci_flags = le32_to_cpu(cp->cp_flags);
0415     ci->ci_cno = le64_to_cpu(cp->cp_cno);
0416     ci->ci_create = le64_to_cpu(cp->cp_create);
0417     ci->ci_nblk_inc = le64_to_cpu(cp->cp_nblk_inc);
0418     ci->ci_inodes_count = le64_to_cpu(cp->cp_inodes_count);
0419     ci->ci_blocks_count = le64_to_cpu(cp->cp_blocks_count);
0420     ci->ci_next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
0421 }
0422 
0423 static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
0424                       void *buf, unsigned int cisz,
0425                       size_t nci)
0426 {
0427     struct nilfs_checkpoint *cp;
0428     struct nilfs_cpinfo *ci = buf;
0429     struct buffer_head *bh;
0430     size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
0431     __u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop;
0432     void *kaddr;
0433     int n, ret;
0434     int ncps, i;
0435 
0436     if (cno == 0)
0437         return -ENOENT; /* checkpoint number 0 is invalid */
0438     down_read(&NILFS_MDT(cpfile)->mi_sem);
0439 
0440     for (n = 0; n < nci; cno += ncps) {
0441         ret = nilfs_cpfile_find_checkpoint_block(
0442             cpfile, cno, cur_cno - 1, &cno, &bh);
0443         if (ret < 0) {
0444             if (likely(ret == -ENOENT))
0445                 break;
0446             goto out;
0447         }
0448         ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno);
0449 
0450         kaddr = kmap_atomic(bh->b_page);
0451         cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
0452         for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) {
0453             if (!nilfs_checkpoint_invalid(cp)) {
0454                 nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp,
0455                                   ci);
0456                 ci = (void *)ci + cisz;
0457                 n++;
0458             }
0459         }
0460         kunmap_atomic(kaddr);
0461         brelse(bh);
0462     }
0463 
0464     ret = n;
0465     if (n > 0) {
0466         ci = (void *)ci - cisz;
0467         *cnop = ci->ci_cno + 1;
0468     }
0469 
0470  out:
0471     up_read(&NILFS_MDT(cpfile)->mi_sem);
0472     return ret;
0473 }
0474 
0475 static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
0476                       void *buf, unsigned int cisz,
0477                       size_t nci)
0478 {
0479     struct buffer_head *bh;
0480     struct nilfs_cpfile_header *header;
0481     struct nilfs_checkpoint *cp;
0482     struct nilfs_cpinfo *ci = buf;
0483     __u64 curr = *cnop, next;
0484     unsigned long curr_blkoff, next_blkoff;
0485     void *kaddr;
0486     int n = 0, ret;
0487 
0488     down_read(&NILFS_MDT(cpfile)->mi_sem);
0489 
0490     if (curr == 0) {
0491         ret = nilfs_cpfile_get_header_block(cpfile, &bh);
0492         if (ret < 0)
0493             goto out;
0494         kaddr = kmap_atomic(bh->b_page);
0495         header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
0496         curr = le64_to_cpu(header->ch_snapshot_list.ssl_next);
0497         kunmap_atomic(kaddr);
0498         brelse(bh);
0499         if (curr == 0) {
0500             ret = 0;
0501             goto out;
0502         }
0503     } else if (unlikely(curr == ~(__u64)0)) {
0504         ret = 0;
0505         goto out;
0506     }
0507 
0508     curr_blkoff = nilfs_cpfile_get_blkoff(cpfile, curr);
0509     ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &bh);
0510     if (unlikely(ret < 0)) {
0511         if (ret == -ENOENT)
0512             ret = 0; /* No snapshots (started from a hole block) */
0513         goto out;
0514     }
0515     kaddr = kmap_atomic(bh->b_page);
0516     while (n < nci) {
0517         cp = nilfs_cpfile_block_get_checkpoint(cpfile, curr, bh, kaddr);
0518         curr = ~(__u64)0; /* Terminator */
0519         if (unlikely(nilfs_checkpoint_invalid(cp) ||
0520                  !nilfs_checkpoint_snapshot(cp)))
0521             break;
0522         nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, ci);
0523         ci = (void *)ci + cisz;
0524         n++;
0525         next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
0526         if (next == 0)
0527             break; /* reach end of the snapshot list */
0528 
0529         next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next);
0530         if (curr_blkoff != next_blkoff) {
0531             kunmap_atomic(kaddr);
0532             brelse(bh);
0533             ret = nilfs_cpfile_get_checkpoint_block(cpfile, next,
0534                                 0, &bh);
0535             if (unlikely(ret < 0)) {
0536                 WARN_ON(ret == -ENOENT);
0537                 goto out;
0538             }
0539             kaddr = kmap_atomic(bh->b_page);
0540         }
0541         curr = next;
0542         curr_blkoff = next_blkoff;
0543     }
0544     kunmap_atomic(kaddr);
0545     brelse(bh);
0546     *cnop = curr;
0547     ret = n;
0548 
0549  out:
0550     up_read(&NILFS_MDT(cpfile)->mi_sem);
0551     return ret;
0552 }
0553 
0554 /**
0555  * nilfs_cpfile_get_cpinfo -
0556  * @cpfile:
0557  * @cno:
0558  * @ci:
0559  * @nci:
0560  */
0561 
0562 ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,
0563                 void *buf, unsigned int cisz, size_t nci)
0564 {
0565     switch (mode) {
0566     case NILFS_CHECKPOINT:
0567         return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, buf, cisz, nci);
0568     case NILFS_SNAPSHOT:
0569         return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, buf, cisz, nci);
0570     default:
0571         return -EINVAL;
0572     }
0573 }
0574 
0575 /**
0576  * nilfs_cpfile_delete_checkpoint -
0577  * @cpfile:
0578  * @cno:
0579  */
0580 int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno)
0581 {
0582     struct nilfs_cpinfo ci;
0583     __u64 tcno = cno;
0584     ssize_t nci;
0585 
0586     nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, sizeof(ci), 1);
0587     if (nci < 0)
0588         return nci;
0589     else if (nci == 0 || ci.ci_cno != cno)
0590         return -ENOENT;
0591     else if (nilfs_cpinfo_snapshot(&ci))
0592         return -EBUSY;
0593 
0594     return nilfs_cpfile_delete_checkpoints(cpfile, cno, cno + 1);
0595 }
0596 
0597 static struct nilfs_snapshot_list *
0598 nilfs_cpfile_block_get_snapshot_list(const struct inode *cpfile,
0599                      __u64 cno,
0600                      struct buffer_head *bh,
0601                      void *kaddr)
0602 {
0603     struct nilfs_cpfile_header *header;
0604     struct nilfs_checkpoint *cp;
0605     struct nilfs_snapshot_list *list;
0606 
0607     if (cno != 0) {
0608         cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
0609         list = &cp->cp_snapshot_list;
0610     } else {
0611         header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
0612         list = &header->ch_snapshot_list;
0613     }
0614     return list;
0615 }
0616 
0617 static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno)
0618 {
0619     struct buffer_head *header_bh, *curr_bh, *prev_bh, *cp_bh;
0620     struct nilfs_cpfile_header *header;
0621     struct nilfs_checkpoint *cp;
0622     struct nilfs_snapshot_list *list;
0623     __u64 curr, prev;
0624     unsigned long curr_blkoff, prev_blkoff;
0625     void *kaddr;
0626     int ret;
0627 
0628     if (cno == 0)
0629         return -ENOENT; /* checkpoint number 0 is invalid */
0630     down_write(&NILFS_MDT(cpfile)->mi_sem);
0631 
0632     ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
0633     if (ret < 0)
0634         goto out_sem;
0635     kaddr = kmap_atomic(cp_bh->b_page);
0636     cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
0637     if (nilfs_checkpoint_invalid(cp)) {
0638         ret = -ENOENT;
0639         kunmap_atomic(kaddr);
0640         goto out_cp;
0641     }
0642     if (nilfs_checkpoint_snapshot(cp)) {
0643         ret = 0;
0644         kunmap_atomic(kaddr);
0645         goto out_cp;
0646     }
0647     kunmap_atomic(kaddr);
0648 
0649     ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
0650     if (ret < 0)
0651         goto out_cp;
0652     kaddr = kmap_atomic(header_bh->b_page);
0653     header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
0654     list = &header->ch_snapshot_list;
0655     curr_bh = header_bh;
0656     get_bh(curr_bh);
0657     curr = 0;
0658     curr_blkoff = 0;
0659     prev = le64_to_cpu(list->ssl_prev);
0660     while (prev > cno) {
0661         prev_blkoff = nilfs_cpfile_get_blkoff(cpfile, prev);
0662         curr = prev;
0663         if (curr_blkoff != prev_blkoff) {
0664             kunmap_atomic(kaddr);
0665             brelse(curr_bh);
0666             ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr,
0667                                 0, &curr_bh);
0668             if (ret < 0)
0669                 goto out_header;
0670             kaddr = kmap_atomic(curr_bh->b_page);
0671         }
0672         curr_blkoff = prev_blkoff;
0673         cp = nilfs_cpfile_block_get_checkpoint(
0674             cpfile, curr, curr_bh, kaddr);
0675         list = &cp->cp_snapshot_list;
0676         prev = le64_to_cpu(list->ssl_prev);
0677     }
0678     kunmap_atomic(kaddr);
0679 
0680     if (prev != 0) {
0681         ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,
0682                             &prev_bh);
0683         if (ret < 0)
0684             goto out_curr;
0685     } else {
0686         prev_bh = header_bh;
0687         get_bh(prev_bh);
0688     }
0689 
0690     kaddr = kmap_atomic(curr_bh->b_page);
0691     list = nilfs_cpfile_block_get_snapshot_list(
0692         cpfile, curr, curr_bh, kaddr);
0693     list->ssl_prev = cpu_to_le64(cno);
0694     kunmap_atomic(kaddr);
0695 
0696     kaddr = kmap_atomic(cp_bh->b_page);
0697     cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
0698     cp->cp_snapshot_list.ssl_next = cpu_to_le64(curr);
0699     cp->cp_snapshot_list.ssl_prev = cpu_to_le64(prev);
0700     nilfs_checkpoint_set_snapshot(cp);
0701     kunmap_atomic(kaddr);
0702 
0703     kaddr = kmap_atomic(prev_bh->b_page);
0704     list = nilfs_cpfile_block_get_snapshot_list(
0705         cpfile, prev, prev_bh, kaddr);
0706     list->ssl_next = cpu_to_le64(cno);
0707     kunmap_atomic(kaddr);
0708 
0709     kaddr = kmap_atomic(header_bh->b_page);
0710     header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
0711     le64_add_cpu(&header->ch_nsnapshots, 1);
0712     kunmap_atomic(kaddr);
0713 
0714     mark_buffer_dirty(prev_bh);
0715     mark_buffer_dirty(curr_bh);
0716     mark_buffer_dirty(cp_bh);
0717     mark_buffer_dirty(header_bh);
0718     nilfs_mdt_mark_dirty(cpfile);
0719 
0720     brelse(prev_bh);
0721 
0722  out_curr:
0723     brelse(curr_bh);
0724 
0725  out_header:
0726     brelse(header_bh);
0727 
0728  out_cp:
0729     brelse(cp_bh);
0730 
0731  out_sem:
0732     up_write(&NILFS_MDT(cpfile)->mi_sem);
0733     return ret;
0734 }
0735 
0736 static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno)
0737 {
0738     struct buffer_head *header_bh, *next_bh, *prev_bh, *cp_bh;
0739     struct nilfs_cpfile_header *header;
0740     struct nilfs_checkpoint *cp;
0741     struct nilfs_snapshot_list *list;
0742     __u64 next, prev;
0743     void *kaddr;
0744     int ret;
0745 
0746     if (cno == 0)
0747         return -ENOENT; /* checkpoint number 0 is invalid */
0748     down_write(&NILFS_MDT(cpfile)->mi_sem);
0749 
0750     ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
0751     if (ret < 0)
0752         goto out_sem;
0753     kaddr = kmap_atomic(cp_bh->b_page);
0754     cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
0755     if (nilfs_checkpoint_invalid(cp)) {
0756         ret = -ENOENT;
0757         kunmap_atomic(kaddr);
0758         goto out_cp;
0759     }
0760     if (!nilfs_checkpoint_snapshot(cp)) {
0761         ret = 0;
0762         kunmap_atomic(kaddr);
0763         goto out_cp;
0764     }
0765 
0766     list = &cp->cp_snapshot_list;
0767     next = le64_to_cpu(list->ssl_next);
0768     prev = le64_to_cpu(list->ssl_prev);
0769     kunmap_atomic(kaddr);
0770 
0771     ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
0772     if (ret < 0)
0773         goto out_cp;
0774     if (next != 0) {
0775         ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0,
0776                             &next_bh);
0777         if (ret < 0)
0778             goto out_header;
0779     } else {
0780         next_bh = header_bh;
0781         get_bh(next_bh);
0782     }
0783     if (prev != 0) {
0784         ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,
0785                             &prev_bh);
0786         if (ret < 0)
0787             goto out_next;
0788     } else {
0789         prev_bh = header_bh;
0790         get_bh(prev_bh);
0791     }
0792 
0793     kaddr = kmap_atomic(next_bh->b_page);
0794     list = nilfs_cpfile_block_get_snapshot_list(
0795         cpfile, next, next_bh, kaddr);
0796     list->ssl_prev = cpu_to_le64(prev);
0797     kunmap_atomic(kaddr);
0798 
0799     kaddr = kmap_atomic(prev_bh->b_page);
0800     list = nilfs_cpfile_block_get_snapshot_list(
0801         cpfile, prev, prev_bh, kaddr);
0802     list->ssl_next = cpu_to_le64(next);
0803     kunmap_atomic(kaddr);
0804 
0805     kaddr = kmap_atomic(cp_bh->b_page);
0806     cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
0807     cp->cp_snapshot_list.ssl_next = cpu_to_le64(0);
0808     cp->cp_snapshot_list.ssl_prev = cpu_to_le64(0);
0809     nilfs_checkpoint_clear_snapshot(cp);
0810     kunmap_atomic(kaddr);
0811 
0812     kaddr = kmap_atomic(header_bh->b_page);
0813     header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
0814     le64_add_cpu(&header->ch_nsnapshots, -1);
0815     kunmap_atomic(kaddr);
0816 
0817     mark_buffer_dirty(next_bh);
0818     mark_buffer_dirty(prev_bh);
0819     mark_buffer_dirty(cp_bh);
0820     mark_buffer_dirty(header_bh);
0821     nilfs_mdt_mark_dirty(cpfile);
0822 
0823     brelse(prev_bh);
0824 
0825  out_next:
0826     brelse(next_bh);
0827 
0828  out_header:
0829     brelse(header_bh);
0830 
0831  out_cp:
0832     brelse(cp_bh);
0833 
0834  out_sem:
0835     up_write(&NILFS_MDT(cpfile)->mi_sem);
0836     return ret;
0837 }
0838 
0839 /**
0840  * nilfs_cpfile_is_snapshot -
0841  * @cpfile: inode of checkpoint file
0842  * @cno: checkpoint number
0843  *
0844  * Description:
0845  *
0846  * Return Value: On success, 1 is returned if the checkpoint specified by
0847  * @cno is a snapshot, or 0 if not. On error, one of the following negative
0848  * error codes is returned.
0849  *
0850  * %-EIO - I/O error.
0851  *
0852  * %-ENOMEM - Insufficient amount of memory available.
0853  *
0854  * %-ENOENT - No such checkpoint.
0855  */
0856 int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno)
0857 {
0858     struct buffer_head *bh;
0859     struct nilfs_checkpoint *cp;
0860     void *kaddr;
0861     int ret;
0862 
0863     /*
0864      * CP number is invalid if it's zero or larger than the
0865      * largest existing one.
0866      */
0867     if (cno == 0 || cno >= nilfs_mdt_cno(cpfile))
0868         return -ENOENT;
0869     down_read(&NILFS_MDT(cpfile)->mi_sem);
0870 
0871     ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh);
0872     if (ret < 0)
0873         goto out;
0874     kaddr = kmap_atomic(bh->b_page);
0875     cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
0876     if (nilfs_checkpoint_invalid(cp))
0877         ret = -ENOENT;
0878     else
0879         ret = nilfs_checkpoint_snapshot(cp);
0880     kunmap_atomic(kaddr);
0881     brelse(bh);
0882 
0883  out:
0884     up_read(&NILFS_MDT(cpfile)->mi_sem);
0885     return ret;
0886 }
0887 
0888 /**
0889  * nilfs_cpfile_change_cpmode - change checkpoint mode
0890  * @cpfile: inode of checkpoint file
0891  * @cno: checkpoint number
0892  * @mode: mode of checkpoint
0893  *
0894  * Description: nilfs_change_cpmode() changes the mode of the checkpoint
0895  * specified by @cno. The mode @mode is NILFS_CHECKPOINT or NILFS_SNAPSHOT.
0896  *
0897  * Return Value: On success, 0 is returned. On error, one of the following
0898  * negative error codes is returned.
0899  *
0900  * %-EIO - I/O error.
0901  *
0902  * %-ENOMEM - Insufficient amount of memory available.
0903  *
0904  * %-ENOENT - No such checkpoint.
0905  */
0906 int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)
0907 {
0908     int ret;
0909 
0910     switch (mode) {
0911     case NILFS_CHECKPOINT:
0912         if (nilfs_checkpoint_is_mounted(cpfile->i_sb, cno))
0913             /*
0914              * Current implementation does not have to protect
0915              * plain read-only mounts since they are exclusive
0916              * with a read/write mount and are protected from the
0917              * cleaner.
0918              */
0919             ret = -EBUSY;
0920         else
0921             ret = nilfs_cpfile_clear_snapshot(cpfile, cno);
0922         return ret;
0923     case NILFS_SNAPSHOT:
0924         return nilfs_cpfile_set_snapshot(cpfile, cno);
0925     default:
0926         return -EINVAL;
0927     }
0928 }
0929 
0930 /**
0931  * nilfs_cpfile_get_stat - get checkpoint statistics
0932  * @cpfile: inode of checkpoint file
0933  * @cpstat: pointer to a structure of checkpoint statistics
0934  *
0935  * Description: nilfs_cpfile_get_stat() returns information about checkpoints.
0936  *
0937  * Return Value: On success, 0 is returned, and checkpoints information is
0938  * stored in the place pointed by @cpstat. On error, one of the following
0939  * negative error codes is returned.
0940  *
0941  * %-EIO - I/O error.
0942  *
0943  * %-ENOMEM - Insufficient amount of memory available.
0944  */
0945 int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat)
0946 {
0947     struct buffer_head *bh;
0948     struct nilfs_cpfile_header *header;
0949     void *kaddr;
0950     int ret;
0951 
0952     down_read(&NILFS_MDT(cpfile)->mi_sem);
0953 
0954     ret = nilfs_cpfile_get_header_block(cpfile, &bh);
0955     if (ret < 0)
0956         goto out_sem;
0957     kaddr = kmap_atomic(bh->b_page);
0958     header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
0959     cpstat->cs_cno = nilfs_mdt_cno(cpfile);
0960     cpstat->cs_ncps = le64_to_cpu(header->ch_ncheckpoints);
0961     cpstat->cs_nsss = le64_to_cpu(header->ch_nsnapshots);
0962     kunmap_atomic(kaddr);
0963     brelse(bh);
0964 
0965  out_sem:
0966     up_read(&NILFS_MDT(cpfile)->mi_sem);
0967     return ret;
0968 }
0969 
0970 /**
0971  * nilfs_cpfile_read - read or get cpfile inode
0972  * @sb: super block instance
0973  * @cpsize: size of a checkpoint entry
0974  * @raw_inode: on-disk cpfile inode
0975  * @inodep: buffer to store the inode
0976  */
0977 int nilfs_cpfile_read(struct super_block *sb, size_t cpsize,
0978               struct nilfs_inode *raw_inode, struct inode **inodep)
0979 {
0980     struct inode *cpfile;
0981     int err;
0982 
0983     if (cpsize > sb->s_blocksize) {
0984         nilfs_err(sb, "too large checkpoint size: %zu bytes", cpsize);
0985         return -EINVAL;
0986     } else if (cpsize < NILFS_MIN_CHECKPOINT_SIZE) {
0987         nilfs_err(sb, "too small checkpoint size: %zu bytes", cpsize);
0988         return -EINVAL;
0989     }
0990 
0991     cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO);
0992     if (unlikely(!cpfile))
0993         return -ENOMEM;
0994     if (!(cpfile->i_state & I_NEW))
0995         goto out;
0996 
0997     err = nilfs_mdt_init(cpfile, NILFS_MDT_GFP, 0);
0998     if (err)
0999         goto failed;
1000 
1001     nilfs_mdt_set_entry_size(cpfile, cpsize,
1002                  sizeof(struct nilfs_cpfile_header));
1003 
1004     err = nilfs_read_inode_common(cpfile, raw_inode);
1005     if (err)
1006         goto failed;
1007 
1008     unlock_new_inode(cpfile);
1009  out:
1010     *inodep = cpfile;
1011     return 0;
1012  failed:
1013     iget_failed(cpfile);
1014     return err;
1015 }