Skip to content

Commit

Permalink
nilfs2: add nilfs_sufile_set_suinfo to update segment usage
Browse files Browse the repository at this point in the history
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.

[[email protected]: use put_bh() instead of brelse() because we know bh != NULL]
Signed-off-by: Andreas Rohner <[email protected]>
Signed-off-by: Ryusuke Konishi <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
zeitgeist87 authored and torvalds committed Apr 3, 2014
1 parent 90ccf7d commit 00e9ffc
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 0 deletions.
131 changes: 131 additions & 0 deletions fs/nilfs2/sufile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions fs/nilfs2/sufile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 00e9ffc

Please sign in to comment.