nilfs2: add nilfs_sufile_set_suinfo to update segment usage

Introduce nilfs_sufile_set_suinfo(), which expects an array of
nilfs_suinfo_update structures and updates the segment usage information
accordingly.

This is basically a helper function for the newly introduced
NILFS_IOCTL_SET_SUINFO ioctl.

[konishi.ryusuke@lab.ntt.co.jp: use put_bh() instead of brelse() because we know bh != NULL]
Signed-off-by: Andreas Rohner <andreas.rohner@gmx.net>
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Andreas Rohner 2014-04-03 14:50:27 -07:00 committed by Linus Torvalds
parent 90ccf7dde9
commit 00e9ffcd27
2 changed files with 132 additions and 0 deletions

View File

@ -869,6 +869,137 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf,
return ret;
}
/**
* nilfs_sufile_set_suinfo - sets segment usage info
* @sufile: inode of segment usage file
* @buf: array of suinfo_update
* @supsz: byte size of suinfo_update
* @nsup: size of suinfo_update array
*
* Description: Takes an array of nilfs_suinfo_update structs and updates
* segment usage accordingly. Only the fields indicated by the sup_flags
* are updated.
*
* Return Value: On success, 0 is returned. On error, one of the
* following negative error codes is returned.
*
* %-EIO - I/O error.
*
* %-ENOMEM - Insufficient amount of memory available.
*
* %-EINVAL - Invalid values in input (segment number, flags or nblocks)
*/
ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf,
unsigned supsz, size_t nsup)
{
struct the_nilfs *nilfs = sufile->i_sb->s_fs_info;
struct buffer_head *header_bh, *bh;
struct nilfs_suinfo_update *sup, *supend = buf + supsz * nsup;
struct nilfs_segment_usage *su;
void *kaddr;
unsigned long blkoff, prev_blkoff;
int cleansi, cleansu, dirtysi, dirtysu;
long ncleaned = 0, ndirtied = 0;
int ret = 0;
if (unlikely(nsup == 0))
return ret;
for (sup = buf; sup < supend; sup = (void *)sup + supsz) {
if (sup->sup_segnum >= nilfs->ns_nsegments
|| (sup->sup_flags &
(~0UL << __NR_NILFS_SUINFO_UPDATE_FIELDS))
|| (nilfs_suinfo_update_nblocks(sup) &&
sup->sup_sui.sui_nblocks >
nilfs->ns_blocks_per_segment))
return -EINVAL;
}
down_write(&NILFS_MDT(sufile)->mi_sem);
ret = nilfs_sufile_get_header_block(sufile, &header_bh);
if (ret < 0)
goto out_sem;
sup = buf;
blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum);
ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh);
if (ret < 0)
goto out_header;
for (;;) {
kaddr = kmap_atomic(bh->b_page);
su = nilfs_sufile_block_get_segment_usage(
sufile, sup->sup_segnum, bh, kaddr);
if (nilfs_suinfo_update_lastmod(sup))
su->su_lastmod = cpu_to_le64(sup->sup_sui.sui_lastmod);
if (nilfs_suinfo_update_nblocks(sup))
su->su_nblocks = cpu_to_le32(sup->sup_sui.sui_nblocks);
if (nilfs_suinfo_update_flags(sup)) {
/*
* Active flag is a virtual flag projected by running
* nilfs kernel code - drop it not to write it to
* disk.
*/
sup->sup_sui.sui_flags &=
~(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
cleansi = nilfs_suinfo_clean(&sup->sup_sui);
cleansu = nilfs_segment_usage_clean(su);
dirtysi = nilfs_suinfo_dirty(&sup->sup_sui);
dirtysu = nilfs_segment_usage_dirty(su);
if (cleansi && !cleansu)
++ncleaned;
else if (!cleansi && cleansu)
--ncleaned;
if (dirtysi && !dirtysu)
++ndirtied;
else if (!dirtysi && dirtysu)
--ndirtied;
su->su_flags = cpu_to_le32(sup->sup_sui.sui_flags);
}
kunmap_atomic(kaddr);
sup = (void *)sup + supsz;
if (sup >= supend)
break;
prev_blkoff = blkoff;
blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum);
if (blkoff == prev_blkoff)
continue;
/* get different block */
mark_buffer_dirty(bh);
put_bh(bh);
ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh);
if (unlikely(ret < 0))
goto out_mark;
}
mark_buffer_dirty(bh);
put_bh(bh);
out_mark:
if (ncleaned || ndirtied) {
nilfs_sufile_mod_counter(header_bh, (u64)ncleaned,
(u64)ndirtied);
NILFS_SUI(sufile)->ncleansegs += ncleaned;
}
nilfs_mdt_mark_dirty(sufile);
out_header:
put_bh(header_bh);
out_sem:
up_write(&NILFS_MDT(sufile)->mi_sem);
return ret;
}
/**
* nilfs_sufile_read - read or get sufile inode
* @sb: super block instance

View File

@ -44,6 +44,7 @@ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned,
size_t);
ssize_t nilfs_sufile_set_suinfo(struct inode *, void *, unsigned , size_t);
int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *,
void (*dofunc)(struct inode *, __u64,