// SPDX-License-Identifier: GPL-2.0+
/*
* NILFS checkpoint file.
*
* Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
*
* Written by Koji Sato.
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/buffer_head.h>
#include <linux/errno.h>
#include "mdt.h"
#include "cpfile.h"
static inline unsigned long
nilfs_cpfile_checkpoints_per_block(const struct inode *cpfile)
{
return NILFS_MDT(cpfile)->mi_entries_per_block;
}
/* block number from the beginning of the file */
static unsigned long
nilfs_cpfile_get_blkoff(const struct inode *cpfile, __u64 cno)
{
__u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
tcno = div64_ul(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
return (unsigned long)tcno;
}
/* offset in block */
static unsigned long
nilfs_cpfile_get_offset(const struct inode *cpfile, __u64 cno)
{
__u64 tcno = cno + NILFS_MDT(cpfile)->mi_first_entry_offset - 1;
return do_div(tcno, nilfs_cpfile_checkpoints_per_block(cpfile));
}
static __u64 nilfs_cpfile_first_checkpoint_in_block(const struct inode *cpfile,
unsigned long blkoff)
{
return (__u64)nilfs_cpfile_checkpoints_per_block(cpfile) * blkoff
+ 1 - NILFS_MDT(cpfile)->mi_first_entry_offset;
}
static unsigned long
nilfs_cpfile_checkpoints_in_block(const struct inode *cpfile,
__u64 curr,
__u64 max)
{
return min_t(__u64,
nilfs_cpfile_checkpoints_per_block(cpfile) -
nilfs_cpfile_get_offset(cpfile, curr),
max - curr);
}
static inline int nilfs_cpfile_is_in_first(const struct inode *cpfile,
__u64 cno)
{
return nilfs_cpfile_get_blkoff(cpfile, cno) == 0;
}
static unsigned int
nilfs_cpfile_block_add_valid_checkpoints(const struct inode *cpfile,
struct buffer_head *bh,
unsigned int n)
{
struct nilfs_checkpoint *cp;
unsigned int count;
cp = kmap_local_folio(bh->b_folio,
offset_in_folio(bh->b_folio, bh->b_data));
count = le32_to_cpu(cp->cp_checkpoints_count) + n;
cp->cp_checkpoints_count = cpu_to_le32(count);
kunmap_local(cp);
return count;
}
static unsigned int
nilfs_cpfile_block_sub_valid_checkpoints(const struct inode *cpfile,
struct buffer_head *bh,
unsigned int n)
{
struct nilfs_checkpoint *cp;
unsigned int count;
cp = kmap_local_folio(bh->b_folio,
offset_in_folio(bh->b_folio, bh->b_data));
WARN_ON(le32_to_cpu(cp->cp_checkpoints_count) < n);
count = le32_to_cpu(cp->cp_checkpoints_count) - n;
cp->cp_checkpoints_count = cpu_to_le32(count);
kunmap_local(cp);
return count;
}
static void nilfs_cpfile_block_init(struct inode *cpfile,
struct buffer_head *bh,
void *from)
{
struct nilfs_checkpoint *cp = from;
size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
int n = nilfs_cpfile_checkpoints_per_block(cpfile);
while (n-- > 0) {
nilfs_checkpoint_set_invalid(cp);
cp = (void *)cp + cpsz;
}
}
/**
* nilfs_cpfile_checkpoint_offset - calculate the byte offset of a checkpoint
* entry in the folio containing it
* @cpfile: checkpoint file inode
* @cno: checkpoint number
* @bh: buffer head of block containing checkpoint indexed by @cno
*
* Return: Byte offset in the folio of the checkpoint specified by @cno.
*/
static size_t nilfs_cpfile_checkpoint_offset(const struct inode *cpfile,
__u64 cno,
struct buffer_head *bh)
{
return offset_in_folio(bh->b_folio, bh->b_data) +
nilfs_cpfile_get_offset(cpfile, cno) *
NILFS_MDT(cpfile)->mi_entry_size;
}
/**
* nilfs_cpfile_cp_snapshot_list_offset - calculate the byte offset of a
* checkpoint snapshot list in the folio
* containing it
* @cpfile: checkpoint file inode
* @cno: checkpoint number
* @bh: buffer head of block containing checkpoint indexed by @cno
*
* Return: Byte offset in the folio of the checkpoint snapshot list specified
* by @cno.
*/
static size_t nilfs_cpfile_cp_snapshot_list_offset(const struct inode *<