xref: /openbmc/linux/fs/nilfs2/cpfile.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1ae98043fSRyusuke Konishi // SPDX-License-Identifier: GPL-2.0+
229619809SKoji Sato /*
3*94ee1d91SRyusuke Konishi  * NILFS checkpoint file.
429619809SKoji Sato  *
529619809SKoji Sato  * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
629619809SKoji Sato  *
74b420ab4SRyusuke Konishi  * Written by Koji Sato.
829619809SKoji Sato  */
929619809SKoji Sato 
1029619809SKoji Sato #include <linux/kernel.h>
1129619809SKoji Sato #include <linux/fs.h>
1229619809SKoji Sato #include <linux/string.h>
1329619809SKoji Sato #include <linux/buffer_head.h>
1429619809SKoji Sato #include <linux/errno.h>
1529619809SKoji Sato #include "mdt.h"
1629619809SKoji Sato #include "cpfile.h"
1729619809SKoji Sato 
1829619809SKoji Sato 
1929619809SKoji Sato static inline unsigned long
nilfs_cpfile_checkpoints_per_block(const struct inode * cpfile)2029619809SKoji Sato nilfs_cpfile_checkpoints_per_block(const struct inode *cpfile)
2129619809SKoji Sato {
2229619809SKoji Sato 	return NILFS_MDT(cpfile)->mi_entries_per_block;
2329619809SKoji Sato }
2429619809SKoji Sato 
2529619809SKoji Sato /* block number from the beginning of the file */
2629619809SKoji Sato static unsigned long
nilfs_cpfile_get_blkoff(const struct inode * cpfile,__u64 cno)2729619809SKoji Sato nilfs_cpfile_get_blkoff(const struct inode *cpfile, __u64 cno)
2829619809SKoji Sato {
291f5abe7eSRyusuke Konishi 	__u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
304ad364caSRyusuke Konishi 
3129619809SKoji Sato 	do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
3229619809SKoji Sato 	return (unsigned long)tcno;
3329619809SKoji Sato }
3429619809SKoji Sato 
3529619809SKoji Sato /* offset in block */
3629619809SKoji Sato static unsigned long
nilfs_cpfile_get_offset(const struct inode * cpfile,__u64 cno)3729619809SKoji Sato nilfs_cpfile_get_offset(const struct inode *cpfile, __u64 cno)
3829619809SKoji Sato {
3929619809SKoji Sato 	__u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
404ad364caSRyusuke Konishi 
4129619809SKoji Sato 	return do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
4229619809SKoji Sato }
4329619809SKoji Sato 
nilfs_cpfile_first_checkpoint_in_block(const struct inode * cpfile,unsigned long blkoff)4453a2c3bdSRyusuke Konishi static __u64 nilfs_cpfile_first_checkpoint_in_block(const struct inode *cpfile,
4553a2c3bdSRyusuke Konishi 						    unsigned long blkoff)
4653a2c3bdSRyusuke Konishi {
4753a2c3bdSRyusuke Konishi 	return (__u64)nilfs_cpfile_checkpoints_per_block(cpfile) * blkoff
4853a2c3bdSRyusuke Konishi 		+ 1 - NILFS_MDT(cpfile)->mi_first_entry_offset;
4953a2c3bdSRyusuke Konishi }
5053a2c3bdSRyusuke Konishi 
5129619809SKoji Sato static unsigned long
nilfs_cpfile_checkpoints_in_block(const struct inode * cpfile,__u64 curr,__u64 max)5229619809SKoji Sato nilfs_cpfile_checkpoints_in_block(const struct inode *cpfile,
5329619809SKoji Sato 				  __u64 curr,
5429619809SKoji Sato 				  __u64 max)
5529619809SKoji Sato {
5629619809SKoji Sato 	return min_t(__u64,
5729619809SKoji Sato 		     nilfs_cpfile_checkpoints_per_block(cpfile) -
5829619809SKoji Sato 		     nilfs_cpfile_get_offset(cpfile, curr),
5929619809SKoji Sato 		     max - curr);
6029619809SKoji Sato }
6129619809SKoji Sato 
nilfs_cpfile_is_in_first(const struct inode * cpfile,__u64 cno)6229619809SKoji Sato static inline int nilfs_cpfile_is_in_first(const struct inode *cpfile,
6329619809SKoji Sato 					   __u64 cno)
6429619809SKoji Sato {
6529619809SKoji Sato 	return nilfs_cpfile_get_blkoff(cpfile, cno) == 0;
6629619809SKoji Sato }
6729619809SKoji Sato 
6829619809SKoji Sato static unsigned int
nilfs_cpfile_block_add_valid_checkpoints(const struct inode * cpfile,struct buffer_head * bh,void * kaddr,unsigned int n)6929619809SKoji Sato nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile,
7029619809SKoji Sato 					 struct buffer_head *bh,
7129619809SKoji Sato 					 void *kaddr,
7229619809SKoji Sato 					 unsigned int n)
7329619809SKoji Sato {
7429619809SKoji Sato 	struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);
7529619809SKoji Sato 	unsigned int count;
7629619809SKoji Sato 
7729619809SKoji Sato 	count = le32_to_cpu(cp->cp_checkpoints_count) + n;
7829619809SKoji Sato 	cp->cp_checkpoints_count = cpu_to_le32(count);
7929619809SKoji Sato 	return count;
8029619809SKoji Sato }
8129619809SKoji Sato 
8229619809SKoji Sato static unsigned int
nilfs_cpfile_block_sub_valid_checkpoints(const struct inode * cpfile,struct buffer_head * bh,void * kaddr,unsigned int n)8329619809SKoji Sato nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile,
8429619809SKoji Sato 					 struct buffer_head *bh,
8529619809SKoji Sato 					 void *kaddr,
8629619809SKoji Sato 					 unsigned int n)
8729619809SKoji Sato {
8829619809SKoji Sato 	struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);
8929619809SKoji Sato 	unsigned int count;
9029619809SKoji Sato 
911f5abe7eSRyusuke Konishi 	WARN_ON(le32_to_cpu(cp->cp_checkpoints_count) < n);
9229619809SKoji Sato 	count = le32_to_cpu(cp->cp_checkpoints_count) - n;
9329619809SKoji Sato 	cp->cp_checkpoints_count = cpu_to_le32(count);
9429619809SKoji Sato 	return count;
9529619809SKoji Sato }
9629619809SKoji Sato 
9729619809SKoji Sato static inline struct nilfs_cpfile_header *
nilfs_cpfile_block_get_header(const struct inode * cpfile,struct buffer_head * bh,void * kaddr)9829619809SKoji Sato nilfs_cpfile_block_get_header(const struct inode *cpfile,
9929619809SKoji Sato 			      struct buffer_head *bh,
10029619809SKoji Sato 			      void *kaddr)
10129619809SKoji Sato {
10229619809SKoji Sato 	return kaddr + bh_offset(bh);
10329619809SKoji Sato }
10429619809SKoji Sato 
10529619809SKoji Sato static struct nilfs_checkpoint *
nilfs_cpfile_block_get_checkpoint(const struct inode * cpfile,__u64 cno,struct buffer_head * bh,void * kaddr)10629619809SKoji Sato nilfs_cpfile_block_get_checkpoint(const struct inode *cpfile, __u64 cno,
10729619809SKoji Sato 				  struct buffer_head *bh,
10829619809SKoji Sato 				  void *kaddr)
10929619809SKoji Sato {
11029619809SKoji Sato 	return kaddr + bh_offset(bh) + nilfs_cpfile_get_offset(cpfile, cno) *
11129619809SKoji Sato 		NILFS_MDT(cpfile)->mi_entry_size;
11229619809SKoji Sato }
11329619809SKoji Sato 
nilfs_cpfile_block_init(struct inode * cpfile,struct buffer_head * bh,void * kaddr)11429619809SKoji Sato static void nilfs_cpfile_block_init(struct inode *cpfile,
11529619809SKoji Sato 				    struct buffer_head *bh,
11629619809SKoji Sato 				    void *kaddr)
11729619809SKoji Sato {
11829619809SKoji Sato 	struct nilfs_checkpoint *cp = kaddr + bh_offset(bh);
11929619809SKoji Sato 	size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
12029619809SKoji Sato 	int n = nilfs_cpfile_checkpoints_per_block(cpfile);
12129619809SKoji Sato 
12229619809SKoji Sato 	while (n-- > 0) {
12329619809SKoji Sato 		nilfs_checkpoint_set_invalid(cp);
12429619809SKoji Sato 		cp = (void *)cp + cpsz;
12529619809SKoji Sato 	}
12629619809SKoji Sato }
12729619809SKoji Sato 
nilfs_cpfile_get_header_block(struct inode * cpfile,struct buffer_head ** bhp)12829619809SKoji Sato static inline int nilfs_cpfile_get_header_block(struct inode *cpfile,
12929619809SKoji Sato 						struct buffer_head **bhp)
13029619809SKoji Sato {
13129619809SKoji Sato 	return nilfs_mdt_get_block(cpfile, 0, 0, NULL, bhp);
13229619809SKoji Sato }
13329619809SKoji Sato 
nilfs_cpfile_get_checkpoint_block(struct inode * cpfile,__u64 cno,int create,struct buffer_head ** bhp)13429619809SKoji Sato static inline int nilfs_cpfile_get_checkpoint_block(struct inode *cpfile,
13529619809SKoji Sato 						    __u64 cno,
13629619809SKoji Sato 						    int create,
13729619809SKoji Sato 						    struct buffer_head **bhp)
13829619809SKoji Sato {
13929619809SKoji Sato 	return nilfs_mdt_get_block(cpfile,
14029619809SKoji Sato 				   nilfs_cpfile_get_blkoff(cpfile, cno),
14129619809SKoji Sato 				   create, nilfs_cpfile_block_init, bhp);
14229619809SKoji Sato }
14329619809SKoji Sato 
14453a2c3bdSRyusuke Konishi /**
14553a2c3bdSRyusuke Konishi  * nilfs_cpfile_find_checkpoint_block - find and get a buffer on cpfile
14653a2c3bdSRyusuke Konishi  * @cpfile: inode of cpfile
14753a2c3bdSRyusuke Konishi  * @start_cno: start checkpoint number (inclusive)
14853a2c3bdSRyusuke Konishi  * @end_cno: end checkpoint number (inclusive)
14953a2c3bdSRyusuke Konishi  * @cnop: place to store the next checkpoint number
15053a2c3bdSRyusuke Konishi  * @bhp: place to store a pointer to buffer_head struct
15153a2c3bdSRyusuke Konishi  *
15253a2c3bdSRyusuke Konishi  * Return Value: On success, it returns 0. On error, the following negative
15353a2c3bdSRyusuke Konishi  * error code is returned.
15453a2c3bdSRyusuke Konishi  *
15553a2c3bdSRyusuke Konishi  * %-ENOMEM - Insufficient memory available.
15653a2c3bdSRyusuke Konishi  *
15753a2c3bdSRyusuke Konishi  * %-EIO - I/O error
15853a2c3bdSRyusuke Konishi  *
15953a2c3bdSRyusuke Konishi  * %-ENOENT - no block exists in the range.
16053a2c3bdSRyusuke Konishi  */
nilfs_cpfile_find_checkpoint_block(struct inode * cpfile,__u64 start_cno,__u64 end_cno,__u64 * cnop,struct buffer_head ** bhp)16153a2c3bdSRyusuke Konishi static int nilfs_cpfile_find_checkpoint_block(struct inode *cpfile,
16253a2c3bdSRyusuke Konishi 					      __u64 start_cno, __u64 end_cno,
16353a2c3bdSRyusuke Konishi 					      __u64 *cnop,
16453a2c3bdSRyusuke Konishi 					      struct buffer_head **bhp)
16553a2c3bdSRyusuke Konishi {
16653a2c3bdSRyusuke Konishi 	unsigned long start, end, blkoff;
16753a2c3bdSRyusuke Konishi 	int ret;
16853a2c3bdSRyusuke Konishi 
16953a2c3bdSRyusuke Konishi 	if (unlikely(start_cno > end_cno))
17053a2c3bdSRyusuke Konishi 		return -ENOENT;
17153a2c3bdSRyusuke Konishi 
17253a2c3bdSRyusuke Konishi 	start = nilfs_cpfile_get_blkoff(cpfile, start_cno);
17353a2c3bdSRyusuke Konishi 	end = nilfs_cpfile_get_blkoff(cpfile, end_cno);
17453a2c3bdSRyusuke Konishi 
17553a2c3bdSRyusuke Konishi 	ret = nilfs_mdt_find_block(cpfile, start, end, &blkoff, bhp);
17653a2c3bdSRyusuke Konishi 	if (!ret)
17753a2c3bdSRyusuke Konishi 		*cnop = (blkoff == start) ? start_cno :
17853a2c3bdSRyusuke Konishi 			nilfs_cpfile_first_checkpoint_in_block(cpfile, blkoff);
17953a2c3bdSRyusuke Konishi 	return ret;
18053a2c3bdSRyusuke Konishi }
18153a2c3bdSRyusuke Konishi 
nilfs_cpfile_delete_checkpoint_block(struct inode * cpfile,__u64 cno)18229619809SKoji Sato static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile,
18329619809SKoji Sato 						       __u64 cno)
18429619809SKoji Sato {
18529619809SKoji Sato 	return nilfs_mdt_delete_block(cpfile,
18629619809SKoji Sato 				      nilfs_cpfile_get_blkoff(cpfile, cno));
18729619809SKoji Sato }
18829619809SKoji Sato 
18929619809SKoji Sato /**
19029619809SKoji Sato  * nilfs_cpfile_get_checkpoint - get a checkpoint
19129619809SKoji Sato  * @cpfile: inode of checkpoint file
19229619809SKoji Sato  * @cno: checkpoint number
19329619809SKoji Sato  * @create: create flag
19429619809SKoji Sato  * @cpp: pointer to a checkpoint
19529619809SKoji Sato  * @bhp: pointer to a buffer head
19629619809SKoji Sato  *
19729619809SKoji Sato  * Description: nilfs_cpfile_get_checkpoint() acquires the checkpoint
19829619809SKoji Sato  * specified by @cno. A new checkpoint will be created if @cno is the current
19929619809SKoji Sato  * checkpoint number and @create is nonzero.
20029619809SKoji Sato  *
20129619809SKoji Sato  * Return Value: On success, 0 is returned, and the checkpoint and the
20229619809SKoji Sato  * buffer head of the buffer on which the checkpoint is located are stored in
20329619809SKoji Sato  * the place pointed by @cpp and @bhp, respectively. On error, one of the
20429619809SKoji Sato  * following negative error codes is returned.
20529619809SKoji Sato  *
20629619809SKoji Sato  * %-EIO - I/O error.
20729619809SKoji Sato  *
20829619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
20929619809SKoji Sato  *
21029619809SKoji Sato  * %-ENOENT - No such checkpoint.
2111f5abe7eSRyusuke Konishi  *
2121f5abe7eSRyusuke Konishi  * %-EINVAL - invalid checkpoint.
21329619809SKoji Sato  */
nilfs_cpfile_get_checkpoint(struct inode * cpfile,__u64 cno,int create,struct nilfs_checkpoint ** cpp,struct buffer_head ** bhp)21429619809SKoji Sato int nilfs_cpfile_get_checkpoint(struct inode *cpfile,
21529619809SKoji Sato 				__u64 cno,
21629619809SKoji Sato 				int create,
21729619809SKoji Sato 				struct nilfs_checkpoint **cpp,
21829619809SKoji Sato 				struct buffer_head **bhp)
21929619809SKoji Sato {
22029619809SKoji Sato 	struct buffer_head *header_bh, *cp_bh;
22129619809SKoji Sato 	struct nilfs_cpfile_header *header;
22229619809SKoji Sato 	struct nilfs_checkpoint *cp;
22329619809SKoji Sato 	void *kaddr;
22429619809SKoji Sato 	int ret;
22529619809SKoji Sato 
2261f5abe7eSRyusuke Konishi 	if (unlikely(cno < 1 || cno > nilfs_mdt_cno(cpfile) ||
2271f5abe7eSRyusuke Konishi 		     (cno < nilfs_mdt_cno(cpfile) && create)))
2281f5abe7eSRyusuke Konishi 		return -EINVAL;
22929619809SKoji Sato 
23029619809SKoji Sato 	down_write(&NILFS_MDT(cpfile)->mi_sem);
23129619809SKoji Sato 
23229619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
23329619809SKoji Sato 	if (ret < 0)
23429619809SKoji Sato 		goto out_sem;
23529619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, create, &cp_bh);
23629619809SKoji Sato 	if (ret < 0)
23729619809SKoji Sato 		goto out_header;
23829619809SKoji Sato 	kaddr = kmap(cp_bh->b_page);
23929619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
24029619809SKoji Sato 	if (nilfs_checkpoint_invalid(cp)) {
24129619809SKoji Sato 		if (!create) {
24229619809SKoji Sato 			kunmap(cp_bh->b_page);
24329619809SKoji Sato 			brelse(cp_bh);
24429619809SKoji Sato 			ret = -ENOENT;
24529619809SKoji Sato 			goto out_header;
24629619809SKoji Sato 		}
24729619809SKoji Sato 		/* a newly-created checkpoint */
24829619809SKoji Sato 		nilfs_checkpoint_clear_invalid(cp);
24929619809SKoji Sato 		if (!nilfs_cpfile_is_in_first(cpfile, cno))
25029619809SKoji Sato 			nilfs_cpfile_block_add_valid_checkpoints(cpfile, cp_bh,
25129619809SKoji Sato 								 kaddr, 1);
2525fc7b141SRyusuke Konishi 		mark_buffer_dirty(cp_bh);
25329619809SKoji Sato 
2547b9c0976SCong Wang 		kaddr = kmap_atomic(header_bh->b_page);
25529619809SKoji Sato 		header = nilfs_cpfile_block_get_header(cpfile, header_bh,
25629619809SKoji Sato 						       kaddr);
25729619809SKoji Sato 		le64_add_cpu(&header->ch_ncheckpoints, 1);
2587b9c0976SCong Wang 		kunmap_atomic(kaddr);
2595fc7b141SRyusuke Konishi 		mark_buffer_dirty(header_bh);
26029619809SKoji Sato 		nilfs_mdt_mark_dirty(cpfile);
26129619809SKoji Sato 	}
26229619809SKoji Sato 
26329619809SKoji Sato 	if (cpp != NULL)
26429619809SKoji Sato 		*cpp = cp;
26529619809SKoji Sato 	*bhp = cp_bh;
26629619809SKoji Sato 
26729619809SKoji Sato  out_header:
26829619809SKoji Sato 	brelse(header_bh);
26929619809SKoji Sato 
27029619809SKoji Sato  out_sem:
27129619809SKoji Sato 	up_write(&NILFS_MDT(cpfile)->mi_sem);
27229619809SKoji Sato 	return ret;
27329619809SKoji Sato }
27429619809SKoji Sato 
27529619809SKoji Sato /**
27629619809SKoji Sato  * nilfs_cpfile_put_checkpoint - put a checkpoint
27729619809SKoji Sato  * @cpfile: inode of checkpoint file
27829619809SKoji Sato  * @cno: checkpoint number
27929619809SKoji Sato  * @bh: buffer head
28029619809SKoji Sato  *
28129619809SKoji Sato  * Description: nilfs_cpfile_put_checkpoint() releases the checkpoint
28229619809SKoji Sato  * specified by @cno. @bh must be the buffer head which has been returned by
28329619809SKoji Sato  * a previous call to nilfs_cpfile_get_checkpoint() with @cno.
28429619809SKoji Sato  */
nilfs_cpfile_put_checkpoint(struct inode * cpfile,__u64 cno,struct buffer_head * bh)28529619809SKoji Sato void nilfs_cpfile_put_checkpoint(struct inode *cpfile, __u64 cno,
28629619809SKoji Sato 				 struct buffer_head *bh)
28729619809SKoji Sato {
28829619809SKoji Sato 	kunmap(bh->b_page);
28929619809SKoji Sato 	brelse(bh);
29029619809SKoji Sato }
29129619809SKoji Sato 
29229619809SKoji Sato /**
29329619809SKoji Sato  * nilfs_cpfile_delete_checkpoints - delete checkpoints
29429619809SKoji Sato  * @cpfile: inode of checkpoint file
29529619809SKoji Sato  * @start: start checkpoint number
296312f79c4SLu Jialin  * @end: end checkpoint number
29729619809SKoji Sato  *
29829619809SKoji Sato  * Description: nilfs_cpfile_delete_checkpoints() deletes the checkpoints in
29929619809SKoji Sato  * the period from @start to @end, excluding @end itself. The checkpoints
30029619809SKoji Sato  * which have been already deleted are ignored.
30129619809SKoji Sato  *
30229619809SKoji Sato  * Return Value: On success, 0 is returned. On error, one of the following
30329619809SKoji Sato  * negative error codes is returned.
30429619809SKoji Sato  *
30529619809SKoji Sato  * %-EIO - I/O error.
30629619809SKoji Sato  *
30729619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
30829619809SKoji Sato  *
30929619809SKoji Sato  * %-EINVAL - invalid checkpoints.
31029619809SKoji Sato  */
nilfs_cpfile_delete_checkpoints(struct inode * cpfile,__u64 start,__u64 end)31129619809SKoji Sato int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
31229619809SKoji Sato 				    __u64 start,
31329619809SKoji Sato 				    __u64 end)
31429619809SKoji Sato {
31529619809SKoji Sato 	struct buffer_head *header_bh, *cp_bh;
31629619809SKoji Sato 	struct nilfs_cpfile_header *header;
31729619809SKoji Sato 	struct nilfs_checkpoint *cp;
31829619809SKoji Sato 	size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
31929619809SKoji Sato 	__u64 cno;
32029619809SKoji Sato 	void *kaddr;
32129619809SKoji Sato 	unsigned long tnicps;
322fe0627e7SRyusuke Konishi 	int ret, ncps, nicps, nss, count, i;
32329619809SKoji Sato 
3241f5abe7eSRyusuke Konishi 	if (unlikely(start == 0 || start > end)) {
325a1d0747aSJoe Perches 		nilfs_err(cpfile->i_sb,
326feee880fSRyusuke Konishi 			  "cannot delete checkpoints: invalid range [%llu, %llu)",
3271f5abe7eSRyusuke Konishi 			  (unsigned long long)start, (unsigned long long)end);
3281f5abe7eSRyusuke Konishi 		return -EINVAL;
32929619809SKoji Sato 	}
33029619809SKoji Sato 
33129619809SKoji Sato 	down_write(&NILFS_MDT(cpfile)->mi_sem);
33229619809SKoji Sato 
33329619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
33429619809SKoji Sato 	if (ret < 0)
33529619809SKoji Sato 		goto out_sem;
33629619809SKoji Sato 	tnicps = 0;
337fe0627e7SRyusuke Konishi 	nss = 0;
33829619809SKoji Sato 
33929619809SKoji Sato 	for (cno = start; cno < end; cno += ncps) {
34029619809SKoji Sato 		ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end);
34129619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
34229619809SKoji Sato 		if (ret < 0) {
34329619809SKoji Sato 			if (ret != -ENOENT)
344d9a0a345SJiro SEKIBA 				break;
34529619809SKoji Sato 			/* skip hole */
34629619809SKoji Sato 			ret = 0;
34729619809SKoji Sato 			continue;
34829619809SKoji Sato 		}
34929619809SKoji Sato 
3507b9c0976SCong Wang 		kaddr = kmap_atomic(cp_bh->b_page);
35129619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(
35229619809SKoji Sato 			cpfile, cno, cp_bh, kaddr);
35329619809SKoji Sato 		nicps = 0;
35429619809SKoji Sato 		for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {
355fe0627e7SRyusuke Konishi 			if (nilfs_checkpoint_snapshot(cp)) {
356fe0627e7SRyusuke Konishi 				nss++;
357fe0627e7SRyusuke Konishi 			} else if (!nilfs_checkpoint_invalid(cp)) {
35829619809SKoji Sato 				nilfs_checkpoint_set_invalid(cp);
35929619809SKoji Sato 				nicps++;
36029619809SKoji Sato 			}
36129619809SKoji Sato 		}
36229619809SKoji Sato 		if (nicps > 0) {
36329619809SKoji Sato 			tnicps += nicps;
3645fc7b141SRyusuke Konishi 			mark_buffer_dirty(cp_bh);
36529619809SKoji Sato 			nilfs_mdt_mark_dirty(cpfile);
3665ee58148SJiro SEKIBA 			if (!nilfs_cpfile_is_in_first(cpfile, cno)) {
3675ee58148SJiro SEKIBA 				count =
3685ee58148SJiro SEKIBA 				  nilfs_cpfile_block_sub_valid_checkpoints(
3695ee58148SJiro SEKIBA 						cpfile, cp_bh, kaddr, nicps);
3705ee58148SJiro SEKIBA 				if (count == 0) {
37129619809SKoji Sato 					/* make hole */
3727b9c0976SCong Wang 					kunmap_atomic(kaddr);
37329619809SKoji Sato 					brelse(cp_bh);
3745ee58148SJiro SEKIBA 					ret =
3755ee58148SJiro SEKIBA 					  nilfs_cpfile_delete_checkpoint_block(
37629619809SKoji Sato 								   cpfile, cno);
37729619809SKoji Sato 					if (ret == 0)
37829619809SKoji Sato 						continue;
379a1d0747aSJoe Perches 					nilfs_err(cpfile->i_sb,
380feee880fSRyusuke Konishi 						  "error %d deleting checkpoint block",
381feee880fSRyusuke Konishi 						  ret);
382d9a0a345SJiro SEKIBA 					break;
38329619809SKoji Sato 				}
38429619809SKoji Sato 			}
3855ee58148SJiro SEKIBA 		}
38629619809SKoji Sato 
3877b9c0976SCong Wang 		kunmap_atomic(kaddr);
38829619809SKoji Sato 		brelse(cp_bh);
38929619809SKoji Sato 	}
39029619809SKoji Sato 
39129619809SKoji Sato 	if (tnicps > 0) {
3927b9c0976SCong Wang 		kaddr = kmap_atomic(header_bh->b_page);
39329619809SKoji Sato 		header = nilfs_cpfile_block_get_header(cpfile, header_bh,
39429619809SKoji Sato 						       kaddr);
3956c98cd4eSKoji Sato 		le64_add_cpu(&header->ch_ncheckpoints, -(u64)tnicps);
3965fc7b141SRyusuke Konishi 		mark_buffer_dirty(header_bh);
39729619809SKoji Sato 		nilfs_mdt_mark_dirty(cpfile);
3987b9c0976SCong Wang 		kunmap_atomic(kaddr);
39929619809SKoji Sato 	}
40062013ab5SRyusuke Konishi 
40129619809SKoji Sato 	brelse(header_bh);
402fe0627e7SRyusuke Konishi 	if (nss > 0)
403fe0627e7SRyusuke Konishi 		ret = -EBUSY;
40429619809SKoji Sato 
40529619809SKoji Sato  out_sem:
40629619809SKoji Sato 	up_write(&NILFS_MDT(cpfile)->mi_sem);
40729619809SKoji Sato 	return ret;
40829619809SKoji Sato }
40929619809SKoji Sato 
nilfs_cpfile_checkpoint_to_cpinfo(struct inode * cpfile,struct nilfs_checkpoint * cp,struct nilfs_cpinfo * ci)41029619809SKoji Sato static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile,
41129619809SKoji Sato 					      struct nilfs_checkpoint *cp,
41229619809SKoji Sato 					      struct nilfs_cpinfo *ci)
41329619809SKoji Sato {
41429619809SKoji Sato 	ci->ci_flags = le32_to_cpu(cp->cp_flags);
41529619809SKoji Sato 	ci->ci_cno = le64_to_cpu(cp->cp_cno);
41629619809SKoji Sato 	ci->ci_create = le64_to_cpu(cp->cp_create);
41729619809SKoji Sato 	ci->ci_nblk_inc = le64_to_cpu(cp->cp_nblk_inc);
41829619809SKoji Sato 	ci->ci_inodes_count = le64_to_cpu(cp->cp_inodes_count);
41929619809SKoji Sato 	ci->ci_blocks_count = le64_to_cpu(cp->cp_blocks_count);
42029619809SKoji Sato 	ci->ci_next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
42129619809SKoji Sato }
42229619809SKoji Sato 
nilfs_cpfile_do_get_cpinfo(struct inode * cpfile,__u64 * cnop,void * buf,unsigned int cisz,size_t nci)42376068c4fSRyusuke Konishi static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
4240c6c44cbSRyusuke Konishi 					  void *buf, unsigned int cisz,
4250c6c44cbSRyusuke Konishi 					  size_t nci)
42629619809SKoji Sato {
42729619809SKoji Sato 	struct nilfs_checkpoint *cp;
428003ff182SRyusuke Konishi 	struct nilfs_cpinfo *ci = buf;
42929619809SKoji Sato 	struct buffer_head *bh;
43029619809SKoji Sato 	size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
43176068c4fSRyusuke Konishi 	__u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop;
43229619809SKoji Sato 	void *kaddr;
43329619809SKoji Sato 	int n, ret;
43429619809SKoji Sato 	int ncps, i;
43529619809SKoji Sato 
4361f5abe7eSRyusuke Konishi 	if (cno == 0)
4371f5abe7eSRyusuke Konishi 		return -ENOENT; /* checkpoint number 0 is invalid */
43829619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
43929619809SKoji Sato 
44053a2c3bdSRyusuke Konishi 	for (n = 0; n < nci; cno += ncps) {
44153a2c3bdSRyusuke Konishi 		ret = nilfs_cpfile_find_checkpoint_block(
44253a2c3bdSRyusuke Konishi 			cpfile, cno, cur_cno - 1, &cno, &bh);
44329619809SKoji Sato 		if (ret < 0) {
44453a2c3bdSRyusuke Konishi 			if (likely(ret == -ENOENT))
44553a2c3bdSRyusuke Konishi 				break;
44629619809SKoji Sato 			goto out;
44729619809SKoji Sato 		}
44853a2c3bdSRyusuke Konishi 		ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno);
44929619809SKoji Sato 
4507b9c0976SCong Wang 		kaddr = kmap_atomic(bh->b_page);
45129619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
45229619809SKoji Sato 		for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) {
453003ff182SRyusuke Konishi 			if (!nilfs_checkpoint_invalid(cp)) {
454003ff182SRyusuke Konishi 				nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp,
455003ff182SRyusuke Konishi 								  ci);
456003ff182SRyusuke Konishi 				ci = (void *)ci + cisz;
457003ff182SRyusuke Konishi 				n++;
458003ff182SRyusuke Konishi 			}
45929619809SKoji Sato 		}
4607b9c0976SCong Wang 		kunmap_atomic(kaddr);
46129619809SKoji Sato 		brelse(bh);
46229619809SKoji Sato 	}
46329619809SKoji Sato 
46429619809SKoji Sato 	ret = n;
465003ff182SRyusuke Konishi 	if (n > 0) {
466003ff182SRyusuke Konishi 		ci = (void *)ci - cisz;
467003ff182SRyusuke Konishi 		*cnop = ci->ci_cno + 1;
468003ff182SRyusuke Konishi 	}
46929619809SKoji Sato 
47029619809SKoji Sato  out:
47129619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
47229619809SKoji Sato 	return ret;
47329619809SKoji Sato }
47429619809SKoji Sato 
nilfs_cpfile_do_get_ssinfo(struct inode * cpfile,__u64 * cnop,void * buf,unsigned int cisz,size_t nci)475b028fcfcSRyusuke Konishi static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
4760c6c44cbSRyusuke Konishi 					  void *buf, unsigned int cisz,
4770c6c44cbSRyusuke Konishi 					  size_t nci)
47829619809SKoji Sato {
47929619809SKoji Sato 	struct buffer_head *bh;
48029619809SKoji Sato 	struct nilfs_cpfile_header *header;
48129619809SKoji Sato 	struct nilfs_checkpoint *cp;
482003ff182SRyusuke Konishi 	struct nilfs_cpinfo *ci = buf;
483b028fcfcSRyusuke Konishi 	__u64 curr = *cnop, next;
48429619809SKoji Sato 	unsigned long curr_blkoff, next_blkoff;
48529619809SKoji Sato 	void *kaddr;
4867fa10d20SRyusuke Konishi 	int n = 0, ret;
48729619809SKoji Sato 
48829619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
48929619809SKoji Sato 
490b028fcfcSRyusuke Konishi 	if (curr == 0) {
49129619809SKoji Sato 		ret = nilfs_cpfile_get_header_block(cpfile, &bh);
49229619809SKoji Sato 		if (ret < 0)
49329619809SKoji Sato 			goto out;
4947b9c0976SCong Wang 		kaddr = kmap_atomic(bh->b_page);
49529619809SKoji Sato 		header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
49629619809SKoji Sato 		curr = le64_to_cpu(header->ch_snapshot_list.ssl_next);
4977b9c0976SCong Wang 		kunmap_atomic(kaddr);
49829619809SKoji Sato 		brelse(bh);
49929619809SKoji Sato 		if (curr == 0) {
50029619809SKoji Sato 			ret = 0;
50129619809SKoji Sato 			goto out;
50229619809SKoji Sato 		}
503b028fcfcSRyusuke Konishi 	} else if (unlikely(curr == ~(__u64)0)) {
504b028fcfcSRyusuke Konishi 		ret = 0;
505b028fcfcSRyusuke Konishi 		goto out;
506b028fcfcSRyusuke Konishi 	}
507b028fcfcSRyusuke Konishi 
50829619809SKoji Sato 	curr_blkoff = nilfs_cpfile_get_blkoff(cpfile, curr);
50929619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &bh);
5107fa10d20SRyusuke Konishi 	if (unlikely(ret < 0)) {
5117fa10d20SRyusuke Konishi 		if (ret == -ENOENT)
5127fa10d20SRyusuke Konishi 			ret = 0; /* No snapshots (started from a hole block) */
51329619809SKoji Sato 		goto out;
51429619809SKoji Sato 	}
5157b9c0976SCong Wang 	kaddr = kmap_atomic(bh->b_page);
5167fa10d20SRyusuke Konishi 	while (n < nci) {
5177fa10d20SRyusuke Konishi 		cp = nilfs_cpfile_block_get_checkpoint(cpfile, curr, bh, kaddr);
5187fa10d20SRyusuke Konishi 		curr = ~(__u64)0; /* Terminator */
5197fa10d20SRyusuke Konishi 		if (unlikely(nilfs_checkpoint_invalid(cp) ||
5207fa10d20SRyusuke Konishi 			     !nilfs_checkpoint_snapshot(cp)))
5217fa10d20SRyusuke Konishi 			break;
522003ff182SRyusuke Konishi 		nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, ci);
523003ff182SRyusuke Konishi 		ci = (void *)ci + cisz;
524003ff182SRyusuke Konishi 		n++;
5257fa10d20SRyusuke Konishi 		next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
5267fa10d20SRyusuke Konishi 		if (next == 0)
5277fa10d20SRyusuke Konishi 			break; /* reach end of the snapshot list */
5287fa10d20SRyusuke Konishi 
52929619809SKoji Sato 		next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next);
53029619809SKoji Sato 		if (curr_blkoff != next_blkoff) {
5317b9c0976SCong Wang 			kunmap_atomic(kaddr);
53229619809SKoji Sato 			brelse(bh);
53329619809SKoji Sato 			ret = nilfs_cpfile_get_checkpoint_block(cpfile, next,
53429619809SKoji Sato 								0, &bh);
5357fa10d20SRyusuke Konishi 			if (unlikely(ret < 0)) {
5367fa10d20SRyusuke Konishi 				WARN_ON(ret == -ENOENT);
53729619809SKoji Sato 				goto out;
5387fa10d20SRyusuke Konishi 			}
5397b9c0976SCong Wang 			kaddr = kmap_atomic(bh->b_page);
54029619809SKoji Sato 		}
54129619809SKoji Sato 		curr = next;
54229619809SKoji Sato 		curr_blkoff = next_blkoff;
54329619809SKoji Sato 	}
5447b9c0976SCong Wang 	kunmap_atomic(kaddr);
54529619809SKoji Sato 	brelse(bh);
546b028fcfcSRyusuke Konishi 	*cnop = curr;
54729619809SKoji Sato 	ret = n;
54829619809SKoji Sato 
54929619809SKoji Sato  out:
55029619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
55129619809SKoji Sato 	return ret;
55229619809SKoji Sato }
55329619809SKoji Sato 
55429619809SKoji Sato /**
55529619809SKoji Sato  * nilfs_cpfile_get_cpinfo -
55629619809SKoji Sato  * @cpfile:
55729619809SKoji Sato  * @cno:
55829619809SKoji Sato  * @ci:
55929619809SKoji Sato  * @nci:
56029619809SKoji Sato  */
561b028fcfcSRyusuke Konishi 
nilfs_cpfile_get_cpinfo(struct inode * cpfile,__u64 * cnop,int mode,void * buf,unsigned int cisz,size_t nci)562b028fcfcSRyusuke Konishi ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,
5630c6c44cbSRyusuke Konishi 				void *buf, unsigned int cisz, size_t nci)
56429619809SKoji Sato {
56529619809SKoji Sato 	switch (mode) {
56629619809SKoji Sato 	case NILFS_CHECKPOINT:
567003ff182SRyusuke Konishi 		return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, buf, cisz, nci);
56829619809SKoji Sato 	case NILFS_SNAPSHOT:
569003ff182SRyusuke Konishi 		return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, buf, cisz, nci);
57029619809SKoji Sato 	default:
57129619809SKoji Sato 		return -EINVAL;
57229619809SKoji Sato 	}
57329619809SKoji Sato }
57429619809SKoji Sato 
57529619809SKoji Sato /**
57629619809SKoji Sato  * nilfs_cpfile_delete_checkpoint -
57729619809SKoji Sato  * @cpfile:
57829619809SKoji Sato  * @cno:
57929619809SKoji Sato  */
nilfs_cpfile_delete_checkpoint(struct inode * cpfile,__u64 cno)58029619809SKoji Sato int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno)
58129619809SKoji Sato {
58229619809SKoji Sato 	struct nilfs_cpinfo ci;
58376068c4fSRyusuke Konishi 	__u64 tcno = cno;
58429619809SKoji Sato 	ssize_t nci;
58529619809SKoji Sato 
586003ff182SRyusuke Konishi 	nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, sizeof(ci), 1);
58729619809SKoji Sato 	if (nci < 0)
58829619809SKoji Sato 		return nci;
58929619809SKoji Sato 	else if (nci == 0 || ci.ci_cno != cno)
59029619809SKoji Sato 		return -ENOENT;
59130c25be7SRyusuke Konishi 	else if (nilfs_cpinfo_snapshot(&ci))
59230c25be7SRyusuke Konishi 		return -EBUSY;
59329619809SKoji Sato 
59429619809SKoji Sato 	return nilfs_cpfile_delete_checkpoints(cpfile, cno, cno + 1);
59529619809SKoji Sato }
59629619809SKoji Sato 
59729619809SKoji Sato static struct nilfs_snapshot_list *
nilfs_cpfile_block_get_snapshot_list(const struct inode * cpfile,__u64 cno,struct buffer_head * bh,void * kaddr)59829619809SKoji Sato nilfs_cpfile_block_get_snapshot_list(const struct inode *cpfile,
59929619809SKoji Sato 				     __u64 cno,
60029619809SKoji Sato 				     struct buffer_head *bh,
60129619809SKoji Sato 				     void *kaddr)
60229619809SKoji Sato {
60329619809SKoji Sato 	struct nilfs_cpfile_header *header;
60429619809SKoji Sato 	struct nilfs_checkpoint *cp;
60529619809SKoji Sato 	struct nilfs_snapshot_list *list;
60629619809SKoji Sato 
60729619809SKoji Sato 	if (cno != 0) {
60829619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
60929619809SKoji Sato 		list = &cp->cp_snapshot_list;
61029619809SKoji Sato 	} else {
61129619809SKoji Sato 		header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
61229619809SKoji Sato 		list = &header->ch_snapshot_list;
61329619809SKoji Sato 	}
61429619809SKoji Sato 	return list;
61529619809SKoji Sato }
61629619809SKoji Sato 
nilfs_cpfile_set_snapshot(struct inode * cpfile,__u64 cno)61729619809SKoji Sato static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno)
61829619809SKoji Sato {
61929619809SKoji Sato 	struct buffer_head *header_bh, *curr_bh, *prev_bh, *cp_bh;
62029619809SKoji Sato 	struct nilfs_cpfile_header *header;
62129619809SKoji Sato 	struct nilfs_checkpoint *cp;
62229619809SKoji Sato 	struct nilfs_snapshot_list *list;
62329619809SKoji Sato 	__u64 curr, prev;
62429619809SKoji Sato 	unsigned long curr_blkoff, prev_blkoff;
62529619809SKoji Sato 	void *kaddr;
62629619809SKoji Sato 	int ret;
62729619809SKoji Sato 
6281f5abe7eSRyusuke Konishi 	if (cno == 0)
6291f5abe7eSRyusuke Konishi 		return -ENOENT; /* checkpoint number 0 is invalid */
63029619809SKoji Sato 	down_write(&NILFS_MDT(cpfile)->mi_sem);
63129619809SKoji Sato 
63229619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
63329619809SKoji Sato 	if (ret < 0)
63429619809SKoji Sato 		goto out_sem;
6357b9c0976SCong Wang 	kaddr = kmap_atomic(cp_bh->b_page);
63629619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
63729619809SKoji Sato 	if (nilfs_checkpoint_invalid(cp)) {
63829619809SKoji Sato 		ret = -ENOENT;
6397b9c0976SCong Wang 		kunmap_atomic(kaddr);
64029619809SKoji Sato 		goto out_cp;
64129619809SKoji Sato 	}
64229619809SKoji Sato 	if (nilfs_checkpoint_snapshot(cp)) {
64329619809SKoji Sato 		ret = 0;
6447b9c0976SCong Wang 		kunmap_atomic(kaddr);
64529619809SKoji Sato 		goto out_cp;
64629619809SKoji Sato 	}
6477b9c0976SCong Wang 	kunmap_atomic(kaddr);
64829619809SKoji Sato 
64929619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
65029619809SKoji Sato 	if (ret < 0)
65129619809SKoji Sato 		goto out_cp;
6527b9c0976SCong Wang 	kaddr = kmap_atomic(header_bh->b_page);
65329619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
65429619809SKoji Sato 	list = &header->ch_snapshot_list;
65529619809SKoji Sato 	curr_bh = header_bh;
65629619809SKoji Sato 	get_bh(curr_bh);
65729619809SKoji Sato 	curr = 0;
65829619809SKoji Sato 	curr_blkoff = 0;
65929619809SKoji Sato 	prev = le64_to_cpu(list->ssl_prev);
66029619809SKoji Sato 	while (prev > cno) {
66129619809SKoji Sato 		prev_blkoff = nilfs_cpfile_get_blkoff(cpfile, prev);
66229619809SKoji Sato 		curr = prev;
66329619809SKoji Sato 		if (curr_blkoff != prev_blkoff) {
6647b9c0976SCong Wang 			kunmap_atomic(kaddr);
66529619809SKoji Sato 			brelse(curr_bh);
66629619809SKoji Sato 			ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr,
66729619809SKoji Sato 								0, &curr_bh);
66829619809SKoji Sato 			if (ret < 0)
66929619809SKoji Sato 				goto out_header;
6707b9c0976SCong Wang 			kaddr = kmap_atomic(curr_bh->b_page);
67129619809SKoji Sato 		}
67229619809SKoji Sato 		curr_blkoff = prev_blkoff;
67329619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(
67429619809SKoji Sato 			cpfile, curr, curr_bh, kaddr);
67529619809SKoji Sato 		list = &cp->cp_snapshot_list;
67629619809SKoji Sato 		prev = le64_to_cpu(list->ssl_prev);
67729619809SKoji Sato 	}
6787b9c0976SCong Wang 	kunmap_atomic(kaddr);
67929619809SKoji Sato 
68029619809SKoji Sato 	if (prev != 0) {
68129619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,
68229619809SKoji Sato 							&prev_bh);
68329619809SKoji Sato 		if (ret < 0)
68429619809SKoji Sato 			goto out_curr;
68529619809SKoji Sato 	} else {
68629619809SKoji Sato 		prev_bh = header_bh;
68729619809SKoji Sato 		get_bh(prev_bh);
68829619809SKoji Sato 	}
68929619809SKoji Sato 
6907b9c0976SCong Wang 	kaddr = kmap_atomic(curr_bh->b_page);
69129619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
69229619809SKoji Sato 		cpfile, curr, curr_bh, kaddr);
69329619809SKoji Sato 	list->ssl_prev = cpu_to_le64(cno);
6947b9c0976SCong Wang 	kunmap_atomic(kaddr);
69529619809SKoji Sato 
6967b9c0976SCong Wang 	kaddr = kmap_atomic(cp_bh->b_page);
69729619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
69829619809SKoji Sato 	cp->cp_snapshot_list.ssl_next = cpu_to_le64(curr);
69929619809SKoji Sato 	cp->cp_snapshot_list.ssl_prev = cpu_to_le64(prev);
70029619809SKoji Sato 	nilfs_checkpoint_set_snapshot(cp);
7017b9c0976SCong Wang 	kunmap_atomic(kaddr);
70229619809SKoji Sato 
7037b9c0976SCong Wang 	kaddr = kmap_atomic(prev_bh->b_page);
70429619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
70529619809SKoji Sato 		cpfile, prev, prev_bh, kaddr);
70629619809SKoji Sato 	list->ssl_next = cpu_to_le64(cno);
7077b9c0976SCong Wang 	kunmap_atomic(kaddr);
70829619809SKoji Sato 
7097b9c0976SCong Wang 	kaddr = kmap_atomic(header_bh->b_page);
71029619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
71129619809SKoji Sato 	le64_add_cpu(&header->ch_nsnapshots, 1);
7127b9c0976SCong Wang 	kunmap_atomic(kaddr);
71329619809SKoji Sato 
7145fc7b141SRyusuke Konishi 	mark_buffer_dirty(prev_bh);
7155fc7b141SRyusuke Konishi 	mark_buffer_dirty(curr_bh);
7165fc7b141SRyusuke Konishi 	mark_buffer_dirty(cp_bh);
7175fc7b141SRyusuke Konishi 	mark_buffer_dirty(header_bh);
71829619809SKoji Sato 	nilfs_mdt_mark_dirty(cpfile);
71929619809SKoji Sato 
72029619809SKoji Sato 	brelse(prev_bh);
72129619809SKoji Sato 
72229619809SKoji Sato  out_curr:
72329619809SKoji Sato 	brelse(curr_bh);
72429619809SKoji Sato 
72529619809SKoji Sato  out_header:
72629619809SKoji Sato 	brelse(header_bh);
72729619809SKoji Sato 
72829619809SKoji Sato  out_cp:
72929619809SKoji Sato 	brelse(cp_bh);
73029619809SKoji Sato 
73129619809SKoji Sato  out_sem:
73229619809SKoji Sato 	up_write(&NILFS_MDT(cpfile)->mi_sem);
73329619809SKoji Sato 	return ret;
73429619809SKoji Sato }
73529619809SKoji Sato 
nilfs_cpfile_clear_snapshot(struct inode * cpfile,__u64 cno)73629619809SKoji Sato static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno)
73729619809SKoji Sato {
73829619809SKoji Sato 	struct buffer_head *header_bh, *next_bh, *prev_bh, *cp_bh;
73929619809SKoji Sato 	struct nilfs_cpfile_header *header;
74029619809SKoji Sato 	struct nilfs_checkpoint *cp;
74129619809SKoji Sato 	struct nilfs_snapshot_list *list;
74229619809SKoji Sato 	__u64 next, prev;
74329619809SKoji Sato 	void *kaddr;
74429619809SKoji Sato 	int ret;
74529619809SKoji Sato 
7461f5abe7eSRyusuke Konishi 	if (cno == 0)
7471f5abe7eSRyusuke Konishi 		return -ENOENT; /* checkpoint number 0 is invalid */
74829619809SKoji Sato 	down_write(&NILFS_MDT(cpfile)->mi_sem);
74929619809SKoji Sato 
75029619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
75129619809SKoji Sato 	if (ret < 0)
75229619809SKoji Sato 		goto out_sem;
7537b9c0976SCong Wang 	kaddr = kmap_atomic(cp_bh->b_page);
75429619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
75529619809SKoji Sato 	if (nilfs_checkpoint_invalid(cp)) {
75629619809SKoji Sato 		ret = -ENOENT;
7577b9c0976SCong Wang 		kunmap_atomic(kaddr);
75829619809SKoji Sato 		goto out_cp;
75929619809SKoji Sato 	}
76029619809SKoji Sato 	if (!nilfs_checkpoint_snapshot(cp)) {
76129619809SKoji Sato 		ret = 0;
7627b9c0976SCong Wang 		kunmap_atomic(kaddr);
76329619809SKoji Sato 		goto out_cp;
76429619809SKoji Sato 	}
76529619809SKoji Sato 
76629619809SKoji Sato 	list = &cp->cp_snapshot_list;
76729619809SKoji Sato 	next = le64_to_cpu(list->ssl_next);
76829619809SKoji Sato 	prev = le64_to_cpu(list->ssl_prev);
7697b9c0976SCong Wang 	kunmap_atomic(kaddr);
77029619809SKoji Sato 
77129619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
77229619809SKoji Sato 	if (ret < 0)
77329619809SKoji Sato 		goto out_cp;
77429619809SKoji Sato 	if (next != 0) {
77529619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0,
77629619809SKoji Sato 							&next_bh);
77729619809SKoji Sato 		if (ret < 0)
77829619809SKoji Sato 			goto out_header;
77929619809SKoji Sato 	} else {
78029619809SKoji Sato 		next_bh = header_bh;
78129619809SKoji Sato 		get_bh(next_bh);
78229619809SKoji Sato 	}
78329619809SKoji Sato 	if (prev != 0) {
78429619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,
78529619809SKoji Sato 							&prev_bh);
78629619809SKoji Sato 		if (ret < 0)
78729619809SKoji Sato 			goto out_next;
78829619809SKoji Sato 	} else {
78929619809SKoji Sato 		prev_bh = header_bh;
79029619809SKoji Sato 		get_bh(prev_bh);
79129619809SKoji Sato 	}
79229619809SKoji Sato 
7937b9c0976SCong Wang 	kaddr = kmap_atomic(next_bh->b_page);
79429619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
79529619809SKoji Sato 		cpfile, next, next_bh, kaddr);
79629619809SKoji Sato 	list->ssl_prev = cpu_to_le64(prev);
7977b9c0976SCong Wang 	kunmap_atomic(kaddr);
79829619809SKoji Sato 
7997b9c0976SCong Wang 	kaddr = kmap_atomic(prev_bh->b_page);
80029619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
80129619809SKoji Sato 		cpfile, prev, prev_bh, kaddr);
80229619809SKoji Sato 	list->ssl_next = cpu_to_le64(next);
8037b9c0976SCong Wang 	kunmap_atomic(kaddr);
80429619809SKoji Sato 
8057b9c0976SCong Wang 	kaddr = kmap_atomic(cp_bh->b_page);
80629619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
80729619809SKoji Sato 	cp->cp_snapshot_list.ssl_next = cpu_to_le64(0);
80829619809SKoji Sato 	cp->cp_snapshot_list.ssl_prev = cpu_to_le64(0);
80929619809SKoji Sato 	nilfs_checkpoint_clear_snapshot(cp);
8107b9c0976SCong Wang 	kunmap_atomic(kaddr);
81129619809SKoji Sato 
8127b9c0976SCong Wang 	kaddr = kmap_atomic(header_bh->b_page);
81329619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
81429619809SKoji Sato 	le64_add_cpu(&header->ch_nsnapshots, -1);
8157b9c0976SCong Wang 	kunmap_atomic(kaddr);
81629619809SKoji Sato 
8175fc7b141SRyusuke Konishi 	mark_buffer_dirty(next_bh);
8185fc7b141SRyusuke Konishi 	mark_buffer_dirty(prev_bh);
8195fc7b141SRyusuke Konishi 	mark_buffer_dirty(cp_bh);
8205fc7b141SRyusuke Konishi 	mark_buffer_dirty(header_bh);
82129619809SKoji Sato 	nilfs_mdt_mark_dirty(cpfile);
82229619809SKoji Sato 
82329619809SKoji Sato 	brelse(prev_bh);
82429619809SKoji Sato 
82529619809SKoji Sato  out_next:
82629619809SKoji Sato 	brelse(next_bh);
82729619809SKoji Sato 
82829619809SKoji Sato  out_header:
82929619809SKoji Sato 	brelse(header_bh);
83029619809SKoji Sato 
83129619809SKoji Sato  out_cp:
83229619809SKoji Sato 	brelse(cp_bh);
83329619809SKoji Sato 
83429619809SKoji Sato  out_sem:
83529619809SKoji Sato 	up_write(&NILFS_MDT(cpfile)->mi_sem);
83629619809SKoji Sato 	return ret;
83729619809SKoji Sato }
83829619809SKoji Sato 
83929619809SKoji Sato /**
84029619809SKoji Sato  * nilfs_cpfile_is_snapshot -
84129619809SKoji Sato  * @cpfile: inode of checkpoint file
84229619809SKoji Sato  * @cno: checkpoint number
84329619809SKoji Sato  *
84429619809SKoji Sato  * Description:
84529619809SKoji Sato  *
84629619809SKoji Sato  * Return Value: On success, 1 is returned if the checkpoint specified by
84729619809SKoji Sato  * @cno is a snapshot, or 0 if not. On error, one of the following negative
84829619809SKoji Sato  * error codes is returned.
84929619809SKoji Sato  *
85029619809SKoji Sato  * %-EIO - I/O error.
85129619809SKoji Sato  *
85229619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
85329619809SKoji Sato  *
85429619809SKoji Sato  * %-ENOENT - No such checkpoint.
85529619809SKoji Sato  */
nilfs_cpfile_is_snapshot(struct inode * cpfile,__u64 cno)85629619809SKoji Sato int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno)
85729619809SKoji Sato {
85829619809SKoji Sato 	struct buffer_head *bh;
85929619809SKoji Sato 	struct nilfs_checkpoint *cp;
86029619809SKoji Sato 	void *kaddr;
86129619809SKoji Sato 	int ret;
86229619809SKoji Sato 
863076a378bSRyusuke Konishi 	/*
864076a378bSRyusuke Konishi 	 * CP number is invalid if it's zero or larger than the
865076a378bSRyusuke Konishi 	 * largest existing one.
866076a378bSRyusuke Konishi 	 */
86743be0ec0SZhu Yanhai 	if (cno == 0 || cno >= nilfs_mdt_cno(cpfile))
86843be0ec0SZhu Yanhai 		return -ENOENT;
86929619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
87029619809SKoji Sato 
87129619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh);
87229619809SKoji Sato 	if (ret < 0)
87329619809SKoji Sato 		goto out;
8747b9c0976SCong Wang 	kaddr = kmap_atomic(bh->b_page);
87529619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
87643be0ec0SZhu Yanhai 	if (nilfs_checkpoint_invalid(cp))
87743be0ec0SZhu Yanhai 		ret = -ENOENT;
87843be0ec0SZhu Yanhai 	else
87929619809SKoji Sato 		ret = nilfs_checkpoint_snapshot(cp);
8807b9c0976SCong Wang 	kunmap_atomic(kaddr);
88129619809SKoji Sato 	brelse(bh);
88229619809SKoji Sato 
88329619809SKoji Sato  out:
88429619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
88529619809SKoji Sato 	return ret;
88629619809SKoji Sato }
88729619809SKoji Sato 
88829619809SKoji Sato /**
88929619809SKoji Sato  * nilfs_cpfile_change_cpmode - change checkpoint mode
89029619809SKoji Sato  * @cpfile: inode of checkpoint file
89129619809SKoji Sato  * @cno: checkpoint number
89264ead520SWang Hai  * @mode: mode of checkpoint
89329619809SKoji Sato  *
89429619809SKoji Sato  * Description: nilfs_change_cpmode() changes the mode of the checkpoint
89529619809SKoji Sato  * specified by @cno. The mode @mode is NILFS_CHECKPOINT or NILFS_SNAPSHOT.
89629619809SKoji Sato  *
89729619809SKoji Sato  * Return Value: On success, 0 is returned. On error, one of the following
89829619809SKoji Sato  * negative error codes is returned.
89929619809SKoji Sato  *
90029619809SKoji Sato  * %-EIO - I/O error.
90129619809SKoji Sato  *
90229619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
90329619809SKoji Sato  *
90429619809SKoji Sato  * %-ENOENT - No such checkpoint.
90529619809SKoji Sato  */
nilfs_cpfile_change_cpmode(struct inode * cpfile,__u64 cno,int mode)90629619809SKoji Sato int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)
90729619809SKoji Sato {
90829619809SKoji Sato 	int ret;
90929619809SKoji Sato 
91029619809SKoji Sato 	switch (mode) {
91129619809SKoji Sato 	case NILFS_CHECKPOINT:
912032dbb3bSRyusuke Konishi 		if (nilfs_checkpoint_is_mounted(cpfile->i_sb, cno))
91329619809SKoji Sato 			/*
914032dbb3bSRyusuke Konishi 			 * Current implementation does not have to protect
915032dbb3bSRyusuke Konishi 			 * plain read-only mounts since they are exclusive
916032dbb3bSRyusuke Konishi 			 * with a read/write mount and are protected from the
917032dbb3bSRyusuke Konishi 			 * cleaner.
91829619809SKoji Sato 			 */
91929619809SKoji Sato 			ret = -EBUSY;
920032dbb3bSRyusuke Konishi 		else
92129619809SKoji Sato 			ret = nilfs_cpfile_clear_snapshot(cpfile, cno);
92229619809SKoji Sato 		return ret;
92329619809SKoji Sato 	case NILFS_SNAPSHOT:
92429619809SKoji Sato 		return nilfs_cpfile_set_snapshot(cpfile, cno);
92529619809SKoji Sato 	default:
92629619809SKoji Sato 		return -EINVAL;
92729619809SKoji Sato 	}
92829619809SKoji Sato }
92929619809SKoji Sato 
93029619809SKoji Sato /**
93129619809SKoji Sato  * nilfs_cpfile_get_stat - get checkpoint statistics
93229619809SKoji Sato  * @cpfile: inode of checkpoint file
93364ead520SWang Hai  * @cpstat: pointer to a structure of checkpoint statistics
93429619809SKoji Sato  *
93529619809SKoji Sato  * Description: nilfs_cpfile_get_stat() returns information about checkpoints.
93629619809SKoji Sato  *
93729619809SKoji Sato  * Return Value: On success, 0 is returned, and checkpoints information is
93864ead520SWang Hai  * stored in the place pointed by @cpstat. On error, one of the following
93929619809SKoji Sato  * negative error codes is returned.
94029619809SKoji Sato  *
94129619809SKoji Sato  * %-EIO - I/O error.
94229619809SKoji Sato  *
94329619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
94429619809SKoji Sato  */
nilfs_cpfile_get_stat(struct inode * cpfile,struct nilfs_cpstat * cpstat)94529619809SKoji Sato int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat)
94629619809SKoji Sato {
94729619809SKoji Sato 	struct buffer_head *bh;
94829619809SKoji Sato 	struct nilfs_cpfile_header *header;
94929619809SKoji Sato 	void *kaddr;
95029619809SKoji Sato 	int ret;
95129619809SKoji Sato 
95229619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
95329619809SKoji Sato 
95429619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &bh);
95529619809SKoji Sato 	if (ret < 0)
95629619809SKoji Sato 		goto out_sem;
9577b9c0976SCong Wang 	kaddr = kmap_atomic(bh->b_page);
95829619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
95929619809SKoji Sato 	cpstat->cs_cno = nilfs_mdt_cno(cpfile);
96029619809SKoji Sato 	cpstat->cs_ncps = le64_to_cpu(header->ch_ncheckpoints);
96129619809SKoji Sato 	cpstat->cs_nsss = le64_to_cpu(header->ch_nsnapshots);
9627b9c0976SCong Wang 	kunmap_atomic(kaddr);
96329619809SKoji Sato 	brelse(bh);
96429619809SKoji Sato 
96529619809SKoji Sato  out_sem:
96629619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
96729619809SKoji Sato 	return ret;
96829619809SKoji Sato }
96979739565SRyusuke Konishi 
97079739565SRyusuke Konishi /**
971f1e89c86SRyusuke Konishi  * nilfs_cpfile_read - read or get cpfile inode
972f1e89c86SRyusuke Konishi  * @sb: super block instance
97379739565SRyusuke Konishi  * @cpsize: size of a checkpoint entry
974f1e89c86SRyusuke Konishi  * @raw_inode: on-disk cpfile inode
975f1e89c86SRyusuke Konishi  * @inodep: buffer to store the inode
97679739565SRyusuke Konishi  */
nilfs_cpfile_read(struct super_block * sb,size_t cpsize,struct nilfs_inode * raw_inode,struct inode ** inodep)977f1e89c86SRyusuke Konishi int nilfs_cpfile_read(struct super_block *sb, size_t cpsize,
978f1e89c86SRyusuke Konishi 		      struct nilfs_inode *raw_inode, struct inode **inodep)
97979739565SRyusuke Konishi {
98079739565SRyusuke Konishi 	struct inode *cpfile;
981f1e89c86SRyusuke Konishi 	int err;
98279739565SRyusuke Konishi 
9830ec060d1SRyusuke Konishi 	if (cpsize > sb->s_blocksize) {
984a1d0747aSJoe Perches 		nilfs_err(sb, "too large checkpoint size: %zu bytes", cpsize);
9850ec060d1SRyusuke Konishi 		return -EINVAL;
9860ec060d1SRyusuke Konishi 	} else if (cpsize < NILFS_MIN_CHECKPOINT_SIZE) {
987a1d0747aSJoe Perches 		nilfs_err(sb, "too small checkpoint size: %zu bytes", cpsize);
9880ec060d1SRyusuke Konishi 		return -EINVAL;
9890ec060d1SRyusuke Konishi 	}
9900ec060d1SRyusuke Konishi 
991f1e89c86SRyusuke Konishi 	cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO);
992f1e89c86SRyusuke Konishi 	if (unlikely(!cpfile))
993f1e89c86SRyusuke Konishi 		return -ENOMEM;
994f1e89c86SRyusuke Konishi 	if (!(cpfile->i_state & I_NEW))
995f1e89c86SRyusuke Konishi 		goto out;
996f1e89c86SRyusuke Konishi 
997f1e89c86SRyusuke Konishi 	err = nilfs_mdt_init(cpfile, NILFS_MDT_GFP, 0);
998f1e89c86SRyusuke Konishi 	if (err)
999f1e89c86SRyusuke Konishi 		goto failed;
1000f1e89c86SRyusuke Konishi 
100179739565SRyusuke Konishi 	nilfs_mdt_set_entry_size(cpfile, cpsize,
100279739565SRyusuke Konishi 				 sizeof(struct nilfs_cpfile_header));
1003f1e89c86SRyusuke Konishi 
1004f1e89c86SRyusuke Konishi 	err = nilfs_read_inode_common(cpfile, raw_inode);
1005f1e89c86SRyusuke Konishi 	if (err)
1006f1e89c86SRyusuke Konishi 		goto failed;
1007f1e89c86SRyusuke Konishi 
1008f1e89c86SRyusuke Konishi 	unlock_new_inode(cpfile);
1009f1e89c86SRyusuke Konishi  out:
1010f1e89c86SRyusuke Konishi 	*inodep = cpfile;
1011f1e89c86SRyusuke Konishi 	return 0;
1012f1e89c86SRyusuke Konishi  failed:
1013f1e89c86SRyusuke Konishi 	iget_failed(cpfile);
1014f1e89c86SRyusuke Konishi 	return err;
101579739565SRyusuke Konishi }
1016