Commit 4e33f9eab07e985282fece4121066c2db1d332ed

Authored by Ryusuke Konishi
1 parent 78eb64c247

nilfs2: implement resize ioctl

This adds resize ioctl which makes online resize possible.

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>

Showing 7 changed files with 189 additions and 5 deletions Side-by-side Diff

... ... @@ -698,6 +698,31 @@
698 698 return 0;
699 699 }
700 700  
  701 +static int nilfs_ioctl_resize(struct inode *inode, struct file *filp,
  702 + void __user *argp)
  703 +{
  704 + __u64 newsize;
  705 + int ret = -EPERM;
  706 +
  707 + if (!capable(CAP_SYS_ADMIN))
  708 + goto out;
  709 +
  710 + ret = mnt_want_write(filp->f_path.mnt);
  711 + if (ret)
  712 + goto out;
  713 +
  714 + ret = -EFAULT;
  715 + if (copy_from_user(&newsize, argp, sizeof(newsize)))
  716 + goto out_drop_write;
  717 +
  718 + ret = nilfs_resize_fs(inode->i_sb, newsize);
  719 +
  720 +out_drop_write:
  721 + mnt_drop_write(filp->f_path.mnt);
  722 +out:
  723 + return ret;
  724 +}
  725 +
701 726 static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp)
702 727 {
703 728 struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
... ... @@ -795,6 +820,8 @@
795 820 return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
796 821 case NILFS_IOCTL_SYNC:
797 822 return nilfs_ioctl_sync(inode, filp, cmd, argp);
  823 + case NILFS_IOCTL_RESIZE:
  824 + return nilfs_ioctl_resize(inode, filp, argp);
798 825 case NILFS_IOCTL_SET_ALLOC_RANGE:
799 826 return nilfs_ioctl_set_alloc_range(inode, argp);
800 827 default:
... ... @@ -298,6 +298,7 @@
298 298 int flip);
299 299 int nilfs_commit_super(struct super_block *sb, int flag);
300 300 int nilfs_cleanup_super(struct super_block *sb);
  301 +int nilfs_resize_fs(struct super_block *sb, __u64 newsize);
301 302 int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt,
302 303 struct nilfs_root **root);
303 304 int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno);
... ... @@ -722,6 +722,73 @@
722 722 }
723 723  
724 724 /**
  725 + * nilfs_sufile_resize - resize segment array
  726 + * @sufile: inode of segment usage file
  727 + * @newnsegs: new number of segments
  728 + *
  729 + * Return Value: On success, 0 is returned. On error, one of the
  730 + * following negative error codes is returned.
  731 + *
  732 + * %-EIO - I/O error.
  733 + *
  734 + * %-ENOMEM - Insufficient amount of memory available.
  735 + *
  736 + * %-ENOSPC - Enough free space is not left for shrinking
  737 + *
  738 + * %-EBUSY - Dirty or active segments exist in the region to be truncated
  739 + */
  740 +int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs)
  741 +{
  742 + struct the_nilfs *nilfs = sufile->i_sb->s_fs_info;
  743 + struct buffer_head *header_bh;
  744 + struct nilfs_sufile_header *header;
  745 + struct nilfs_sufile_info *sui = NILFS_SUI(sufile);
  746 + void *kaddr;
  747 + unsigned long nsegs, nrsvsegs;
  748 + int ret = 0;
  749 +
  750 + down_write(&NILFS_MDT(sufile)->mi_sem);
  751 +
  752 + nsegs = nilfs_sufile_get_nsegments(sufile);
  753 + if (nsegs == newnsegs)
  754 + goto out;
  755 +
  756 + ret = -ENOSPC;
  757 + nrsvsegs = nilfs_nrsvsegs(nilfs, newnsegs);
  758 + if (newnsegs < nsegs && nsegs - newnsegs + nrsvsegs > sui->ncleansegs)
  759 + goto out;
  760 +
  761 + ret = nilfs_sufile_get_header_block(sufile, &header_bh);
  762 + if (ret < 0)
  763 + goto out;
  764 +
  765 + if (newnsegs > nsegs) {
  766 + sui->ncleansegs += newnsegs - nsegs;
  767 + } else /* newnsegs < nsegs */ {
  768 + ret = nilfs_sufile_truncate_range(sufile, newnsegs, nsegs - 1);
  769 + if (ret < 0)
  770 + goto out_header;
  771 +
  772 + sui->ncleansegs -= nsegs - newnsegs;
  773 + }
  774 +
  775 + kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
  776 + header = kaddr + bh_offset(header_bh);
  777 + header->sh_ncleansegs = cpu_to_le64(sui->ncleansegs);
  778 + kunmap_atomic(kaddr, KM_USER0);
  779 +
  780 + nilfs_mdt_mark_buffer_dirty(header_bh);
  781 + nilfs_mdt_mark_dirty(sufile);
  782 + nilfs_set_nsegments(nilfs, newnsegs);
  783 +
  784 +out_header:
  785 + brelse(header_bh);
  786 +out:
  787 + up_write(&NILFS_MDT(sufile)->mi_sem);
  788 + return ret;
  789 +}
  790 +
  791 +/**
725 792 * nilfs_sufile_get_suinfo -
726 793 * @sufile: inode of segment usage file
727 794 * @segnum: segment number to start looking
... ... @@ -62,6 +62,7 @@
62 62 void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *,
63 63 struct buffer_head *);
64 64  
  65 +int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs);
65 66 int nilfs_sufile_read(struct super_block *sb, size_t susize,
66 67 struct nilfs_inode *raw_inode, struct inode **inodep);
67 68  
... ... @@ -56,6 +56,7 @@
56 56 #include "btnode.h"
57 57 #include "page.h"
58 58 #include "cpfile.h"
  59 +#include "sufile.h" /* nilfs_sufile_resize(), nilfs_sufile_set_alloc_range() */
