/*
* sufile.c - NILFS segment usage file.
*
* Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Written by Koji Sato <koji@osrg.net>.
* Revised by Ryusuke Konishi <ryusuke@osrg.net>.
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/buffer_head.h>
#include <linux/errno.h>
#include <linux/nilfs2_fs.h>
#include "mdt.h"
#include "sufile.h"
/**
* struct nilfs_sufile_info - on-memory private data of sufile
* @mi: on-memory private data of metadata file
* @ncleansegs: number of clean segments
* @allocmin: lower limit of allocatable segment range
* @allocmax: upper limit of allocatable segment range
*/
struct nilfs_sufile_info {
struct nilfs_mdt_info mi;
unsigned long ncleansegs;/* number of clean segments */
__u64 allocmin; /* lower limit of allocatable segment range */
__u64 allocmax; /* upper limit of allocatable segment range */
};
static inline struct nilfs_sufile_info *NILFS_SUI(struct inode *sufile)
{
return (struct nilfs_sufile_info *)NILFS_MDT(sufile);
}
static inline unsigned long
nilfs_sufile_segment_usages_per_block(const struct inode *sufile)
{
return NILFS_MDT(sufile)->mi_entries_per_block;
}
static unsigned long
nilfs_sufile_get_blkoff(const struct inode *sufile, __u64 segnum)
{
__u64 t = segnum + NILFS_MDT(sufile)->mi_first_entry_offset;
do_div(t, nilfs_sufile_segment_usages_per_block(sufile));
return (unsigned long)t;
}
static unsigned long
nilfs_sufile_get_offset(const struct inode *sufile, __u64 segnum)
{
__u64 t = segnum + NILFS_MDT(sufile)->mi_first_entry_offset;
return do_div(t, nilfs_sufile_segment_usages_per_block(sufile));
}
static unsigned long
nilfs_sufile_segment_usages_in_block(const struct inode *sufile, __u64 curr,
__u64 max)
{
return min_t(unsigned long,
nilfs_sufile_segment_usages_per_block(sufile) -
nilfs_sufile_get_offset(sufile, curr),
max - curr + 1);
}
static struct nilfs_segment_usage *
nilfs_sufile_block_get_segment_usage(const struct inode *sufile, __u64 segnum,
struct buffer_head *bh, void *kaddr)
{
return kaddr + bh_offset(bh) +
nilfs_sufile_get_offset(sufile, segnum) *
NILFS_MDT(sufile)->mi_entry_size;
}
static inline int nilfs_sufile_get_header_block(struct inode *sufile,
struct buffer_head **bhp)
{
return nilfs_mdt_get_block(sufile, 0, 0, NULL, bhp);
}
static inline int
nilfs_sufile_get_segment_usage_block(struct inode *sufile, __u64 segnum,
int create, struct buffer_head **bhp)
{
return nilfs_mdt_get_block(sufile,
nilfs_sufile_get_blkoff(sufile, segnum),
create, NULL, bhp);
}
static int nilfs_sufile_delete_segment_usage_block(struct inode *sufile,
__u64 segnum)
{
return nilfs_mdt_