xref: /linux/fs/nilfs2/cpfile.c (revision 7856a565416e0cf091f825b0e25c7a1b7abb650e)
1ae98043fSRyusuke Konishi // SPDX-License-Identifier: GPL-2.0+
229619809SKoji Sato /*
394ee1d91SRyusuke 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
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
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 
31adc2c8d0SThorsten Blum 	tcno = div64_ul(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
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 
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
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 
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
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
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 *
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 *
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 
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 
128*d07d8ba4SRyusuke Konishi static int nilfs_cpfile_get_header_block(struct inode *cpfile,
12929619809SKoji Sato 					 struct buffer_head **bhp)
13029619809SKoji Sato {
131*d07d8ba4SRyusuke Konishi 	int err = nilfs_mdt_get_block(cpfile, 0, 0, NULL, bhp);
132*d07d8ba4SRyusuke Konishi 
133*d07d8ba4SRyusuke Konishi 	if (unlikely(err == -ENOENT)) {
134*d07d8ba4SRyusuke Konishi 		nilfs_error(cpfile->i_sb,
135*d07d8ba4SRyusuke Konishi 			    "missing header block in checkpoint metadata");
136*d07d8ba4SRyusuke Konishi 		err = -EIO;
137*d07d8ba4SRyusuke Konishi 	}
138*d07d8ba4SRyusuke Konishi 	return err;
13929619809SKoji Sato }
14029619809SKoji Sato 
14129619809SKoji Sato static inline int nilfs_cpfile_get_checkpoint_block(struct inode *cpfile,
14229619809SKoji Sato 						    __u64 cno,
14329619809SKoji Sato 						    int create,
14429619809SKoji Sato 						    struct buffer_head **bhp)
14529619809SKoji Sato {
14629619809SKoji Sato 	return nilfs_mdt_get_block(cpfile,
14729619809SKoji Sato 				   nilfs_cpfile_get_blkoff(cpfile, cno),
14829619809SKoji Sato 				   create, nilfs_cpfile_block_init, bhp);
14929619809SKoji Sato }
15029619809SKoji Sato 
15153a2c3bdSRyusuke Konishi /**
15253a2c3bdSRyusuke Konishi  * nilfs_cpfile_find_checkpoint_block - find and get a buffer on cpfile
15353a2c3bdSRyusuke Konishi  * @cpfile: inode of cpfile
15453a2c3bdSRyusuke Konishi  * @start_cno: start checkpoint number (inclusive)
15553a2c3bdSRyusuke Konishi  * @end_cno: end checkpoint number (inclusive)
15653a2c3bdSRyusuke Konishi  * @cnop: place to store the next checkpoint number
15753a2c3bdSRyusuke Konishi  * @bhp: place to store a pointer to buffer_head struct
15853a2c3bdSRyusuke Konishi  *
15953a2c3bdSRyusuke Konishi  * Return Value: On success, it returns 0. On error, the following negative
16053a2c3bdSRyusuke Konishi  * error code is returned.
16153a2c3bdSRyusuke Konishi  *
16253a2c3bdSRyusuke Konishi  * %-ENOMEM - Insufficient memory available.
16353a2c3bdSRyusuke Konishi  *
16453a2c3bdSRyusuke Konishi  * %-EIO - I/O error
16553a2c3bdSRyusuke Konishi  *
16653a2c3bdSRyusuke Konishi  * %-ENOENT - no block exists in the range.
16753a2c3bdSRyusuke Konishi  */
16853a2c3bdSRyusuke Konishi static int nilfs_cpfile_find_checkpoint_block(struct inode *cpfile,
16953a2c3bdSRyusuke Konishi 					      __u64 start_cno, __u64 end_cno,
17053a2c3bdSRyusuke Konishi 					      __u64 *cnop,
17153a2c3bdSRyusuke Konishi 					      struct buffer_head **bhp)
17253a2c3bdSRyusuke Konishi {
17353a2c3bdSRyusuke Konishi 	unsigned long start, end, blkoff;
17453a2c3bdSRyusuke Konishi 	int ret;
17553a2c3bdSRyusuke Konishi 
17653a2c3bdSRyusuke Konishi 	if (unlikely(start_cno > end_cno))
17753a2c3bdSRyusuke Konishi 		return -ENOENT;
17853a2c3bdSRyusuke Konishi 
17953a2c3bdSRyusuke Konishi 	start = nilfs_cpfile_get_blkoff(cpfile, start_cno);
18053a2c3bdSRyusuke Konishi 	end = nilfs_cpfile_get_blkoff(cpfile, end_cno);
18153a2c3bdSRyusuke Konishi 
18253a2c3bdSRyusuke Konishi 	ret = nilfs_mdt_find_block(cpfile, start, end, &blkoff, bhp);
18353a2c3bdSRyusuke Konishi 	if (!ret)
18453a2c3bdSRyusuke Konishi 		*cnop = (blkoff == start) ? start_cno :
18553a2c3bdSRyusuke Konishi 			nilfs_cpfile_first_checkpoint_in_block(cpfile, blkoff);
18653a2c3bdSRyusuke Konishi 	return ret;
18753a2c3bdSRyusuke Konishi }
18853a2c3bdSRyusuke Konishi 
18929619809SKoji Sato static inline int nilfs_cpfile_delete_checkpoint_block(struct inode *cpfile,
19029619809SKoji Sato 						       __u64 cno)
19129619809SKoji Sato {
19229619809SKoji Sato 	return nilfs_mdt_delete_block(cpfile,
19329619809SKoji Sato 				      nilfs_cpfile_get_blkoff(cpfile, cno));
19429619809SKoji Sato }
19529619809SKoji Sato 
19629619809SKoji Sato /**
1971244a6d7SRyusuke Konishi  * nilfs_cpfile_read_checkpoint - read a checkpoint entry in cpfile
1981244a6d7SRyusuke Konishi  * @cpfile: checkpoint file inode
1991244a6d7SRyusuke Konishi  * @cno:    number of checkpoint entry to read
2001244a6d7SRyusuke Konishi  * @root:   nilfs root object
2011244a6d7SRyusuke Konishi  * @ifile:  ifile's inode to read and attach to @root
2021244a6d7SRyusuke Konishi  *
2031244a6d7SRyusuke Konishi  * This function imports checkpoint information from the checkpoint file and
2041244a6d7SRyusuke Konishi  * stores it to the inode file given by @ifile and the nilfs root object
2051244a6d7SRyusuke Konishi  * given by @root.
2061244a6d7SRyusuke Konishi  *
2071244a6d7SRyusuke Konishi  * Return: 0 on success, or the following negative error code on failure.
2081244a6d7SRyusuke Konishi  * * %-EINVAL	- Invalid checkpoint.
2091244a6d7SRyusuke Konishi  * * %-ENOMEM	- Insufficient memory available.
2101244a6d7SRyusuke Konishi  * * %-EIO	- I/O error (including metadata corruption).
2111244a6d7SRyusuke Konishi  */
2121244a6d7SRyusuke Konishi int nilfs_cpfile_read_checkpoint(struct inode *cpfile, __u64 cno,
2131244a6d7SRyusuke Konishi 				 struct nilfs_root *root, struct inode *ifile)
2141244a6d7SRyusuke Konishi {
2151244a6d7SRyusuke Konishi 	struct buffer_head *cp_bh;
2161244a6d7SRyusuke Konishi 	struct nilfs_checkpoint *cp;
2171244a6d7SRyusuke Konishi 	void *kaddr;
2181244a6d7SRyusuke Konishi 	int ret;
2191244a6d7SRyusuke Konishi 
2201244a6d7SRyusuke Konishi 	if (cno < 1 || cno > nilfs_mdt_cno(cpfile))
2211244a6d7SRyusuke Konishi 		return -EINVAL;
2221244a6d7SRyusuke Konishi 
2231244a6d7SRyusuke Konishi 	down_read(&NILFS_MDT(cpfile)->mi_sem);
2241244a6d7SRyusuke Konishi 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
2251244a6d7SRyusuke Konishi 	if (unlikely(ret < 0)) {
2261244a6d7SRyusuke Konishi 		if (ret == -ENOENT)
2271244a6d7SRyusuke Konishi 			ret = -EINVAL;
2281244a6d7SRyusuke Konishi 		goto out_sem;
2291244a6d7SRyusuke Konishi 	}
2301244a6d7SRyusuke Konishi 
2311244a6d7SRyusuke Konishi 	kaddr = kmap_local_page(cp_bh->b_page);
2321244a6d7SRyusuke Konishi 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
2331244a6d7SRyusuke Konishi 	if (nilfs_checkpoint_invalid(cp)) {
2341244a6d7SRyusuke Konishi 		ret = -EINVAL;
2351244a6d7SRyusuke Konishi 		goto put_cp;
2361244a6d7SRyusuke Konishi 	}
2371244a6d7SRyusuke Konishi 
2381244a6d7SRyusuke Konishi 	ret = nilfs_read_inode_common(ifile, &cp->cp_ifile_inode);
2391244a6d7SRyusuke Konishi 	if (unlikely(ret)) {
2401244a6d7SRyusuke Konishi 		/*
2411244a6d7SRyusuke Konishi 		 * Since this inode is on a checkpoint entry, treat errors
2421244a6d7SRyusuke Konishi 		 * as metadata corruption.
2431244a6d7SRyusuke Konishi 		 */
2441244a6d7SRyusuke Konishi 		nilfs_err(cpfile->i_sb,
2451244a6d7SRyusuke Konishi 			  "ifile inode (checkpoint number=%llu) corrupted",
2461244a6d7SRyusuke Konishi 			  (unsigned long long)cno);
2471244a6d7SRyusuke Konishi 		ret = -EIO;
2481244a6d7SRyusuke Konishi 		goto put_cp;
2491244a6d7SRyusuke Konishi 	}
2501244a6d7SRyusuke Konishi 
2511244a6d7SRyusuke Konishi 	/* Configure the nilfs root object */
2521244a6d7SRyusuke Konishi 	atomic64_set(&root->inodes_count, le64_to_cpu(cp->cp_inodes_count));
2531244a6d7SRyusuke Konishi 	atomic64_set(&root->blocks_count, le64_to_cpu(cp->cp_blocks_count));
2541244a6d7SRyusuke Konishi 	root->ifile = ifile;
2551244a6d7SRyusuke Konishi 
2561244a6d7SRyusuke Konishi put_cp:
2571244a6d7SRyusuke Konishi 	kunmap_local(kaddr);
2581244a6d7SRyusuke Konishi 	brelse(cp_bh);
2591244a6d7SRyusuke Konishi out_sem:
2601244a6d7SRyusuke Konishi 	up_read(&NILFS_MDT(cpfile)->mi_sem);
2611244a6d7SRyusuke Konishi 	return ret;
2621244a6d7SRyusuke Konishi }
2631244a6d7SRyusuke Konishi 
2641244a6d7SRyusuke Konishi /**
265d37db936SRyusuke Konishi  * nilfs_cpfile_create_checkpoint - create a checkpoint entry on cpfile
266d37db936SRyusuke Konishi  * @cpfile: checkpoint file inode
267d37db936SRyusuke Konishi  * @cno:    number of checkpoint to set up
268d37db936SRyusuke Konishi  *
269d37db936SRyusuke Konishi  * This function creates a checkpoint with the number specified by @cno on
270d37db936SRyusuke Konishi  * cpfile.  If the specified checkpoint entry already exists due to a past
271d37db936SRyusuke Konishi  * failure, it will be reused without returning an error.
272d37db936SRyusuke Konishi  * In either case, the buffer of the block containing the checkpoint entry
273d37db936SRyusuke Konishi  * and the cpfile inode are made dirty for inclusion in the write log.
274d37db936SRyusuke Konishi  *
275d37db936SRyusuke Konishi  * Return: 0 on success, or the following negative error code on failure.
276d37db936SRyusuke Konishi  * * %-ENOMEM	- Insufficient memory available.
277d37db936SRyusuke Konishi  * * %-EIO	- I/O error (including metadata corruption).
278d37db936SRyusuke Konishi  * * %-EROFS	- Read only filesystem
279d37db936SRyusuke Konishi  */
280d37db936SRyusuke Konishi int nilfs_cpfile_create_checkpoint(struct inode *cpfile, __u64 cno)
281d37db936SRyusuke Konishi {
282d37db936SRyusuke Konishi 	struct buffer_head *header_bh, *cp_bh;
283d37db936SRyusuke Konishi 	struct nilfs_cpfile_header *header;
284d37db936SRyusuke Konishi 	struct nilfs_checkpoint *cp;
285d37db936SRyusuke Konishi 	void *kaddr;
286d37db936SRyusuke Konishi 	int ret;
287d37db936SRyusuke Konishi 
288d37db936SRyusuke Konishi 	if (WARN_ON_ONCE(cno < 1))
289d37db936SRyusuke Konishi 		return -EIO;
290d37db936SRyusuke Konishi 
291d37db936SRyusuke Konishi 	down_write(&NILFS_MDT(cpfile)->mi_sem);
292d37db936SRyusuke Konishi 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
293*d07d8ba4SRyusuke Konishi 	if (unlikely(ret < 0))
294d37db936SRyusuke Konishi 		goto out_sem;
295*d07d8ba4SRyusuke Konishi 
296d37db936SRyusuke Konishi 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 1, &cp_bh);
297d37db936SRyusuke Konishi 	if (unlikely(ret < 0))
298d37db936SRyusuke Konishi 		goto out_header;
299d37db936SRyusuke Konishi 
300d37db936SRyusuke Konishi 	kaddr = kmap_local_page(cp_bh->b_page);
301d37db936SRyusuke Konishi 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
302d37db936SRyusuke Konishi 	if (nilfs_checkpoint_invalid(cp)) {
303d37db936SRyusuke Konishi 		/* a newly-created checkpoint */
304d37db936SRyusuke Konishi 		nilfs_checkpoint_clear_invalid(cp);
305d37db936SRyusuke Konishi 		if (!nilfs_cpfile_is_in_first(cpfile, cno))
306d37db936SRyusuke Konishi 			nilfs_cpfile_block_add_valid_checkpoints(cpfile, cp_bh,
307d37db936SRyusuke Konishi 								 kaddr, 1);
308d37db936SRyusuke Konishi 		kunmap_local(kaddr);
309d37db936SRyusuke Konishi 
310d37db936SRyusuke Konishi 		kaddr = kmap_local_page(header_bh->b_page);
311d37db936SRyusuke Konishi 		header = nilfs_cpfile_block_get_header(cpfile, header_bh,
312d37db936SRyusuke Konishi 						       kaddr);
313d37db936SRyusuke Konishi 		le64_add_cpu(&header->ch_ncheckpoints, 1);
314d37db936SRyusuke Konishi 		kunmap_local(kaddr);
315d37db936SRyusuke Konishi 		mark_buffer_dirty(header_bh);
316d37db936SRyusuke Konishi 	} else {
317d37db936SRyusuke Konishi 		kunmap_local(kaddr);
318d37db936SRyusuke Konishi 	}
319d37db936SRyusuke Konishi 
320d37db936SRyusuke Konishi 	/* Force the buffer and the inode to become dirty */
321d37db936SRyusuke Konishi 	mark_buffer_dirty(cp_bh);
322d37db936SRyusuke Konishi 	brelse(cp_bh);
323d37db936SRyusuke Konishi 	nilfs_mdt_mark_dirty(cpfile);
324d37db936SRyusuke Konishi 
325d37db936SRyusuke Konishi out_header:
326d37db936SRyusuke Konishi 	brelse(header_bh);
327d37db936SRyusuke Konishi 
328d37db936SRyusuke Konishi out_sem:
329d37db936SRyusuke Konishi 	up_write(&NILFS_MDT(cpfile)->mi_sem);
330d37db936SRyusuke Konishi 	return ret;
331d37db936SRyusuke Konishi }
332d37db936SRyusuke Konishi 
333d37db936SRyusuke Konishi /**
334cce259b4SRyusuke Konishi  * nilfs_cpfile_finalize_checkpoint - fill in a checkpoint entry in cpfile
335cce259b4SRyusuke Konishi  * @cpfile: checkpoint file inode
336cce259b4SRyusuke Konishi  * @cno:    checkpoint number
337cce259b4SRyusuke Konishi  * @root:   nilfs root object
338cce259b4SRyusuke Konishi  * @blkinc: number of blocks added by this checkpoint
339cce259b4SRyusuke Konishi  * @ctime:  checkpoint creation time
340cce259b4SRyusuke Konishi  * @minor:  minor checkpoint flag
341cce259b4SRyusuke Konishi  *
342cce259b4SRyusuke Konishi  * This function completes the checkpoint entry numbered by @cno in the
343cce259b4SRyusuke Konishi  * cpfile with the data given by the arguments @root, @blkinc, @ctime, and
344cce259b4SRyusuke Konishi  * @minor.
345cce259b4SRyusuke Konishi  *
346cce259b4SRyusuke Konishi  * Return: 0 on success, or the following negative error code on failure.
347cce259b4SRyusuke Konishi  * * %-ENOMEM	- Insufficient memory available.
348cce259b4SRyusuke Konishi  * * %-EIO	- I/O error (including metadata corruption).
349cce259b4SRyusuke Konishi  */
350cce259b4SRyusuke Konishi int nilfs_cpfile_finalize_checkpoint(struct inode *cpfile, __u64 cno,
351cce259b4SRyusuke Konishi 				     struct nilfs_root *root, __u64 blkinc,
352cce259b4SRyusuke Konishi 				     time64_t ctime, bool minor)
353cce259b4SRyusuke Konishi {
354cce259b4SRyusuke Konishi 	struct buffer_head *cp_bh;
355cce259b4SRyusuke Konishi 	struct nilfs_checkpoint *cp;
356cce259b4SRyusuke Konishi 	void *kaddr;
357cce259b4SRyusuke Konishi 	int ret;
358cce259b4SRyusuke Konishi 
359cce259b4SRyusuke Konishi 	if (WARN_ON_ONCE(cno < 1))
360cce259b4SRyusuke Konishi 		return -EIO;
361cce259b4SRyusuke Konishi 
362cce259b4SRyusuke Konishi 	down_write(&NILFS_MDT(cpfile)->mi_sem);
363cce259b4SRyusuke Konishi 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
364cce259b4SRyusuke Konishi 	if (unlikely(ret < 0)) {
365cce259b4SRyusuke Konishi 		if (ret == -ENOENT)
366cce259b4SRyusuke Konishi 			goto error;
367cce259b4SRyusuke Konishi 		goto out_sem;
368cce259b4SRyusuke Konishi 	}
369cce259b4SRyusuke Konishi 
370cce259b4SRyusuke Konishi 	kaddr = kmap_local_page(cp_bh->b_page);
371cce259b4SRyusuke Konishi 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
372cce259b4SRyusuke Konishi 	if (unlikely(nilfs_checkpoint_invalid(cp))) {
373cce259b4SRyusuke Konishi 		kunmap_local(kaddr);
374cce259b4SRyusuke Konishi 		brelse(cp_bh);
375cce259b4SRyusuke Konishi 		goto error;
376cce259b4SRyusuke Konishi 	}
377cce259b4SRyusuke Konishi 
378cce259b4SRyusuke Konishi 	cp->cp_snapshot_list.ssl_next = 0;
379cce259b4SRyusuke Konishi 	cp->cp_snapshot_list.ssl_prev = 0;
380cce259b4SRyusuke Konishi 	cp->cp_inodes_count = cpu_to_le64(atomic64_read(&root->inodes_count));
381cce259b4SRyusuke Konishi 	cp->cp_blocks_count = cpu_to_le64(atomic64_read(&root->blocks_count));
382cce259b4SRyusuke Konishi 	cp->cp_nblk_inc = cpu_to_le64(blkinc);
383cce259b4SRyusuke Konishi 	cp->cp_create = cpu_to_le64(ctime);
384cce259b4SRyusuke Konishi 	cp->cp_cno = cpu_to_le64(cno);
385cce259b4SRyusuke Konishi 
386cce259b4SRyusuke Konishi 	if (minor)
387cce259b4SRyusuke Konishi 		nilfs_checkpoint_set_minor(cp);
388cce259b4SRyusuke Konishi 	else
389cce259b4SRyusuke Konishi 		nilfs_checkpoint_clear_minor(cp);
390cce259b4SRyusuke Konishi 
391cce259b4SRyusuke Konishi 	nilfs_write_inode_common(root->ifile, &cp->cp_ifile_inode);
392cce259b4SRyusuke Konishi 	nilfs_bmap_write(NILFS_I(root->ifile)->i_bmap, &cp->cp_ifile_inode);
393cce259b4SRyusuke Konishi 
394cce259b4SRyusuke Konishi 	kunmap_local(kaddr);
395cce259b4SRyusuke Konishi 	brelse(cp_bh);
396cce259b4SRyusuke Konishi out_sem:
397cce259b4SRyusuke Konishi 	up_write(&NILFS_MDT(cpfile)->mi_sem);
398cce259b4SRyusuke Konishi 	return ret;
399cce259b4SRyusuke Konishi 
400cce259b4SRyusuke Konishi error:
401cce259b4SRyusuke Konishi 	nilfs_error(cpfile->i_sb,
402cce259b4SRyusuke Konishi 		    "checkpoint finalization failed due to metadata corruption.");
403cce259b4SRyusuke Konishi 	ret = -EIO;
404cce259b4SRyusuke Konishi 	goto out_sem;
405cce259b4SRyusuke Konishi }
406cce259b4SRyusuke Konishi 
407cce259b4SRyusuke Konishi /**
40829619809SKoji Sato  * nilfs_cpfile_delete_checkpoints - delete checkpoints
40929619809SKoji Sato  * @cpfile: inode of checkpoint file
41029619809SKoji Sato  * @start: start checkpoint number
411312f79c4SLu Jialin  * @end: end checkpoint number
41229619809SKoji Sato  *
41329619809SKoji Sato  * Description: nilfs_cpfile_delete_checkpoints() deletes the checkpoints in
41429619809SKoji Sato  * the period from @start to @end, excluding @end itself. The checkpoints
41529619809SKoji Sato  * which have been already deleted are ignored.
41629619809SKoji Sato  *
41729619809SKoji Sato  * Return Value: On success, 0 is returned. On error, one of the following
41829619809SKoji Sato  * negative error codes is returned.
41929619809SKoji Sato  *
42029619809SKoji Sato  * %-EIO - I/O error.
42129619809SKoji Sato  *
42229619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
42329619809SKoji Sato  *
42429619809SKoji Sato  * %-EINVAL - invalid checkpoints.
42529619809SKoji Sato  */
42629619809SKoji Sato int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
42729619809SKoji Sato 				    __u64 start,
42829619809SKoji Sato 				    __u64 end)
42929619809SKoji Sato {
43029619809SKoji Sato 	struct buffer_head *header_bh, *cp_bh;
43129619809SKoji Sato 	struct nilfs_cpfile_header *header;
43229619809SKoji Sato 	struct nilfs_checkpoint *cp;
43329619809SKoji Sato 	size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
43429619809SKoji Sato 	__u64 cno;
43529619809SKoji Sato 	void *kaddr;
43629619809SKoji Sato 	unsigned long tnicps;
437fe0627e7SRyusuke Konishi 	int ret, ncps, nicps, nss, count, i;
43829619809SKoji Sato 
4391f5abe7eSRyusuke Konishi 	if (unlikely(start == 0 || start > end)) {
440a1d0747aSJoe Perches 		nilfs_err(cpfile->i_sb,
441feee880fSRyusuke Konishi 			  "cannot delete checkpoints: invalid range [%llu, %llu)",
4421f5abe7eSRyusuke Konishi 			  (unsigned long long)start, (unsigned long long)end);
4431f5abe7eSRyusuke Konishi 		return -EINVAL;
44429619809SKoji Sato 	}
44529619809SKoji Sato 
44629619809SKoji Sato 	down_write(&NILFS_MDT(cpfile)->mi_sem);
44729619809SKoji Sato 
44829619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
44929619809SKoji Sato 	if (ret < 0)
45029619809SKoji Sato 		goto out_sem;
45129619809SKoji Sato 	tnicps = 0;
452fe0627e7SRyusuke Konishi 	nss = 0;
45329619809SKoji Sato 
45429619809SKoji Sato 	for (cno = start; cno < end; cno += ncps) {
45529619809SKoji Sato 		ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end);
45629619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
45729619809SKoji Sato 		if (ret < 0) {
45829619809SKoji Sato 			if (ret != -ENOENT)
459d9a0a345SJiro SEKIBA 				break;
46029619809SKoji Sato 			/* skip hole */
46129619809SKoji Sato 			ret = 0;
46229619809SKoji Sato 			continue;
46329619809SKoji Sato 		}
46429619809SKoji Sato 
4655eccc067SRyusuke Konishi 		kaddr = kmap_local_page(cp_bh->b_page);
46629619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(
46729619809SKoji Sato 			cpfile, cno, cp_bh, kaddr);
46829619809SKoji Sato 		nicps = 0;
46929619809SKoji Sato 		for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {
470fe0627e7SRyusuke Konishi 			if (nilfs_checkpoint_snapshot(cp)) {
471fe0627e7SRyusuke Konishi 				nss++;
472fe0627e7SRyusuke Konishi 			} else if (!nilfs_checkpoint_invalid(cp)) {
47329619809SKoji Sato 				nilfs_checkpoint_set_invalid(cp);
47429619809SKoji Sato 				nicps++;
47529619809SKoji Sato 			}
47629619809SKoji Sato 		}
47729619809SKoji Sato 		if (nicps > 0) {
47829619809SKoji Sato 			tnicps += nicps;
4795fc7b141SRyusuke Konishi 			mark_buffer_dirty(cp_bh);
48029619809SKoji Sato 			nilfs_mdt_mark_dirty(cpfile);
4815ee58148SJiro SEKIBA 			if (!nilfs_cpfile_is_in_first(cpfile, cno)) {
4825ee58148SJiro SEKIBA 				count =
4835ee58148SJiro SEKIBA 				  nilfs_cpfile_block_sub_valid_checkpoints(
4845ee58148SJiro SEKIBA 						cpfile, cp_bh, kaddr, nicps);
4855ee58148SJiro SEKIBA 				if (count == 0) {
48629619809SKoji Sato 					/* make hole */
4875eccc067SRyusuke Konishi 					kunmap_local(kaddr);
48829619809SKoji Sato 					brelse(cp_bh);
4895ee58148SJiro SEKIBA 					ret =
4905ee58148SJiro SEKIBA 					  nilfs_cpfile_delete_checkpoint_block(
49129619809SKoji Sato 								   cpfile, cno);
49229619809SKoji Sato 					if (ret == 0)
49329619809SKoji Sato 						continue;
494a1d0747aSJoe Perches 					nilfs_err(cpfile->i_sb,
495feee880fSRyusuke Konishi 						  "error %d deleting checkpoint block",
496feee880fSRyusuke Konishi 						  ret);
497d9a0a345SJiro SEKIBA 					break;
49829619809SKoji Sato 				}
49929619809SKoji Sato 			}
5005ee58148SJiro SEKIBA 		}
50129619809SKoji Sato 
5025eccc067SRyusuke Konishi 		kunmap_local(kaddr);
50329619809SKoji Sato 		brelse(cp_bh);
50429619809SKoji Sato 	}
50529619809SKoji Sato 
50629619809SKoji Sato 	if (tnicps > 0) {
5075eccc067SRyusuke Konishi 		kaddr = kmap_local_page(header_bh->b_page);
50829619809SKoji Sato 		header = nilfs_cpfile_block_get_header(cpfile, header_bh,
50929619809SKoji Sato 						       kaddr);
5106c98cd4eSKoji Sato 		le64_add_cpu(&header->ch_ncheckpoints, -(u64)tnicps);
5115fc7b141SRyusuke Konishi 		mark_buffer_dirty(header_bh);
51229619809SKoji Sato 		nilfs_mdt_mark_dirty(cpfile);
5135eccc067SRyusuke Konishi 		kunmap_local(kaddr);
51429619809SKoji Sato 	}
51562013ab5SRyusuke Konishi 
51629619809SKoji Sato 	brelse(header_bh);
517fe0627e7SRyusuke Konishi 	if (nss > 0)
518fe0627e7SRyusuke Konishi 		ret = -EBUSY;
51929619809SKoji Sato 
52029619809SKoji Sato  out_sem:
52129619809SKoji Sato 	up_write(&NILFS_MDT(cpfile)->mi_sem);
52229619809SKoji Sato 	return ret;
52329619809SKoji Sato }
52429619809SKoji Sato 
52529619809SKoji Sato static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile,
52629619809SKoji Sato 					      struct nilfs_checkpoint *cp,
52729619809SKoji Sato 					      struct nilfs_cpinfo *ci)
52829619809SKoji Sato {
52929619809SKoji Sato 	ci->ci_flags = le32_to_cpu(cp->cp_flags);
53029619809SKoji Sato 	ci->ci_cno = le64_to_cpu(cp->cp_cno);
53129619809SKoji Sato 	ci->ci_create = le64_to_cpu(cp->cp_create);
53229619809SKoji Sato 	ci->ci_nblk_inc = le64_to_cpu(cp->cp_nblk_inc);
53329619809SKoji Sato 	ci->ci_inodes_count = le64_to_cpu(cp->cp_inodes_count);
53429619809SKoji Sato 	ci->ci_blocks_count = le64_to_cpu(cp->cp_blocks_count);
53529619809SKoji Sato 	ci->ci_next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
53629619809SKoji Sato }
53729619809SKoji Sato 
53876068c4fSRyusuke Konishi static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
5390c6c44cbSRyusuke Konishi 					  void *buf, unsigned int cisz,
5400c6c44cbSRyusuke Konishi 					  size_t nci)
54129619809SKoji Sato {
54229619809SKoji Sato 	struct nilfs_checkpoint *cp;
543003ff182SRyusuke Konishi 	struct nilfs_cpinfo *ci = buf;
54429619809SKoji Sato 	struct buffer_head *bh;
54529619809SKoji Sato 	size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
54676068c4fSRyusuke Konishi 	__u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop;
54729619809SKoji Sato 	void *kaddr;
54829619809SKoji Sato 	int n, ret;
54929619809SKoji Sato 	int ncps, i;
55029619809SKoji Sato 
5511f5abe7eSRyusuke Konishi 	if (cno == 0)
5521f5abe7eSRyusuke Konishi 		return -ENOENT; /* checkpoint number 0 is invalid */
55329619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
55429619809SKoji Sato 
55553a2c3bdSRyusuke Konishi 	for (n = 0; n < nci; cno += ncps) {
55653a2c3bdSRyusuke Konishi 		ret = nilfs_cpfile_find_checkpoint_block(
55753a2c3bdSRyusuke Konishi 			cpfile, cno, cur_cno - 1, &cno, &bh);
55829619809SKoji Sato 		if (ret < 0) {
55953a2c3bdSRyusuke Konishi 			if (likely(ret == -ENOENT))
56053a2c3bdSRyusuke Konishi 				break;
56129619809SKoji Sato 			goto out;
56229619809SKoji Sato 		}
56353a2c3bdSRyusuke Konishi 		ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, cur_cno);
56429619809SKoji Sato 
5655eccc067SRyusuke Konishi 		kaddr = kmap_local_page(bh->b_page);
56629619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
56729619809SKoji Sato 		for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) {
568003ff182SRyusuke Konishi 			if (!nilfs_checkpoint_invalid(cp)) {
569003ff182SRyusuke Konishi 				nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp,
570003ff182SRyusuke Konishi 								  ci);
571003ff182SRyusuke Konishi 				ci = (void *)ci + cisz;
572003ff182SRyusuke Konishi 				n++;
573003ff182SRyusuke Konishi 			}
57429619809SKoji Sato 		}
5755eccc067SRyusuke Konishi 		kunmap_local(kaddr);
57629619809SKoji Sato 		brelse(bh);
57729619809SKoji Sato 	}
57829619809SKoji Sato 
57929619809SKoji Sato 	ret = n;
580003ff182SRyusuke Konishi 	if (n > 0) {
581003ff182SRyusuke Konishi 		ci = (void *)ci - cisz;
582003ff182SRyusuke Konishi 		*cnop = ci->ci_cno + 1;
583003ff182SRyusuke Konishi 	}
58429619809SKoji Sato 
58529619809SKoji Sato  out:
58629619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
58729619809SKoji Sato 	return ret;
58829619809SKoji Sato }
58929619809SKoji Sato 
590b028fcfcSRyusuke Konishi static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
5910c6c44cbSRyusuke Konishi 					  void *buf, unsigned int cisz,
5920c6c44cbSRyusuke Konishi 					  size_t nci)
59329619809SKoji Sato {
59429619809SKoji Sato 	struct buffer_head *bh;
59529619809SKoji Sato 	struct nilfs_cpfile_header *header;
59629619809SKoji Sato 	struct nilfs_checkpoint *cp;
597003ff182SRyusuke Konishi 	struct nilfs_cpinfo *ci = buf;
598b028fcfcSRyusuke Konishi 	__u64 curr = *cnop, next;
59929619809SKoji Sato 	unsigned long curr_blkoff, next_blkoff;
60029619809SKoji Sato 	void *kaddr;
6017fa10d20SRyusuke Konishi 	int n = 0, ret;
60229619809SKoji Sato 
60329619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
60429619809SKoji Sato 
605b028fcfcSRyusuke Konishi 	if (curr == 0) {
60629619809SKoji Sato 		ret = nilfs_cpfile_get_header_block(cpfile, &bh);
60729619809SKoji Sato 		if (ret < 0)
60829619809SKoji Sato 			goto out;
6095eccc067SRyusuke Konishi 		kaddr = kmap_local_page(bh->b_page);
61029619809SKoji Sato 		header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
61129619809SKoji Sato 		curr = le64_to_cpu(header->ch_snapshot_list.ssl_next);
6125eccc067SRyusuke Konishi 		kunmap_local(kaddr);
61329619809SKoji Sato 		brelse(bh);
61429619809SKoji Sato 		if (curr == 0) {
61529619809SKoji Sato 			ret = 0;
61629619809SKoji Sato 			goto out;
61729619809SKoji Sato 		}
618b028fcfcSRyusuke Konishi 	} else if (unlikely(curr == ~(__u64)0)) {
619b028fcfcSRyusuke Konishi 		ret = 0;
620b028fcfcSRyusuke Konishi 		goto out;
621b028fcfcSRyusuke Konishi 	}
622b028fcfcSRyusuke Konishi 
62329619809SKoji Sato 	curr_blkoff = nilfs_cpfile_get_blkoff(cpfile, curr);
62429619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr, 0, &bh);
6257fa10d20SRyusuke Konishi 	if (unlikely(ret < 0)) {
6267fa10d20SRyusuke Konishi 		if (ret == -ENOENT)
6277fa10d20SRyusuke Konishi 			ret = 0; /* No snapshots (started from a hole block) */
62829619809SKoji Sato 		goto out;
62929619809SKoji Sato 	}
6305eccc067SRyusuke Konishi 	kaddr = kmap_local_page(bh->b_page);
6317fa10d20SRyusuke Konishi 	while (n < nci) {
6327fa10d20SRyusuke Konishi 		cp = nilfs_cpfile_block_get_checkpoint(cpfile, curr, bh, kaddr);
6337fa10d20SRyusuke Konishi 		curr = ~(__u64)0; /* Terminator */
6347fa10d20SRyusuke Konishi 		if (unlikely(nilfs_checkpoint_invalid(cp) ||
6357fa10d20SRyusuke Konishi 			     !nilfs_checkpoint_snapshot(cp)))
6367fa10d20SRyusuke Konishi 			break;
637003ff182SRyusuke Konishi 		nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, ci);
638003ff182SRyusuke Konishi 		ci = (void *)ci + cisz;
639003ff182SRyusuke Konishi 		n++;
6407fa10d20SRyusuke Konishi 		next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
6417fa10d20SRyusuke Konishi 		if (next == 0)
6427fa10d20SRyusuke Konishi 			break; /* reach end of the snapshot list */
6437fa10d20SRyusuke Konishi 
64429619809SKoji Sato 		next_blkoff = nilfs_cpfile_get_blkoff(cpfile, next);
64529619809SKoji Sato 		if (curr_blkoff != next_blkoff) {
6465eccc067SRyusuke Konishi 			kunmap_local(kaddr);
64729619809SKoji Sato 			brelse(bh);
64829619809SKoji Sato 			ret = nilfs_cpfile_get_checkpoint_block(cpfile, next,
64929619809SKoji Sato 								0, &bh);
6507fa10d20SRyusuke Konishi 			if (unlikely(ret < 0)) {
6517fa10d20SRyusuke Konishi 				WARN_ON(ret == -ENOENT);
65229619809SKoji Sato 				goto out;
6537fa10d20SRyusuke Konishi 			}
6545eccc067SRyusuke Konishi 			kaddr = kmap_local_page(bh->b_page);
65529619809SKoji Sato 		}
65629619809SKoji Sato 		curr = next;
65729619809SKoji Sato 		curr_blkoff = next_blkoff;
65829619809SKoji Sato 	}
6595eccc067SRyusuke Konishi 	kunmap_local(kaddr);
66029619809SKoji Sato 	brelse(bh);
661b028fcfcSRyusuke Konishi 	*cnop = curr;
66229619809SKoji Sato 	ret = n;
66329619809SKoji Sato 
66429619809SKoji Sato  out:
66529619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
66629619809SKoji Sato 	return ret;
66729619809SKoji Sato }
66829619809SKoji Sato 
66929619809SKoji Sato /**
6708e226a0aSRandy Dunlap  * nilfs_cpfile_get_cpinfo - get information on checkpoints
6718e226a0aSRandy Dunlap  * @cpfile: checkpoint file inode
6728e226a0aSRandy Dunlap  * @cnop:   place to pass a starting checkpoint number and receive a
6738e226a0aSRandy Dunlap  *          checkpoint number to continue the search
6748e226a0aSRandy Dunlap  * @mode:   mode of checkpoints that the caller wants to retrieve
6758e226a0aSRandy Dunlap  * @buf:    buffer for storing checkpoints' information
6768e226a0aSRandy Dunlap  * @cisz:   byte size of one checkpoint info item in array
6778e226a0aSRandy Dunlap  * @nci:    number of checkpoint info items to retrieve
6788e226a0aSRandy Dunlap  *
6798e226a0aSRandy Dunlap  * nilfs_cpfile_get_cpinfo() searches for checkpoints in @mode state
6808e226a0aSRandy Dunlap  * starting from the checkpoint number stored in @cnop, and stores
6818e226a0aSRandy Dunlap  * information about found checkpoints in @buf.
6828e226a0aSRandy Dunlap  * The buffer pointed to by @buf must be large enough to store information
6838e226a0aSRandy Dunlap  * for @nci checkpoints.  If at least one checkpoint information is
6848e226a0aSRandy Dunlap  * successfully retrieved, @cnop is updated to point to the checkpoint
6858e226a0aSRandy Dunlap  * number to continue searching.
6868e226a0aSRandy Dunlap  *
6878e226a0aSRandy Dunlap  * Return: Count of checkpoint info items stored in the output buffer on
6888e226a0aSRandy Dunlap  * success, or the following negative error code on failure.
6898e226a0aSRandy Dunlap  * * %-EINVAL	- Invalid checkpoint mode.
6908e226a0aSRandy Dunlap  * * %-ENOMEM	- Insufficient memory available.
6918e226a0aSRandy Dunlap  * * %-EIO	- I/O error (including metadata corruption).
6928e226a0aSRandy Dunlap  * * %-ENOENT	- Invalid checkpoint number specified.
69329619809SKoji Sato  */
694b028fcfcSRyusuke Konishi 
695b028fcfcSRyusuke Konishi ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,
6960c6c44cbSRyusuke Konishi 				void *buf, unsigned int cisz, size_t nci)
69729619809SKoji Sato {
69829619809SKoji Sato 	switch (mode) {
69929619809SKoji Sato 	case NILFS_CHECKPOINT:
700003ff182SRyusuke Konishi 		return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, buf, cisz, nci);
70129619809SKoji Sato 	case NILFS_SNAPSHOT:
702003ff182SRyusuke Konishi 		return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, buf, cisz, nci);
70329619809SKoji Sato 	default:
70429619809SKoji Sato 		return -EINVAL;
70529619809SKoji Sato 	}
70629619809SKoji Sato }
70729619809SKoji Sato 
70829619809SKoji Sato /**
709caaab566SRyusuke Konishi  * nilfs_cpfile_delete_checkpoint - delete a checkpoint
710caaab566SRyusuke Konishi  * @cpfile: checkpoint file inode
711caaab566SRyusuke Konishi  * @cno:    checkpoint number to delete
712caaab566SRyusuke Konishi  *
713caaab566SRyusuke Konishi  * Return: 0 on success, or the following negative error code on failure.
714caaab566SRyusuke Konishi  * * %-EBUSY	- Checkpoint in use (snapshot specified).
715caaab566SRyusuke Konishi  * * %-EIO	- I/O error (including metadata corruption).
716caaab566SRyusuke Konishi  * * %-ENOENT	- No valid checkpoint found.
717caaab566SRyusuke Konishi  * * %-ENOMEM	- Insufficient memory available.
71829619809SKoji Sato  */
71929619809SKoji Sato int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno)
72029619809SKoji Sato {
72129619809SKoji Sato 	struct nilfs_cpinfo ci;
72276068c4fSRyusuke Konishi 	__u64 tcno = cno;
72329619809SKoji Sato 	ssize_t nci;
72429619809SKoji Sato 
725003ff182SRyusuke Konishi 	nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, sizeof(ci), 1);
72629619809SKoji Sato 	if (nci < 0)
72729619809SKoji Sato 		return nci;
72829619809SKoji Sato 	else if (nci == 0 || ci.ci_cno != cno)
72929619809SKoji Sato 		return -ENOENT;
73030c25be7SRyusuke Konishi 	else if (nilfs_cpinfo_snapshot(&ci))
73130c25be7SRyusuke Konishi 		return -EBUSY;
73229619809SKoji Sato 
73329619809SKoji Sato 	return nilfs_cpfile_delete_checkpoints(cpfile, cno, cno + 1);
73429619809SKoji Sato }
73529619809SKoji Sato 
73629619809SKoji Sato static struct nilfs_snapshot_list *
73729619809SKoji Sato nilfs_cpfile_block_get_snapshot_list(const struct inode *cpfile,
73829619809SKoji Sato 				     __u64 cno,
73929619809SKoji Sato 				     struct buffer_head *bh,
74029619809SKoji Sato 				     void *kaddr)
74129619809SKoji Sato {
74229619809SKoji Sato 	struct nilfs_cpfile_header *header;
74329619809SKoji Sato 	struct nilfs_checkpoint *cp;
74429619809SKoji Sato 	struct nilfs_snapshot_list *list;
74529619809SKoji Sato 
74629619809SKoji Sato 	if (cno != 0) {
74729619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
74829619809SKoji Sato 		list = &cp->cp_snapshot_list;
74929619809SKoji Sato 	} else {
75029619809SKoji Sato 		header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
75129619809SKoji Sato 		list = &header->ch_snapshot_list;
75229619809SKoji Sato 	}
75329619809SKoji Sato 	return list;
75429619809SKoji Sato }
75529619809SKoji Sato 
75629619809SKoji Sato static int nilfs_cpfile_set_snapshot(struct inode *cpfile, __u64 cno)
75729619809SKoji Sato {
75829619809SKoji Sato 	struct buffer_head *header_bh, *curr_bh, *prev_bh, *cp_bh;
75929619809SKoji Sato 	struct nilfs_cpfile_header *header;
76029619809SKoji Sato 	struct nilfs_checkpoint *cp;
76129619809SKoji Sato 	struct nilfs_snapshot_list *list;
76229619809SKoji Sato 	__u64 curr, prev;
76329619809SKoji Sato 	unsigned long curr_blkoff, prev_blkoff;
76429619809SKoji Sato 	void *kaddr;
76529619809SKoji Sato 	int ret;
76629619809SKoji Sato 
7671f5abe7eSRyusuke Konishi 	if (cno == 0)
7681f5abe7eSRyusuke Konishi 		return -ENOENT; /* checkpoint number 0 is invalid */
76929619809SKoji Sato 	down_write(&NILFS_MDT(cpfile)->mi_sem);
77029619809SKoji Sato 
77129619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
77229619809SKoji Sato 	if (ret < 0)
77329619809SKoji Sato 		goto out_sem;
7745eccc067SRyusuke Konishi 	kaddr = kmap_local_page(cp_bh->b_page);
77529619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
77629619809SKoji Sato 	if (nilfs_checkpoint_invalid(cp)) {
77729619809SKoji Sato 		ret = -ENOENT;
7785eccc067SRyusuke Konishi 		kunmap_local(kaddr);
77929619809SKoji Sato 		goto out_cp;
78029619809SKoji Sato 	}
78129619809SKoji Sato 	if (nilfs_checkpoint_snapshot(cp)) {
78229619809SKoji Sato 		ret = 0;
7835eccc067SRyusuke Konishi 		kunmap_local(kaddr);
78429619809SKoji Sato 		goto out_cp;
78529619809SKoji Sato 	}
7865eccc067SRyusuke Konishi 	kunmap_local(kaddr);
78729619809SKoji Sato 
78829619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
78929619809SKoji Sato 	if (ret < 0)
79029619809SKoji Sato 		goto out_cp;
7915eccc067SRyusuke Konishi 	kaddr = kmap_local_page(header_bh->b_page);
79229619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
79329619809SKoji Sato 	list = &header->ch_snapshot_list;
79429619809SKoji Sato 	curr_bh = header_bh;
79529619809SKoji Sato 	get_bh(curr_bh);
79629619809SKoji Sato 	curr = 0;
79729619809SKoji Sato 	curr_blkoff = 0;
79829619809SKoji Sato 	prev = le64_to_cpu(list->ssl_prev);
79929619809SKoji Sato 	while (prev > cno) {
80029619809SKoji Sato 		prev_blkoff = nilfs_cpfile_get_blkoff(cpfile, prev);
80129619809SKoji Sato 		curr = prev;
80229619809SKoji Sato 		if (curr_blkoff != prev_blkoff) {
8035eccc067SRyusuke Konishi 			kunmap_local(kaddr);
80429619809SKoji Sato 			brelse(curr_bh);
80529619809SKoji Sato 			ret = nilfs_cpfile_get_checkpoint_block(cpfile, curr,
80629619809SKoji Sato 								0, &curr_bh);
80729619809SKoji Sato 			if (ret < 0)
80829619809SKoji Sato 				goto out_header;
8095eccc067SRyusuke Konishi 			kaddr = kmap_local_page(curr_bh->b_page);
81029619809SKoji Sato 		}
81129619809SKoji Sato 		curr_blkoff = prev_blkoff;
81229619809SKoji Sato 		cp = nilfs_cpfile_block_get_checkpoint(
81329619809SKoji Sato 			cpfile, curr, curr_bh, kaddr);
81429619809SKoji Sato 		list = &cp->cp_snapshot_list;
81529619809SKoji Sato 		prev = le64_to_cpu(list->ssl_prev);
81629619809SKoji Sato 	}
8175eccc067SRyusuke Konishi 	kunmap_local(kaddr);
81829619809SKoji Sato 
81929619809SKoji Sato 	if (prev != 0) {
82029619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,
82129619809SKoji Sato 							&prev_bh);
82229619809SKoji Sato 		if (ret < 0)
82329619809SKoji Sato 			goto out_curr;
82429619809SKoji Sato 	} else {
82529619809SKoji Sato 		prev_bh = header_bh;
82629619809SKoji Sato 		get_bh(prev_bh);
82729619809SKoji Sato 	}
82829619809SKoji Sato 
8295eccc067SRyusuke Konishi 	kaddr = kmap_local_page(curr_bh->b_page);
83029619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
83129619809SKoji Sato 		cpfile, curr, curr_bh, kaddr);
83229619809SKoji Sato 	list->ssl_prev = cpu_to_le64(cno);
8335eccc067SRyusuke Konishi 	kunmap_local(kaddr);
83429619809SKoji Sato 
8355eccc067SRyusuke Konishi 	kaddr = kmap_local_page(cp_bh->b_page);
83629619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
83729619809SKoji Sato 	cp->cp_snapshot_list.ssl_next = cpu_to_le64(curr);
83829619809SKoji Sato 	cp->cp_snapshot_list.ssl_prev = cpu_to_le64(prev);
83929619809SKoji Sato 	nilfs_checkpoint_set_snapshot(cp);
8405eccc067SRyusuke Konishi 	kunmap_local(kaddr);
84129619809SKoji Sato 
8425eccc067SRyusuke Konishi 	kaddr = kmap_local_page(prev_bh->b_page);
84329619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
84429619809SKoji Sato 		cpfile, prev, prev_bh, kaddr);
84529619809SKoji Sato 	list->ssl_next = cpu_to_le64(cno);
8465eccc067SRyusuke Konishi 	kunmap_local(kaddr);
84729619809SKoji Sato 
8485eccc067SRyusuke Konishi 	kaddr = kmap_local_page(header_bh->b_page);
84929619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
85029619809SKoji Sato 	le64_add_cpu(&header->ch_nsnapshots, 1);
8515eccc067SRyusuke Konishi 	kunmap_local(kaddr);
85229619809SKoji Sato 
8535fc7b141SRyusuke Konishi 	mark_buffer_dirty(prev_bh);
8545fc7b141SRyusuke Konishi 	mark_buffer_dirty(curr_bh);
8555fc7b141SRyusuke Konishi 	mark_buffer_dirty(cp_bh);
8565fc7b141SRyusuke Konishi 	mark_buffer_dirty(header_bh);
85729619809SKoji Sato 	nilfs_mdt_mark_dirty(cpfile);
85829619809SKoji Sato 
85929619809SKoji Sato 	brelse(prev_bh);
86029619809SKoji Sato 
86129619809SKoji Sato  out_curr:
86229619809SKoji Sato 	brelse(curr_bh);
86329619809SKoji Sato 
86429619809SKoji Sato  out_header:
86529619809SKoji Sato 	brelse(header_bh);
86629619809SKoji Sato 
86729619809SKoji Sato  out_cp:
86829619809SKoji Sato 	brelse(cp_bh);
86929619809SKoji Sato 
87029619809SKoji Sato  out_sem:
87129619809SKoji Sato 	up_write(&NILFS_MDT(cpfile)->mi_sem);
87229619809SKoji Sato 	return ret;
87329619809SKoji Sato }
87429619809SKoji Sato 
87529619809SKoji Sato static int nilfs_cpfile_clear_snapshot(struct inode *cpfile, __u64 cno)
87629619809SKoji Sato {
87729619809SKoji Sato 	struct buffer_head *header_bh, *next_bh, *prev_bh, *cp_bh;
87829619809SKoji Sato 	struct nilfs_cpfile_header *header;
87929619809SKoji Sato 	struct nilfs_checkpoint *cp;
88029619809SKoji Sato 	struct nilfs_snapshot_list *list;
88129619809SKoji Sato 	__u64 next, prev;
88229619809SKoji Sato 	void *kaddr;
88329619809SKoji Sato 	int ret;
88429619809SKoji Sato 
8851f5abe7eSRyusuke Konishi 	if (cno == 0)
8861f5abe7eSRyusuke Konishi 		return -ENOENT; /* checkpoint number 0 is invalid */
88729619809SKoji Sato 	down_write(&NILFS_MDT(cpfile)->mi_sem);
88829619809SKoji Sato 
88929619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &cp_bh);
89029619809SKoji Sato 	if (ret < 0)
89129619809SKoji Sato 		goto out_sem;
8925eccc067SRyusuke Konishi 	kaddr = kmap_local_page(cp_bh->b_page);
89329619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
89429619809SKoji Sato 	if (nilfs_checkpoint_invalid(cp)) {
89529619809SKoji Sato 		ret = -ENOENT;
8965eccc067SRyusuke Konishi 		kunmap_local(kaddr);
89729619809SKoji Sato 		goto out_cp;
89829619809SKoji Sato 	}
89929619809SKoji Sato 	if (!nilfs_checkpoint_snapshot(cp)) {
90029619809SKoji Sato 		ret = 0;
9015eccc067SRyusuke Konishi 		kunmap_local(kaddr);
90229619809SKoji Sato 		goto out_cp;
90329619809SKoji Sato 	}
90429619809SKoji Sato 
90529619809SKoji Sato 	list = &cp->cp_snapshot_list;
90629619809SKoji Sato 	next = le64_to_cpu(list->ssl_next);
90729619809SKoji Sato 	prev = le64_to_cpu(list->ssl_prev);
9085eccc067SRyusuke Konishi 	kunmap_local(kaddr);
90929619809SKoji Sato 
91029619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &header_bh);
91129619809SKoji Sato 	if (ret < 0)
91229619809SKoji Sato 		goto out_cp;
91329619809SKoji Sato 	if (next != 0) {
91429619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, next, 0,
91529619809SKoji Sato 							&next_bh);
91629619809SKoji Sato 		if (ret < 0)
91729619809SKoji Sato 			goto out_header;
91829619809SKoji Sato 	} else {
91929619809SKoji Sato 		next_bh = header_bh;
92029619809SKoji Sato 		get_bh(next_bh);
92129619809SKoji Sato 	}
92229619809SKoji Sato 	if (prev != 0) {
92329619809SKoji Sato 		ret = nilfs_cpfile_get_checkpoint_block(cpfile, prev, 0,
92429619809SKoji Sato 							&prev_bh);
92529619809SKoji Sato 		if (ret < 0)
92629619809SKoji Sato 			goto out_next;
92729619809SKoji Sato 	} else {
92829619809SKoji Sato 		prev_bh = header_bh;
92929619809SKoji Sato 		get_bh(prev_bh);
93029619809SKoji Sato 	}
93129619809SKoji Sato 
9325eccc067SRyusuke Konishi 	kaddr = kmap_local_page(next_bh->b_page);
93329619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
93429619809SKoji Sato 		cpfile, next, next_bh, kaddr);
93529619809SKoji Sato 	list->ssl_prev = cpu_to_le64(prev);
9365eccc067SRyusuke Konishi 	kunmap_local(kaddr);
93729619809SKoji Sato 
9385eccc067SRyusuke Konishi 	kaddr = kmap_local_page(prev_bh->b_page);
93929619809SKoji Sato 	list = nilfs_cpfile_block_get_snapshot_list(
94029619809SKoji Sato 		cpfile, prev, prev_bh, kaddr);
94129619809SKoji Sato 	list->ssl_next = cpu_to_le64(next);
9425eccc067SRyusuke Konishi 	kunmap_local(kaddr);
94329619809SKoji Sato 
9445eccc067SRyusuke Konishi 	kaddr = kmap_local_page(cp_bh->b_page);
94529619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, cp_bh, kaddr);
94629619809SKoji Sato 	cp->cp_snapshot_list.ssl_next = cpu_to_le64(0);
94729619809SKoji Sato 	cp->cp_snapshot_list.ssl_prev = cpu_to_le64(0);
94829619809SKoji Sato 	nilfs_checkpoint_clear_snapshot(cp);
9495eccc067SRyusuke Konishi 	kunmap_local(kaddr);
95029619809SKoji Sato 
9515eccc067SRyusuke Konishi 	kaddr = kmap_local_page(header_bh->b_page);
95229619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, header_bh, kaddr);
95329619809SKoji Sato 	le64_add_cpu(&header->ch_nsnapshots, -1);
9545eccc067SRyusuke Konishi 	kunmap_local(kaddr);
95529619809SKoji Sato 
9565fc7b141SRyusuke Konishi 	mark_buffer_dirty(next_bh);
9575fc7b141SRyusuke Konishi 	mark_buffer_dirty(prev_bh);
9585fc7b141SRyusuke Konishi 	mark_buffer_dirty(cp_bh);
9595fc7b141SRyusuke Konishi 	mark_buffer_dirty(header_bh);
96029619809SKoji Sato 	nilfs_mdt_mark_dirty(cpfile);
96129619809SKoji Sato 
96229619809SKoji Sato 	brelse(prev_bh);
96329619809SKoji Sato 
96429619809SKoji Sato  out_next:
96529619809SKoji Sato 	brelse(next_bh);
96629619809SKoji Sato 
96729619809SKoji Sato  out_header:
96829619809SKoji Sato 	brelse(header_bh);
96929619809SKoji Sato 
97029619809SKoji Sato  out_cp:
97129619809SKoji Sato 	brelse(cp_bh);
97229619809SKoji Sato 
97329619809SKoji Sato  out_sem:
97429619809SKoji Sato 	up_write(&NILFS_MDT(cpfile)->mi_sem);
97529619809SKoji Sato 	return ret;
97629619809SKoji Sato }
97729619809SKoji Sato 
97829619809SKoji Sato /**
979caaab566SRyusuke Konishi  * nilfs_cpfile_is_snapshot - determine if checkpoint is a snapshot
98029619809SKoji Sato  * @cpfile: inode of checkpoint file
98129619809SKoji Sato  * @cno:    checkpoint number
98229619809SKoji Sato  *
983caaab566SRyusuke Konishi  * Return: 1 if the checkpoint specified by @cno is a snapshot, 0 if not, or
984caaab566SRyusuke Konishi  * the following negative error code on failure.
985caaab566SRyusuke Konishi  * * %-EIO	- I/O error (including metadata corruption).
986caaab566SRyusuke Konishi  * * %-ENOENT	- No such checkpoint.
987caaab566SRyusuke Konishi  * * %-ENOMEM	- Insufficient memory available.
98829619809SKoji Sato  */
98929619809SKoji Sato int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno)
99029619809SKoji Sato {
99129619809SKoji Sato 	struct buffer_head *bh;
99229619809SKoji Sato 	struct nilfs_checkpoint *cp;
99329619809SKoji Sato 	void *kaddr;
99429619809SKoji Sato 	int ret;
99529619809SKoji Sato 
996076a378bSRyusuke Konishi 	/*
997076a378bSRyusuke Konishi 	 * CP number is invalid if it's zero or larger than the
998076a378bSRyusuke Konishi 	 * largest existing one.
999076a378bSRyusuke Konishi 	 */
100043be0ec0SZhu Yanhai 	if (cno == 0 || cno >= nilfs_mdt_cno(cpfile))
100143be0ec0SZhu Yanhai 		return -ENOENT;
100229619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
100329619809SKoji Sato 
100429619809SKoji Sato 	ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh);
100529619809SKoji Sato 	if (ret < 0)
100629619809SKoji Sato 		goto out;
10075eccc067SRyusuke Konishi 	kaddr = kmap_local_page(bh->b_page);
100829619809SKoji Sato 	cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
100943be0ec0SZhu Yanhai 	if (nilfs_checkpoint_invalid(cp))
101043be0ec0SZhu Yanhai 		ret = -ENOENT;
101143be0ec0SZhu Yanhai 	else
101229619809SKoji Sato 		ret = nilfs_checkpoint_snapshot(cp);
10135eccc067SRyusuke Konishi 	kunmap_local(kaddr);
101429619809SKoji Sato 	brelse(bh);
101529619809SKoji Sato 
101629619809SKoji Sato  out:
101729619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
101829619809SKoji Sato 	return ret;
101929619809SKoji Sato }
102029619809SKoji Sato 
102129619809SKoji Sato /**
102229619809SKoji Sato  * nilfs_cpfile_change_cpmode - change checkpoint mode
102329619809SKoji Sato  * @cpfile: inode of checkpoint file
102429619809SKoji Sato  * @cno: checkpoint number
102564ead520SWang Hai  * @mode: mode of checkpoint
102629619809SKoji Sato  *
102729619809SKoji Sato  * Description: nilfs_change_cpmode() changes the mode of the checkpoint
102829619809SKoji Sato  * specified by @cno. The mode @mode is NILFS_CHECKPOINT or NILFS_SNAPSHOT.
102929619809SKoji Sato  *
103029619809SKoji Sato  * Return Value: On success, 0 is returned. On error, one of the following
103129619809SKoji Sato  * negative error codes is returned.
103229619809SKoji Sato  *
103329619809SKoji Sato  * %-EIO - I/O error.
103429619809SKoji Sato  *
103529619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
103629619809SKoji Sato  *
103729619809SKoji Sato  * %-ENOENT - No such checkpoint.
103829619809SKoji Sato  */
103929619809SKoji Sato int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode)
104029619809SKoji Sato {
104129619809SKoji Sato 	int ret;
104229619809SKoji Sato 
104329619809SKoji Sato 	switch (mode) {
104429619809SKoji Sato 	case NILFS_CHECKPOINT:
1045032dbb3bSRyusuke Konishi 		if (nilfs_checkpoint_is_mounted(cpfile->i_sb, cno))
104629619809SKoji Sato 			/*
1047032dbb3bSRyusuke Konishi 			 * Current implementation does not have to protect
1048032dbb3bSRyusuke Konishi 			 * plain read-only mounts since they are exclusive
1049032dbb3bSRyusuke Konishi 			 * with a read/write mount and are protected from the
1050032dbb3bSRyusuke Konishi 			 * cleaner.
105129619809SKoji Sato 			 */
105229619809SKoji Sato 			ret = -EBUSY;
1053032dbb3bSRyusuke Konishi 		else
105429619809SKoji Sato 			ret = nilfs_cpfile_clear_snapshot(cpfile, cno);
105529619809SKoji Sato 		return ret;
105629619809SKoji Sato 	case NILFS_SNAPSHOT:
105729619809SKoji Sato 		return nilfs_cpfile_set_snapshot(cpfile, cno);
105829619809SKoji Sato 	default:
105929619809SKoji Sato 		return -EINVAL;
106029619809SKoji Sato 	}
106129619809SKoji Sato }
106229619809SKoji Sato 
106329619809SKoji Sato /**
106429619809SKoji Sato  * nilfs_cpfile_get_stat - get checkpoint statistics
106529619809SKoji Sato  * @cpfile: inode of checkpoint file
106664ead520SWang Hai  * @cpstat: pointer to a structure of checkpoint statistics
106729619809SKoji Sato  *
106829619809SKoji Sato  * Description: nilfs_cpfile_get_stat() returns information about checkpoints.
106929619809SKoji Sato  *
107029619809SKoji Sato  * Return Value: On success, 0 is returned, and checkpoints information is
107164ead520SWang Hai  * stored in the place pointed by @cpstat. On error, one of the following
107229619809SKoji Sato  * negative error codes is returned.
107329619809SKoji Sato  *
107429619809SKoji Sato  * %-EIO - I/O error.
107529619809SKoji Sato  *
107629619809SKoji Sato  * %-ENOMEM - Insufficient amount of memory available.
107729619809SKoji Sato  */
107829619809SKoji Sato int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat)
107929619809SKoji Sato {
108029619809SKoji Sato 	struct buffer_head *bh;
108129619809SKoji Sato 	struct nilfs_cpfile_header *header;
108229619809SKoji Sato 	void *kaddr;
108329619809SKoji Sato 	int ret;
108429619809SKoji Sato 
108529619809SKoji Sato 	down_read(&NILFS_MDT(cpfile)->mi_sem);
108629619809SKoji Sato 
108729619809SKoji Sato 	ret = nilfs_cpfile_get_header_block(cpfile, &bh);
108829619809SKoji Sato 	if (ret < 0)
108929619809SKoji Sato 		goto out_sem;
10905eccc067SRyusuke Konishi 	kaddr = kmap_local_page(bh->b_page);
109129619809SKoji Sato 	header = nilfs_cpfile_block_get_header(cpfile, bh, kaddr);
109229619809SKoji Sato 	cpstat->cs_cno = nilfs_mdt_cno(cpfile);
109329619809SKoji Sato 	cpstat->cs_ncps = le64_to_cpu(header->ch_ncheckpoints);
109429619809SKoji Sato 	cpstat->cs_nsss = le64_to_cpu(header->ch_nsnapshots);
10955eccc067SRyusuke Konishi 	kunmap_local(kaddr);
109629619809SKoji Sato 	brelse(bh);
109729619809SKoji Sato 
109829619809SKoji Sato  out_sem:
109929619809SKoji Sato 	up_read(&NILFS_MDT(cpfile)->mi_sem);
110029619809SKoji Sato 	return ret;
110129619809SKoji Sato }
110279739565SRyusuke Konishi 
110379739565SRyusuke Konishi /**
1104f1e89c86SRyusuke Konishi  * nilfs_cpfile_read - read or get cpfile inode
1105f1e89c86SRyusuke Konishi  * @sb: super block instance
110679739565SRyusuke Konishi  * @cpsize: size of a checkpoint entry
1107f1e89c86SRyusuke Konishi  * @raw_inode: on-disk cpfile inode
1108f1e89c86SRyusuke Konishi  * @inodep: buffer to store the inode
110979739565SRyusuke Konishi  */
1110f1e89c86SRyusuke Konishi int nilfs_cpfile_read(struct super_block *sb, size_t cpsize,
1111f1e89c86SRyusuke Konishi 		      struct nilfs_inode *raw_inode, struct inode **inodep)
111279739565SRyusuke Konishi {
111379739565SRyusuke Konishi 	struct inode *cpfile;
1114f1e89c86SRyusuke Konishi 	int err;
111579739565SRyusuke Konishi 
11160ec060d1SRyusuke Konishi 	if (cpsize > sb->s_blocksize) {
1117a1d0747aSJoe Perches 		nilfs_err(sb, "too large checkpoint size: %zu bytes", cpsize);
11180ec060d1SRyusuke Konishi 		return -EINVAL;
11190ec060d1SRyusuke Konishi 	} else if (cpsize < NILFS_MIN_CHECKPOINT_SIZE) {
1120a1d0747aSJoe Perches 		nilfs_err(sb, "too small checkpoint size: %zu bytes", cpsize);
11210ec060d1SRyusuke Konishi 		return -EINVAL;
11220ec060d1SRyusuke Konishi 	}
11230ec060d1SRyusuke Konishi 
1124f1e89c86SRyusuke Konishi 	cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO);
1125f1e89c86SRyusuke Konishi 	if (unlikely(!cpfile))
1126f1e89c86SRyusuke Konishi 		return -ENOMEM;
1127f1e89c86SRyusuke Konishi 	if (!(cpfile->i_state & I_NEW))
1128f1e89c86SRyusuke Konishi 		goto out;
1129f1e89c86SRyusuke Konishi 
1130f1e89c86SRyusuke Konishi 	err = nilfs_mdt_init(cpfile, NILFS_MDT_GFP, 0);
1131f1e89c86SRyusuke Konishi 	if (err)
1132f1e89c86SRyusuke Konishi 		goto failed;
1133f1e89c86SRyusuke Konishi 
113479739565SRyusuke Konishi 	nilfs_mdt_set_entry_size(cpfile, cpsize,
113579739565SRyusuke Konishi 				 sizeof(struct nilfs_cpfile_header));
1136f1e89c86SRyusuke Konishi 
1137f1e89c86SRyusuke Konishi 	err = nilfs_read_inode_common(cpfile, raw_inode);
1138f1e89c86SRyusuke Konishi 	if (err)
1139f1e89c86SRyusuke Konishi 		goto failed;
1140f1e89c86SRyusuke Konishi 
1141f1e89c86SRyusuke Konishi 	unlock_new_inode(cpfile);
1142f1e89c86SRyusuke Konishi  out:
1143f1e89c86SRyusuke Konishi 	*inodep = cpfile;
1144f1e89c86SRyusuke Konishi 	return 0;
1145f1e89c86SRyusuke Konishi  failed:
1146f1e89c86SRyusuke Konishi 	iget_failed(cpfile);
1147f1e89c86SRyusuke Konishi 	return err;
114879739565SRyusuke Konishi }
1149