Commit 4e33f9eab07e985282fece4121066c2db1d332ed
1 parent
78eb64c247
Exists in
master
and in
7 other branches
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
fs/nilfs2/ioctl.c
... | ... | @@ -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: |
fs/nilfs2/nilfs.h
... | ... | @@ -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); |
fs/nilfs2/sufile.c
... | ... | @@ -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 |
fs/nilfs2/sufile.h
... | ... | @@ -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 |
fs/nilfs2/super.c
... | ... | @@ -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); |