59 60 #include "ifile.h"
60 61 #include "dat.h"
61 62 #include "segment.h"
... ... @@ -400,6 +401,77 @@
400 401 } else {
401 402 brelse(nsbh);
402 403 }
  404 +out:
  405 + return ret;
  406 +}
  407 +
  408 +/**
  409 + * nilfs_resize_fs - resize the filesystem
  410 + * @sb: super block instance
  411 + * @newsize: new size of the filesystem (in bytes)
  412 + */
  413 +int nilfs_resize_fs(struct super_block *sb, __u64 newsize)
  414 +{
  415 + struct the_nilfs *nilfs = sb->s_fs_info;
  416 + struct nilfs_super_block **sbp;
  417 + __u64 devsize, newnsegs;
  418 + loff_t sb2off;
  419 + int ret;
  420 +
  421 + ret = -ERANGE;
  422 + devsize = i_size_read(sb->s_bdev->bd_inode);
  423 + if (newsize > devsize)
  424 + goto out;
  425 +
  426 + /*
  427 + * Write lock is required to protect some functions depending
  428 + * on the number of segments, the number of reserved segments,
  429 + * and so forth.
  430 + */
  431 + down_write(&nilfs->ns_segctor_sem);
  432 +
  433 + sb2off = NILFS_SB2_OFFSET_BYTES(newsize);
  434 + newnsegs = sb2off >> nilfs->ns_blocksize_bits;
  435 + do_div(newnsegs, nilfs->ns_blocks_per_segment);
  436 +
  437 + ret = nilfs_sufile_resize(nilfs->ns_sufile, newnsegs);
  438 + up_write(&nilfs->ns_segctor_sem);
  439 + if (ret < 0)
  440 + goto out;
  441 +
  442 + ret = nilfs_construct_segment(sb);
  443 + if (ret < 0)
  444 + goto out;
  445 +
  446 + down_write(&nilfs->ns_sem);
  447 + nilfs_move_2nd_super(sb, sb2off);
  448 + ret = -EIO;
  449 + sbp = nilfs_prepare_super(sb, 0);
  450 + if (likely(sbp)) {
  451 + nilfs_set_log_cursor(sbp[0], nilfs);
  452 + /*
  453 + * Drop NILFS_RESIZE_FS flag for compatibility with
  454 + * mount-time resize which may be implemented in a
  455 + * future release.
  456 + */
  457 + sbp[0]->s_state = cpu_to_le16(le16_to_cpu(sbp[0]->s_state) &
  458 + ~NILFS_RESIZE_FS);
  459 + sbp[0]->s_dev_size = cpu_to_le64(newsize);
  460 + sbp[0]->s_nsegments = cpu_to_le64(nilfs->ns_nsegments);
  461 + if (sbp[1])
  462 + memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
  463 + ret = nilfs_commit_super(sb, NILFS_SB_COMMIT_ALL);
  464 + }
  465 + up_write(&nilfs->ns_sem);
  466 +
  467 + /*
  468 + * Reset the range of allocatable segments last. This order
  469 + * is important in the case of expansion because the secondary
  470 + * superblock must be protected from log write until migration
  471 + * completes.
  472 + */
  473 + if (!ret)
  474 + nilfs_sufile_set_alloc_range(nilfs->ns_sufile, 0, newnsegs - 1);
403 475 out:
404 476 return ret;
405 477 }
fs/nilfs2/the_nilfs.c
... ... @@ -363,6 +363,24 @@
363 363 return res;
364 364 }
365 365  
  366 +/**
  367 + * nilfs_nrsvsegs - calculate the number of reserved segments
  368 + * @nilfs: nilfs object
  369 + * @nsegs: total number of segments
  370 + */
  371 +unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs)
  372 +{
  373 + return max_t(unsigned long, NILFS_MIN_NRSVSEGS,
  374 + DIV_ROUND_UP(nsegs * nilfs->ns_r_segments_percentage,
  375 + 100));
  376 +}
  377 +
  378 +void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs)
  379 +{
  380 + nilfs->ns_nsegments = nsegs;
  381 + nilfs->ns_nrsvsegs = nilfs_nrsvsegs(nilfs, nsegs);
  382 +}
  383 +
366 384 static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
367 385 struct nilfs_super_block *sbp)
368 386 {
369 387  
... ... @@ -389,13 +407,9 @@
389 407 }
390 408  
391 409 nilfs->ns_first_data_block = le64_to_cpu(sbp->s_first_data_block);
392   - nilfs->ns_nsegments = le64_to_cpu(sbp->s_nsegments);
393 410 nilfs->ns_r_segments_percentage =
394 411 le32_to_cpu(sbp->s_r_segments_percentage);
395   - nilfs->ns_nrsvsegs =
396   - max_t(unsigned long, NILFS_MIN_NRSVSEGS,
397   - DIV_ROUND_UP(nilfs->ns_nsegments *
398   - nilfs->ns_r_segments_percentage, 100));
  412 + nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments));
399 413 nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed);
400 414 return 0;
401 415 }
fs/nilfs2/the_nilfs.h
... ... @@ -268,6 +268,8 @@
268 268 void destroy_nilfs(struct the_nilfs *nilfs);
269 269 int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data);
270 270 int load_nilfs(struct the_nilfs *nilfs, struct super_block *sb);
  271 +unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs);
  272 +void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs);
271 273 int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t);
272 274 int nilfs_count_free_blocks(struct the_nilfs *, sector_t *);
273 275 struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno);