nilfs2: implement resize ioctl
This adds resize ioctl which makes online resize possible. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
This commit is contained in:
parent
78eb64c247
commit
4e33f9eab0
|
@ -698,6 +698,31 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nilfs_ioctl_resize(struct inode *inode, struct file *filp,
|
||||
void __user *argp)
|
||||
{
|
||||
__u64 newsize;
|
||||
int ret = -EPERM;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
goto out;
|
||||
|
||||
ret = mnt_want_write(filp->f_path.mnt);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&newsize, argp, sizeof(newsize)))
|
||||
goto out_drop_write;
|
||||
|
||||
ret = nilfs_resize_fs(inode->i_sb, newsize);
|
||||
|
||||
out_drop_write:
|
||||
mnt_drop_write(filp->f_path.mnt);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp)
|
||||
{
|
||||
struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
|
||||
|
@ -795,6 +820,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
|
||||
case NILFS_IOCTL_SYNC:
|
||||
return nilfs_ioctl_sync(inode, filp, cmd, argp);
|
||||
case NILFS_IOCTL_RESIZE:
|
||||
return nilfs_ioctl_resize(inode, filp, argp);
|
||||
case NILFS_IOCTL_SET_ALLOC_RANGE:
|
||||
return nilfs_ioctl_set_alloc_range(inode, argp);
|
||||
default:
|
||||
|
|
|
@ -298,6 +298,7 @@ struct nilfs_super_block **nilfs_prepare_super(struct super_block *sb,
|
|||
int flip);
|
||||
int nilfs_commit_super(struct super_block *sb, int flag);
|
||||
int nilfs_cleanup_super(struct super_block *sb);
|
||||
int nilfs_resize_fs(struct super_block *sb, __u64 newsize);
|
||||
int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt,
|
||||
struct nilfs_root **root);
|
||||
int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno);
|
||||
|
|
|
@ -721,6 +721,73 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nilfs_sufile_resize - resize segment array
|
||||
* @sufile: inode of segment usage file
|
||||
* @newnsegs: new number of segments
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* %-ENOSPC - Enough free space is not left for shrinking
|
||||
*
|
||||
* %-EBUSY - Dirty or active segments exist in the region to be truncated
|
||||
*/
|
||||
int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs)
|
||||
{
|
||||
struct the_nilfs *nilfs = sufile->i_sb->s_fs_info;
|
||||
struct buffer_head *header_bh;
|
||||
struct nilfs_sufile_header *header;
|
||||
struct nilfs_sufile_info *sui = NILFS_SUI(sufile);
|
||||
void *kaddr;
|
||||
unsigned long nsegs, nrsvsegs;
|
||||
int ret = 0;
|
||||
|
||||
down_write(&NILFS_MDT(sufile)->mi_sem);
|
||||
|
||||
nsegs = nilfs_sufile_get_nsegments(sufile);
|
||||
if (nsegs == newnsegs)
|
||||
goto out;
|
||||
|
||||
ret = -ENOSPC;
|
||||
nrsvsegs = nilfs_nrsvsegs(nilfs, newnsegs);
|
||||
if (newnsegs < nsegs && nsegs - newnsegs + nrsvsegs > sui->ncleansegs)
|
||||
goto out;
|
||||
|
||||
ret = nilfs_sufile_get_header_block(sufile, &header_bh);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (newnsegs > nsegs) {
|
||||
sui->ncleansegs += newnsegs - nsegs;
|
||||
} else /* newnsegs < nsegs */ {
|
||||
ret = nilfs_sufile_truncate_range(sufile, newnsegs, nsegs - 1);
|
||||
if (ret < 0)
|
||||
goto out_header;
|
||||
|
||||
sui->ncleansegs -= nsegs - newnsegs;
|
||||
}
|
||||
|
||||
kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
|
||||
header = kaddr + bh_offset(header_bh);
|
||||
header->sh_ncleansegs = cpu_to_le64(sui->ncleansegs);
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
|
||||
nilfs_mdt_mark_buffer_dirty(header_bh);
|
||||
nilfs_mdt_mark_dirty(sufile);
|
||||
nilfs_set_nsegments(nilfs, newnsegs);
|
||||
|
||||
out_header:
|
||||
brelse(header_bh);
|
||||
out:
|
||||
up_write(&NILFS_MDT(sufile)->mi_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nilfs_sufile_get_suinfo -
|
||||
* @sufile: inode of segment usage file
|
||||
|
|
|
@ -62,6 +62,7 @@ void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *,
|
|||
void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *,
|
||||
struct buffer_head *);
|
||||
|
||||
int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs);
|
||||
int nilfs_sufile_read(struct super_block *sb, size_t susize,
|
||||
struct nilfs_inode *raw_inode, struct inode **inodep);
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include "btnode.h"
|
||||
#include "page.h"
|
||||
#include "cpfile.h"
|
||||
#include "sufile.h" /* nilfs_sufile_resize(), nilfs_sufile_set_alloc_range() */
|
||||
#include "ifile.h"
|
||||
#include "dat.h"
|
||||
#include "segment.h"
|
||||
|
@ -404,6 +405,77 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nilfs_resize_fs - resize the filesystem
|
||||
* @sb: super block instance
|
||||
* @newsize: new size of the filesystem (in bytes)
|
||||
*/
|
||||
int nilfs_resize_fs(struct super_block *sb, __u64 newsize)
|
||||
{
|
||||
struct the_nilfs *nilfs = sb->s_fs_info;
|
||||
struct nilfs_super_block **sbp;
|
||||
__u64 devsize, newnsegs;
|
||||
loff_t sb2off;
|
||||
int ret;
|
||||
|
||||
ret = -ERANGE;
|
||||
devsize = i_size_read(sb->s_bdev->bd_inode);
|
||||
if (newsize > devsize)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Write lock is required to protect some functions depending
|
||||
* on the number of segments, the number of reserved segments,
|
||||
* and so forth.
|
||||
*/
|
||||
down_write(&nilfs->ns_segctor_sem);
|
||||
|
||||
sb2off = NILFS_SB2_OFFSET_BYTES(newsize);
|
||||
newnsegs = sb2off >> nilfs->ns_blocksize_bits;
|
||||
do_div(newnsegs, nilfs->ns_blocks_per_segment);
|
||||
|
||||
ret = nilfs_sufile_resize(nilfs->ns_sufile, newnsegs);
|
||||
up_write(&nilfs->ns_segctor_sem);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = nilfs_construct_segment(sb);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
down_write(&nilfs->ns_sem);
|
||||
nilfs_move_2nd_super(sb, sb2off);
|
||||
ret = -EIO;
|
||||
sbp = nilfs_prepare_super(sb, 0);
|
||||
if (likely(sbp)) {
|
||||
nilfs_set_log_cursor(sbp[0], nilfs);
|
||||
/*
|
||||
* Drop NILFS_RESIZE_FS flag for compatibility with
|
||||
* mount-time resize which may be implemented in a
|
||||
* future release.
|
||||
*/
|
||||
sbp[0]->s_state = cpu_to_le16(le16_to_cpu(sbp[0]->s_state) &
|
||||
~NILFS_RESIZE_FS);
|
||||
sbp[0]->s_dev_size = cpu_to_le64(newsize);
|
||||
sbp[0]->s_nsegments = cpu_to_le64(nilfs->ns_nsegments);
|
||||
if (sbp[1])
|
||||
memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
|
||||
ret = nilfs_commit_super(sb, NILFS_SB_COMMIT_ALL);
|
||||
}
|
||||
up_write(&nilfs->ns_sem);
|
||||
|
||||
/*
|
||||
* Reset the range of allocatable segments last. This order
|
||||
* is important in the case of expansion because the secondary
|
||||
* superblock must be protected from log write until migration
|
||||
* completes.
|
||||
*/
|
||||
if (!ret)
|
||||
nilfs_sufile_set_alloc_range(nilfs->ns_sufile, 0, newnsegs - 1);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nilfs_put_super(struct super_block *sb)
|
||||
{
|
||||
struct the_nilfs *nilfs = sb->s_fs_info;
|
||||
|
|
|
@ -363,6 +363,24 @@ static unsigned long long nilfs_max_size(unsigned int blkbits)
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* nilfs_nrsvsegs - calculate the number of reserved segments
|
||||
* @nilfs: nilfs object
|
||||
* @nsegs: total number of segments
|
||||
*/
|
||||
unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs)
|
||||
{
|
||||
return max_t(unsigned long, NILFS_MIN_NRSVSEGS,
|
||||
DIV_ROUND_UP(nsegs * nilfs->ns_r_segments_percentage,
|
||||
100));
|
||||
}
|
||||
|
||||
void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs)
|
||||
{
|
||||
nilfs->ns_nsegments = nsegs;
|
||||
nilfs->ns_nrsvsegs = nilfs_nrsvsegs(nilfs, nsegs);
|
||||
}
|
||||
|
||||
static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
|
||||
struct nilfs_super_block *sbp)
|
||||
{
|
||||
|
@ -389,13 +407,9 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
|
|||
}
|
||||
|
||||
nilfs->ns_first_data_block = le64_to_cpu(sbp->s_first_data_block);
|
||||
nilfs->ns_nsegments = le64_to_cpu(sbp->s_nsegments);
|
||||
nilfs->ns_r_segments_percentage =
|
||||
le32_to_cpu(sbp->s_r_segments_percentage);
|
||||
nilfs->ns_nrsvsegs =
|
||||
max_t(unsigned long, NILFS_MIN_NRSVSEGS,
|
||||
DIV_ROUND_UP(nilfs->ns_nsegments *
|
||||
nilfs->ns_r_segments_percentage, 100));
|
||||
nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments));
|
||||
nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -268,6 +268,8 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev);
|
|||
void destroy_nilfs(struct the_nilfs *nilfs);
|
||||
int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data);
|
||||
int load_nilfs(struct the_nilfs *nilfs, struct super_block *sb);
|
||||
unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs);
|
||||
void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs);
|
||||
int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t);
|
||||
int nilfs_count_free_blocks(struct the_nilfs *, sector_t *);
|
||||
struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno);
|
||||
|
|
Loading…
Reference in New Issue