1149 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * NILFS checkpoint file.4 *5 * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.6 *7 * Written by Koji Sato.8 */9 10#include <linux/kernel.h>11#include <linux/fs.h>12#include <linux/string.h>13#include <linux/buffer_head.h>14#include <linux/errno.h>15#include "mdt.h"16#include "cpfile.h"17 18 19static inline unsigned long20nilfs_cpfile_checkpoints_per_block(const struct inode *cpfile)21{22 return NILFS_MDT(cpfile)->mi_entries_per_block;23}24 25/* block number from the beginning of the file */26static unsigned long27nilfs_cpfile_get_blkoff(const struct inode *cpfile, __u64 cno)28{29 __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;30 31 tcno = div64_ul(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));32 return (unsigned long)tcno;33}34 35/* offset in block */36static unsigned long37nilfs_cpfile_get_offset(const struct inode *cpfile, __u64 cno)38{39 __u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;40 41 return do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));42}43 44static __u64 nilfs_cpfile_first_checkpoint_in_block(const struct inode *cpfile,45 unsigned long blkoff)46{47 return (__u64)nilfs_cpfile_checkpoints_per_block(cpfile) * blkoff48 + 1 - NILFS_MDT(cpfile)->mi_first_entry_offset;49}50 51static unsigned long52nilfs_cpfile_checkpoints_in_block(const struct inode *cpfile,53 __u64 curr,54 __u64 max)55{56 return min_t(__u64,57 nilfs_cpfile_checkpoints_per_block(cpfile) -58 nilfs_cpfile_get_offset(cpfile, curr),59 max - curr);60}61 62static inline int nilfs_cpfile_is_in_first(const struct inode *cpfile,63 __u64 cno)64{65 return nilfs_cpfile_get_blkoff(cpfile, cno) == 0;66}67 68static unsigned int69nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile,70 struct buffer_head *bh,71 void *kaddr,72 unsigned int n)73{74 struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);75 unsigned int count;76 77 count = le32_to_cpu(cp->cp_checkpoints_count) + n;78 cp->cp_checkpoints_count = cpu_to_le32(count);79 return count;80}81 82static unsigned int83nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile,84 struct buffer_head *bh,85 void *kaddr,86 unsigned int n)87{88 struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);89 unsigned int count;90 91 WARN_ON(le32_to_cpu(cp->cp_checkpoints_count) < n);92 count = le32_to_cpu(cp->cp_checkpoints_count) - n;93 cp->cp_checkpoints_count = cpu_to_le32(count);94 return count;95}96 97static inline struct nilfs_cpfile_header *98nilfs_cpfile_block_get_header(const struct inode *cpfile,99 struct buffer_head *bh,100 void *kaddr)101{102 return kaddr + bh_offset(bh);103}104 105static struct nilfs_checkpoint *106nilfs_cpfile_block_get_checkpoint(const struct inode *cpfile, __u64 cno,107 struct buffer_head *bh,108 void *kaddr)109{110 return kaddr + bh_offset(bh) + nilfs_cpfile_get_offset(cpfile, cno) *111 NILFS_MDT(cpfile)->mi_entry_size;112}113 114static void nilfs_cpfile_block_init(struct inode *cpfile,115 struct buffer_head *bh,116 void *kaddr)117{118 struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);119 size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;120 int n = nilfs_cpfile_checkpoints_per_block(cpfile);121 122 while (n-- > 0) {123 nilfs_checkpoint_set_invalid(cp);124 cp = (void *)cp + cpsz;125 }126}127 128static int nilfs_cpfile_get_header_block(struct inode *cpfile,129 struct buffer_head **bhp)130{131 int err = nilfs_mdt_get_block(cpfile, 0, 0, NULL, bhp);132 133 if (unlikely(err == -ENOENT)) {134 nilfs_error(cpfile->i_sb,135 "missing header block in checkpoint metadata");136 err = -EIO;137 }138 return err;139}140 141static inline int nilfs_cpfile_get_checkpoint_block(struct inode *cpfile,142 __u64 cno,143 int create,144 struct buffer_head **bhp)145{146 return nilfs_mdt_get_block(cpfile,147 nilfs_cpfile_get_blkoff(cpfile, cno),148 create, nilfs_cpfile_block_init, bhp);149}150 151/**152 * nilfs_cpfile_find_checkpoint_block - find and get a buffer on cpfile153 * @cpfile: inode of cpfile154 * @start_cno: start checkpoint number (inclusive)155 * @end_cno: end checkpoint number (inclusive)156 * @cnop: place to store the next checkpoint number157 * @bhp: place to store a pointer to buffer_head struct158 *159 * Return Value: On success, it returns 0. On error, the following negative160 * error code is returned.161 *162 * %-ENOMEM - Insufficient memory available.163 *164 * %-EIO - I/O error165 *166 * %-ENOENT - no block exists in the range.167 */168static int nilfs_cpfile_find_checkpoint_block(struct inode *cpfile,169 __u64 start_cno, __u64 end_cno,170 __u64 *cnop,171 struct buffer_head **bhp)172{173 unsigned long start, end, blkoff;174 int ret;175 176 if (unlikely(start_cno > end_cno))177 return -ENOENT;178 179 start = nilfs_cpfile_get_blkoff(cpfile, start_cno);180 end = nilfs_cpfile_get_blkoff(cpfile, end_cno);181 182 ret = nilfs_mdt_find_block(cpfile, start, end, &blkoff, bhp);183 if (!ret)184 *cnop = (blkoff == start) ? start_cno :185 nilfs_cpfile_first_checkpoint_in_block(cpfile, blkoff);186 return ret;187}188 189static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile,190 __u64 cno)191{192 return nilfs_mdt_delete_block(cpfile,193 nilfs_cpfile_get_blkoff(cpfile, cno));194}195 196/**197 * nilfs_cpfile_read_checkpoint - read a checkpoint entry in cpfile198 * @cpfile: checkpoint file inode199 * @cno: number of checkpoint entry to read200 * @root: nilfs root object201 * @ifile: ifile's inode to read and attach to @root202 *203 * This function imports checkpoint information from the checkpoint file and204 * stores it to the inode file given by @ifile and the nilfs root object205 * given by @root.206 *207 * Return: 0 on success, or the following negative error code on failure.208 * * %-EINVAL - Invalid checkpoint.209 * * %-ENOMEM - Insufficient memory available.210 * * %-EIO - I/O error (including metadata corruption).211 */212int nilfs_cpfile_read_checkpoint(struct inode *cpfile, __u64 cno,213 struct nilfs_root *root, struct inode *ifile)214{215 struct buffer_head *cp_bh;216 struct nilfs_checkpoint *cp;217 void *kaddr;218 int ret;219 220 if (cno < 1 || cno > nilfs_mdt_cno(cpfile))221 return -EINVAL;222 223 down_read(&NILFS_MDT(cpfile)->mi_sem);224 ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);225 if (unlikely(ret < 0)) {226 if (ret == -ENOENT)227 ret = -EINVAL;228 goto out_sem;229 }230 231 kaddr = kmap_local_page(cp_bh->b_page);232 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);233 if (nilfs_checkpoint_invalid(cp)) {234 ret = -EINVAL;235 goto put_cp;236 }237 238 ret = nilfs_read_inode_common(ifile, &cp->cp_ifile_inode);239 if (unlikely(ret)) {240 /*241 * Since this inode is on a checkpoint entry, treat errors242 * as metadata corruption.243 */244 nilfs_err(cpfile->i_sb,245 "ifile inode (checkpoint number=%llu) corrupted",246 (unsigned long long)cno);247 ret = -EIO;248 goto put_cp;249 }250 251 /* Configure the nilfs root object */252 atomic64_set(&root->inodes_count, le64_to_cpu(cp->cp_inodes_count));253 atomic64_set(&root->blocks_count, le64_to_cpu(cp->cp_blocks_count));254 root->ifile = ifile;255 256put_cp:257 kunmap_local(kaddr);258 brelse(cp_bh);259out_sem:260 up_read(&NILFS_MDT(cpfile)->mi_sem);261 return ret;262}263 264/**265 * nilfs_cpfile_create_checkpoint - create a checkpoint entry on cpfile266 * @cpfile: checkpoint file inode267 * @cno: number of checkpoint to set up268 *269 * This function creates a checkpoint with the number specified by @cno on270 * cpfile. If the specified checkpoint entry already exists due to a past271 * failure, it will be reused without returning an error.272 * In either case, the buffer of the block containing the checkpoint entry273 * and the cpfile inode are made dirty for inclusion in the write log.274 *275 * Return: 0 on success, or the following negative error code on failure.276 * * %-ENOMEM - Insufficient memory available.277 * * %-EIO - I/O error (including metadata corruption).278 * * %-EROFS - Read only filesystem279 */280int nilfs_cpfile_create_checkpoint(struct inode *cpfile, __u64 cno)281{282 struct buffer_head *header_bh, *cp_bh;283 struct nilfs_cpfile_header *header;284 struct nilfs_checkpoint *cp;285 void *kaddr;286 int ret;287 288 if (WARN_ON_ONCE(cno < 1))289 return -EIO;290 291 down_write(&NILFS_MDT(cpfile)->mi_sem);292 ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);293 if (unlikely(ret < 0))294 goto out_sem;295 296 ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 1, &cp_bh);297 if (unlikely(ret < 0))298 goto out_header;299 300 kaddr = kmap_local_page(cp_bh->b_page);301 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);302 if (nilfs_checkpoint_invalid(cp)) {303 /* a newly-created checkpoint */304 nilfs_checkpoint_clear_invalid(cp);305 if (!nilfs_cpfile_is_in_first(cpfile, cno))306 nilfs_cpfile_block_add_valid_checkpoints(cpfile, cp_bh,307 kaddr, 1);308 kunmap_local(kaddr);309 310 kaddr = kmap_local_page(header_bh->b_page);311 header = nilfs_cpfile_block_get_header(cpfile, header_bh,312 kaddr);313 le64_add_cpu(&header->ch_ncheckpoints, 1);314 kunmap_local(kaddr);315 mark_buffer_dirty(header_bh);316 } else {317 kunmap_local(kaddr);318 }319 320 /* Force the buffer and the inode to become dirty */321 mark_buffer_dirty(cp_bh);322 brelse(cp_bh);323 nilfs_mdt_mark_dirty(cpfile);324 325out_header:326 brelse(header_bh);327 328out_sem:329 up_write(&NILFS_MDT(cpfile)->mi_sem);330 return ret;331}332 333/**334 * nilfs_cpfile_finalize_checkpoint - fill in a checkpoint entry in cpfile335 * @cpfile: checkpoint file inode336 * @cno: checkpoint number337 * @root: nilfs root object338 * @blkinc: number of blocks added by this checkpoint339 * @ctime: checkpoint creation time340 * @minor: minor checkpoint flag341 *342 * This function completes the checkpoint entry numbered by @cno in the343 * cpfile with the data given by the arguments @root, @blkinc, @ctime, and344 * @minor.345 *346 * Return: 0 on success, or the following negative error code on failure.347 * * %-ENOMEM - Insufficient memory available.348 * * %-EIO - I/O error (including metadata corruption).349 */350int nilfs_cpfile_finalize_checkpoint(struct inode *cpfile, __u64 cno,351 struct nilfs_root *root, __u64 blkinc,352 time64_t ctime, bool minor)353{354 struct buffer_head *cp_bh;355 struct nilfs_checkpoint *cp;356 void *kaddr;357 int ret;358 359 if (WARN_ON_ONCE(cno < 1))360 return -EIO;361 362 down_write(&NILFS_MDT(cpfile)->mi_sem);363 ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);364 if (unlikely(ret < 0)) {365 if (ret == -ENOENT)366 goto error;367 goto out_sem;368 }369 370 kaddr = kmap_local_page(cp_bh->b_page);371 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);372 if (unlikely(nilfs_checkpoint_invalid(cp))) {373 kunmap_local(kaddr);374 brelse(cp_bh);375 goto error;376 }377 378 cp->cp_snapshot_list.ssl_next = 0;379 cp->cp_snapshot_list.ssl_prev = 0;380 cp->cp_inodes_count = cpu_to_le64(atomic64_read(&root->inodes_count));381 cp->cp_blocks_count = cpu_to_le64(atomic64_read(&root->blocks_count));382 cp->cp_nblk_inc = cpu_to_le64(blkinc);383 cp->cp_create = cpu_to_le64(ctime);384 cp->cp_cno = cpu_to_le64(cno);385 386 if (minor)387 nilfs_checkpoint_set_minor(cp);388 else389 nilfs_checkpoint_clear_minor(cp);390 391 nilfs_write_inode_common(root->ifile, &cp->cp_ifile_inode);392 nilfs_bmap_write(NILFS_I(root->ifile)->i_bmap, &cp->cp_ifile_inode);393 394 kunmap_local(kaddr);395 brelse(cp_bh);396out_sem:397 up_write(&NILFS_MDT(cpfile)->mi_sem);398 return ret;399 400error:401 nilfs_error(cpfile->i_sb,402 "checkpoint finalization failed due to metadata corruption.");403 ret = -EIO;404 goto out_sem;405}406 407/**408 * nilfs_cpfile_delete_checkpoints - delete checkpoints409 * @cpfile: inode of checkpoint file410 * @start: start checkpoint number411 * @end: end checkpoint number412 *413 * Description: nilfs_cpfile_delete_checkpoints() deletes the checkpoints in414 * the period from @start to @end, excluding @end itself. The checkpoints415 * which have been already deleted are ignored.416 *417 * Return Value: On success, 0 is returned. On error, one of the following418 * negative error codes is returned.419 *420 * %-EIO - I/O error.421 *422 * %-ENOMEM - Insufficient amount of memory available.423 *424 * %-EINVAL - invalid checkpoints.425 */426int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,427 __u64 start,428 __u64 end)429{430 struct buffer_head *header_bh, *cp_bh;431 struct nilfs_cpfile_header *header;432 struct nilfs_checkpoint *cp;433 size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;434 __u64 cno;435 void *kaddr;436 unsigned long tnicps;437 int ret, ncps, nicps, nss, count, i;438 439 if (unlikely(start == 0 || start > end)) {440 nilfs_err(cpfile->i_sb,441 "cannot delete checkpoints: invalid range [%llu, %llu)",442 (unsigned long long)start, (unsigned long long)end);443 return -EINVAL;444 }445 446 down_write(&NILFS_MDT(cpfile)->mi_sem);447 448 ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);449 if (ret < 0)450 goto out_sem;451 tnicps = 0;452 nss = 0;453 454 for (cno = start; cno < end; cno += ncps) {455 ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end);456 ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);457 if (ret < 0) {458 if (ret != -ENOENT)459 break;460 /* skip hole */461 ret = 0;462 continue;463 }464 465 kaddr = kmap_local_page(cp_bh->b_page);466 cp = nilfs_cpfile_block_get_checkpoint(467 cpfile, cno, cp_bh, kaddr);468 nicps = 0;469 for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {470 if (nilfs_checkpoint_snapshot(cp)) {471 nss++;472 } else if (!nilfs_checkpoint_invalid(cp)) {473 nilfs_checkpoint_set_invalid(cp);474 nicps++;475 }476 }477 if (nicps > 0) {478 tnicps += nicps;479 mark_buffer_dirty(cp_bh);480 nilfs_mdt_mark_dirty(cpfile);481 if (!nilfs_cpfile_is_in_first(cpfile, cno)) {482 count =483 nilfs_cpfile_block_sub_valid_checkpoints(484 cpfile, cp_bh, kaddr, nicps);485 if (count == 0) {486 /* make hole */487 kunmap_local(kaddr);488 brelse(cp_bh);489 ret =490 nilfs_cpfile_delete_checkpoint_block(491 cpfile, cno);492 if (ret == 0)493 continue;494 nilfs_err(cpfile->i_sb,495 "error %d deleting checkpoint block",496 ret);497 break;498 }499 }500 }501 502 kunmap_local(kaddr);503 brelse(cp_bh);504 }505 506 if (tnicps > 0) {507 kaddr = kmap_local_page(header_bh->b_page);508 header = nilfs_cpfile_block_get_header(cpfile, header_bh,509 kaddr);510 le64_add_cpu(&header->ch_ncheckpoints, -(u64)tnicps);511 mark_buffer_dirty(header_bh);512 nilfs_mdt_mark_dirty(cpfile);513 kunmap_local(kaddr);514 }515 516 brelse(header_bh);517 if (nss > 0)518 ret = -EBUSY;519 520 out_sem:521 up_write(&NILFS_MDT(cpfile)->mi_sem);522 return ret;523}524 525static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile,526 struct nilfs_checkpoint *cp,527 struct nilfs_cpinfo *ci)528{529 ci->ci_flags = le32_to_cpu(cp->cp_flags);530 ci->ci_cno = le64_to_cpu(cp->cp_cno);531 ci->ci_create = le64_to_cpu(cp->cp_create);532 ci->ci_nblk_inc = le64_to_cpu(cp->cp_nblk_inc);533 ci->ci_inodes_count = le64_to_cpu(cp->cp_inodes_count);534 ci->ci_blocks_count = le64_to_cpu(cp->cp_blocks_count);535 ci->ci_next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);536}537 538static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,539 void *buf, unsigned int cisz,540 size_t nci)541{542 struct nilfs_checkpoint *cp;543 struct nilfs_cpinfo *ci = buf;544 struct buffer_head *bh;545 size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;546 __u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop;547 void *kaddr;548 int n, ret;549 int ncps, i;550 551 if (cno == 0)552 return -ENOENT; /* checkpoint number 0 is invalid */553 down_read(&NILFS_MDT(cpfile)->mi_sem);554 555 for (n = 0; n < nci; cno += ncps) {556 ret = nilfs_cpfile_find_checkpoint_block(557 cpfile, cno, cur_cno - 1, &cno, &bh);558 if (ret < 0) {559 if (likely(ret == -ENOENT))560 break;561 goto out;562 }563 ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno);564 565 kaddr = kmap_local_page(bh->b_page);566 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);567 for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) {568 if (!nilfs_checkpoint_invalid(cp)) {569 nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp,570 ci);571 ci = (void *)ci + cisz;572 n++;573 }574 }575 kunmap_local(kaddr);576 brelse(bh);577 }578 579 ret = n;580 if (n > 0) {581 ci = (void *)ci - cisz;582 *cnop = ci->ci_cno + 1;583 }584 585 out:586 up_read(&NILFS_MDT(cpfile)->mi_sem);587 return ret;588}589 590static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,591 void *buf, unsigned int cisz,592 size_t nci)593{594 struct buffer_head *bh;595 struct nilfs_cpfile_header *header;596 struct nilfs_checkpoint *cp;597 struct nilfs_cpinfo *ci = buf;598 __u64 curr = *cnop, next;599 unsigned long curr_blkoff, next_blkoff;600 void *kaddr;601 int n = 0, ret;602 603 down_read(&NILFS_MDT(cpfile)->mi_sem);604 605 if (curr == 0) {606 ret = nilfs_cpfile_get_header_block(cpfile, &bh);607 if (ret < 0)608 goto out;609 kaddr = kmap_local_page(bh->b_page);610 header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);611 curr = le64_to_cpu(header->ch_snapshot_list.ssl_next);612 kunmap_local(kaddr);613 brelse(bh);614 if (curr == 0) {615 ret = 0;616 goto out;617 }618 } else if (unlikely(curr == ~(__u64)0)) {619 ret = 0;620 goto out;621 }622 623 curr_blkoff = nilfs_cpfile_get_blkoff(cpfile, curr);624 ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &bh);625 if (unlikely(ret < 0)) {626 if (ret == -ENOENT)627 ret = 0; /* No snapshots (started from a hole block) */628 goto out;629 }630 kaddr = kmap_local_page(bh->b_page);631 while (n < nci) {632 cp = nilfs_cpfile_block_get_checkpoint(cpfile, curr, bh, kaddr);633 curr = ~(__u64)0; /* Terminator */634 if (unlikely(nilfs_checkpoint_invalid(cp) ||635 !nilfs_checkpoint_snapshot(cp)))636 break;637 nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, ci);638 ci = (void *)ci + cisz;639 n++;640 next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);641 if (next == 0)642 break; /* reach end of the snapshot list */643 644 next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next);645 if (curr_blkoff != next_blkoff) {646 kunmap_local(kaddr);647 brelse(bh);648 ret = nilfs_cpfile_get_checkpoint_block(cpfile, next,649 0, &bh);650 if (unlikely(ret < 0)) {651 WARN_ON(ret == -ENOENT);652 goto out;653 }654 kaddr = kmap_local_page(bh->b_page);655 }656 curr = next;657 curr_blkoff = next_blkoff;658 }659 kunmap_local(kaddr);660 brelse(bh);661 *cnop = curr;662 ret = n;663 664 out:665 up_read(&NILFS_MDT(cpfile)->mi_sem);666 return ret;667}668 669/**670 * nilfs_cpfile_get_cpinfo - get information on checkpoints671 * @cpfile: checkpoint file inode672 * @cnop: place to pass a starting checkpoint number and receive a673 * checkpoint number to continue the search674 * @mode: mode of checkpoints that the caller wants to retrieve675 * @buf: buffer for storing checkpoints' information676 * @cisz: byte size of one checkpoint info item in array677 * @nci: number of checkpoint info items to retrieve678 *679 * nilfs_cpfile_get_cpinfo() searches for checkpoints in @mode state680 * starting from the checkpoint number stored in @cnop, and stores681 * information about found checkpoints in @buf.682 * The buffer pointed to by @buf must be large enough to store information683 * for @nci checkpoints. If at least one checkpoint information is684 * successfully retrieved, @cnop is updated to point to the checkpoint685 * number to continue searching.686 *687 * Return: Count of checkpoint info items stored in the output buffer on688 * success, or the following negative error code on failure.689 * * %-EINVAL - Invalid checkpoint mode.690 * * %-ENOMEM - Insufficient memory available.691 * * %-EIO - I/O error (including metadata corruption).692 * * %-ENOENT - Invalid checkpoint number specified.693 */694 695ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,696 void *buf, unsigned int cisz, size_t nci)697{698 switch (mode) {699 case NILFS_CHECKPOINT:700 return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, buf, cisz, nci);701 case NILFS_SNAPSHOT:702 return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, buf, cisz, nci);703 default:704 return -EINVAL;705 }706}707 708/**709 * nilfs_cpfile_delete_checkpoint - delete a checkpoint710 * @cpfile: checkpoint file inode711 * @cno: checkpoint number to delete712 *713 * Return: 0 on success, or the following negative error code on failure.714 * * %-EBUSY - Checkpoint in use (snapshot specified).715 * * %-EIO - I/O error (including metadata corruption).716 * * %-ENOENT - No valid checkpoint found.717 * * %-ENOMEM - Insufficient memory available.718 */719int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno)720{721 struct nilfs_cpinfo ci;722 __u64 tcno = cno;723 ssize_t nci;724 725 nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, sizeof(ci), 1);726 if (nci < 0)727 return nci;728 else if (nci == 0 || ci.ci_cno != cno)729 return -ENOENT;730 else if (nilfs_cpinfo_snapshot(&ci))731 return -EBUSY;732 733 return nilfs_cpfile_delete_checkpoints(cpfile, cno, cno + 1);734}735 736static struct nilfs_snapshot_list *737nilfs_cpfile_block_get_snapshot_list(const struct inode *cpfile,738 __u64 cno,739 struct buffer_head *bh,740 void *kaddr)741{742 struct nilfs_cpfile_header *header;743 struct nilfs_checkpoint *cp;744 struct nilfs_snapshot_list *list;745 746 if (cno != 0) {747 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);748 list = &cp->cp_snapshot_list;749 } else {750 header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);751 list = &header->ch_snapshot_list;752 }753 return list;754}755 756static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno)757{758 struct buffer_head *header_bh, *curr_bh, *prev_bh, *cp_bh;759 struct nilfs_cpfile_header *header;760 struct nilfs_checkpoint *cp;761 struct nilfs_snapshot_list *list;762 __u64 curr, prev;763 unsigned long curr_blkoff, prev_blkoff;764 void *kaddr;765 int ret;766 767 if (cno == 0)768 return -ENOENT; /* checkpoint number 0 is invalid */769 down_write(&NILFS_MDT(cpfile)->mi_sem);770 771 ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);772 if (ret < 0)773 goto out_sem;774 kaddr = kmap_local_page(cp_bh->b_page);775 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);776 if (nilfs_checkpoint_invalid(cp)) {777 ret = -ENOENT;778 kunmap_local(kaddr);779 goto out_cp;780 }781 if (nilfs_checkpoint_snapshot(cp)) {782 ret = 0;783 kunmap_local(kaddr);784 goto out_cp;785 }786 kunmap_local(kaddr);787 788 ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);789 if (ret < 0)790 goto out_cp;791 kaddr = kmap_local_page(header_bh->b_page);792 header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);793 list = &header->ch_snapshot_list;794 curr_bh = header_bh;795 get_bh(curr_bh);796 curr = 0;797 curr_blkoff = 0;798 prev = le64_to_cpu(list->ssl_prev);799 while (prev > cno) {800 prev_blkoff = nilfs_cpfile_get_blkoff(cpfile, prev);801 curr = prev;802 if (curr_blkoff != prev_blkoff) {803 kunmap_local(kaddr);804 brelse(curr_bh);805 ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr,806 0, &curr_bh);807 if (ret < 0)808 goto out_header;809 kaddr = kmap_local_page(curr_bh->b_page);810 }811 curr_blkoff = prev_blkoff;812 cp = nilfs_cpfile_block_get_checkpoint(813 cpfile, curr, curr_bh, kaddr);814 list = &cp->cp_snapshot_list;815 prev = le64_to_cpu(list->ssl_prev);816 }817 kunmap_local(kaddr);818 819 if (prev != 0) {820 ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,821 &prev_bh);822 if (ret < 0)823 goto out_curr;824 } else {825 prev_bh = header_bh;826 get_bh(prev_bh);827 }828 829 kaddr = kmap_local_page(curr_bh->b_page);830 list = nilfs_cpfile_block_get_snapshot_list(831 cpfile, curr, curr_bh, kaddr);832 list->ssl_prev = cpu_to_le64(cno);833 kunmap_local(kaddr);834 835 kaddr = kmap_local_page(cp_bh->b_page);836 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);837 cp->cp_snapshot_list.ssl_next = cpu_to_le64(curr);838 cp->cp_snapshot_list.ssl_prev = cpu_to_le64(prev);839 nilfs_checkpoint_set_snapshot(cp);840 kunmap_local(kaddr);841 842 kaddr = kmap_local_page(prev_bh->b_page);843 list = nilfs_cpfile_block_get_snapshot_list(844 cpfile, prev, prev_bh, kaddr);845 list->ssl_next = cpu_to_le64(cno);846 kunmap_local(kaddr);847 848 kaddr = kmap_local_page(header_bh->b_page);849 header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);850 le64_add_cpu(&header->ch_nsnapshots, 1);851 kunmap_local(kaddr);852 853 mark_buffer_dirty(prev_bh);854 mark_buffer_dirty(curr_bh);855 mark_buffer_dirty(cp_bh);856 mark_buffer_dirty(header_bh);857 nilfs_mdt_mark_dirty(cpfile);858 859 brelse(prev_bh);860 861 out_curr:862 brelse(curr_bh);863 864 out_header:865 brelse(header_bh);866 867 out_cp:868 brelse(cp_bh);869 870 out_sem:871 up_write(&NILFS_MDT(cpfile)->mi_sem);872 return ret;873}874 875static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno)876{877 struct buffer_head *header_bh, *next_bh, *prev_bh, *cp_bh;878 struct nilfs_cpfile_header *header;879 struct nilfs_checkpoint *cp;880 struct nilfs_snapshot_list *list;881 __u64 next, prev;882 void *kaddr;883 int ret;884 885 if (cno == 0)886 return -ENOENT; /* checkpoint number 0 is invalid */887 down_write(&NILFS_MDT(cpfile)->mi_sem);888 889 ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);890 if (ret < 0)891 goto out_sem;892 kaddr = kmap_local_page(cp_bh->b_page);893 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);894 if (nilfs_checkpoint_invalid(cp)) {895 ret = -ENOENT;896 kunmap_local(kaddr);897 goto out_cp;898 }899 if (!nilfs_checkpoint_snapshot(cp)) {900 ret = 0;901 kunmap_local(kaddr);902 goto out_cp;903 }904 905 list = &cp->cp_snapshot_list;906 next = le64_to_cpu(list->ssl_next);907 prev = le64_to_cpu(list->ssl_prev);908 kunmap_local(kaddr);909 910 ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);911 if (ret < 0)912 goto out_cp;913 if (next != 0) {914 ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0,915 &next_bh);916 if (ret < 0)917 goto out_header;918 } else {919 next_bh = header_bh;920 get_bh(next_bh);921 }922 if (prev != 0) {923 ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,924 &prev_bh);925 if (ret < 0)926 goto out_next;927 } else {928 prev_bh = header_bh;929 get_bh(prev_bh);930 }931 932 kaddr = kmap_local_page(next_bh->b_page);933 list = nilfs_cpfile_block_get_snapshot_list(934 cpfile, next, next_bh, kaddr);935 list->ssl_prev = cpu_to_le64(prev);936 kunmap_local(kaddr);937 938 kaddr = kmap_local_page(prev_bh->b_page);939 list = nilfs_cpfile_block_get_snapshot_list(940 cpfile, prev, prev_bh, kaddr);941 list->ssl_next = cpu_to_le64(next);942 kunmap_local(kaddr);943 944 kaddr = kmap_local_page(cp_bh->b_page);945 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);946 cp->cp_snapshot_list.ssl_next = cpu_to_le64(0);947 cp->cp_snapshot_list.ssl_prev = cpu_to_le64(0);948 nilfs_checkpoint_clear_snapshot(cp);949 kunmap_local(kaddr);950 951 kaddr = kmap_local_page(header_bh->b_page);952 header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);953 le64_add_cpu(&header->ch_nsnapshots, -1);954 kunmap_local(kaddr);955 956 mark_buffer_dirty(next_bh);957 mark_buffer_dirty(prev_bh);958 mark_buffer_dirty(cp_bh);959 mark_buffer_dirty(header_bh);960 nilfs_mdt_mark_dirty(cpfile);961 962 brelse(prev_bh);963 964 out_next:965 brelse(next_bh);966 967 out_header:968 brelse(header_bh);969 970 out_cp:971 brelse(cp_bh);972 973 out_sem:974 up_write(&NILFS_MDT(cpfile)->mi_sem);975 return ret;976}977 978/**979 * nilfs_cpfile_is_snapshot - determine if checkpoint is a snapshot980 * @cpfile: inode of checkpoint file981 * @cno: checkpoint number982 *983 * Return: 1 if the checkpoint specified by @cno is a snapshot, 0 if not, or984 * the following negative error code on failure.985 * * %-EIO - I/O error (including metadata corruption).986 * * %-ENOENT - No such checkpoint.987 * * %-ENOMEM - Insufficient memory available.988 */989int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno)990{991 struct buffer_head *bh;992 struct nilfs_checkpoint *cp;993 void *kaddr;994 int ret;995 996 /*997 * CP number is invalid if it's zero or larger than the998 * largest existing one.999 */1000 if (cno == 0 || cno >= nilfs_mdt_cno(cpfile))1001 return -ENOENT;1002 down_read(&NILFS_MDT(cpfile)->mi_sem);1003 1004 ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh);1005 if (ret < 0)1006 goto out;1007 kaddr = kmap_local_page(bh->b_page);1008 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);1009 if (nilfs_checkpoint_invalid(cp))1010 ret = -ENOENT;1011 else1012 ret = nilfs_checkpoint_snapshot(cp);1013 kunmap_local(kaddr);1014 brelse(bh);1015 1016 out:1017 up_read(&NILFS_MDT(cpfile)->mi_sem);1018 return ret;1019}1020 1021/**1022 * nilfs_cpfile_change_cpmode - change checkpoint mode1023 * @cpfile: inode of checkpoint file1024 * @cno: checkpoint number1025 * @mode: mode of checkpoint1026 *1027 * Description: nilfs_change_cpmode() changes the mode of the checkpoint1028 * specified by @cno. The mode @mode is NILFS_CHECKPOINT or NILFS_SNAPSHOT.1029 *1030 * Return Value: On success, 0 is returned. On error, one of the following1031 * negative error codes is returned.1032 *1033 * %-EIO - I/O error.1034 *1035 * %-ENOMEM - Insufficient amount of memory available.1036 *1037 * %-ENOENT - No such checkpoint.1038 */1039int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)1040{1041 int ret;1042 1043 switch (mode) {1044 case NILFS_CHECKPOINT:1045 if (nilfs_checkpoint_is_mounted(cpfile->i_sb, cno))1046 /*1047 * Current implementation does not have to protect1048 * plain read-only mounts since they are exclusive1049 * with a read/write mount and are protected from the1050 * cleaner.1051 */1052 ret = -EBUSY;1053 else1054 ret = nilfs_cpfile_clear_snapshot(cpfile, cno);1055 return ret;1056 case NILFS_SNAPSHOT:1057 return nilfs_cpfile_set_snapshot(cpfile, cno);1058 default:1059 return -EINVAL;1060 }1061}1062 1063/**1064 * nilfs_cpfile_get_stat - get checkpoint statistics1065 * @cpfile: inode of checkpoint file1066 * @cpstat: pointer to a structure of checkpoint statistics1067 *1068 * Description: nilfs_cpfile_get_stat() returns information about checkpoints.1069 *1070 * Return Value: On success, 0 is returned, and checkpoints information is1071 * stored in the place pointed by @cpstat. On error, one of the following1072 * negative error codes is returned.1073 *1074 * %-EIO - I/O error.1075 *1076 * %-ENOMEM - Insufficient amount of memory available.1077 */1078int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat)1079{1080 struct buffer_head *bh;1081 struct nilfs_cpfile_header *header;1082 void *kaddr;1083 int ret;1084 1085 down_read(&NILFS_MDT(cpfile)->mi_sem);1086 1087 ret = nilfs_cpfile_get_header_block(cpfile, &bh);1088 if (ret < 0)1089 goto out_sem;1090 kaddr = kmap_local_page(bh->b_page);1091 header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);1092 cpstat->cs_cno = nilfs_mdt_cno(cpfile);1093 cpstat->cs_ncps = le64_to_cpu(header->ch_ncheckpoints);1094 cpstat->cs_nsss = le64_to_cpu(header->ch_nsnapshots);1095 kunmap_local(kaddr);1096 brelse(bh);1097 1098 out_sem:1099 up_read(&NILFS_MDT(cpfile)->mi_sem);1100 return ret;1101}1102 1103/**1104 * nilfs_cpfile_read - read or get cpfile inode1105 * @sb: super block instance1106 * @cpsize: size of a checkpoint entry1107 * @raw_inode: on-disk cpfile inode1108 * @inodep: buffer to store the inode1109 */1110int nilfs_cpfile_read(struct super_block *sb, size_t cpsize,1111 struct nilfs_inode *raw_inode, struct inode **inodep)1112{1113 struct inode *cpfile;1114 int err;1115 1116 if (cpsize > sb->s_blocksize) {1117 nilfs_err(sb, "too large checkpoint size: %zu bytes", cpsize);1118 return -EINVAL;1119 } else if (cpsize < NILFS_MIN_CHECKPOINT_SIZE) {1120 nilfs_err(sb, "too small checkpoint size: %zu bytes", cpsize);1121 return -EINVAL;1122 }1123 1124 cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO);1125 if (unlikely(!cpfile))1126 return -ENOMEM;1127 if (!(cpfile->i_state & I_NEW))1128 goto out;1129 1130 err = nilfs_mdt_init(cpfile, NILFS_MDT_GFP, 0);1131 if (err)1132 goto failed;1133 1134 nilfs_mdt_set_entry_size(cpfile, cpsize,1135 sizeof(struct nilfs_cpfile_header));1136 1137 err = nilfs_read_inode_common(cpfile, raw_inode);1138 if (err)1139 goto failed;1140 1141 unlock_new_inode(cpfile);1142 out:1143 *inodep = cpfile;1144 return 0;1145 failed:1146 iget_failed(cpfile);1147 return err;1148}1149