diff options
-rw-r--r-- | fs/nilfs2/ioctl.c | 27 | ||||
-rw-r--r-- | fs/nilfs2/nilfs.h | 1 | ||||
-rw-r--r-- | fs/nilfs2/sufile.c | 67 | ||||
-rw-r--r-- | fs/nilfs2/sufile.h | 1 | ||||
-rw-r--r-- | fs/nilfs2/super.c | 72 | ||||
-rw-r--r-- | fs/nilfs2/the_nilfs.c | 24 | ||||
-rw-r--r-- | fs/nilfs2/the_nilfs.h | 2 |
7 files changed, 189 insertions, 5 deletions
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 6f617773a7f7..41d6743d303c 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c | |||
@@ -698,6 +698,31 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, | |||
698 | return 0; | 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 | static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp) | 726 | static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp) |
702 | { | 727 | { |
703 | struct the_nilfs *nilfs = inode->i_sb->s_fs_info; | 728 | 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) | |||
795 | return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); | 820 | return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); |
796 | case NILFS_IOCTL_SYNC: | 821 | case NILFS_IOCTL_SYNC: |
797 | return nilfs_ioctl_sync(inode, filp, cmd, argp); | 822 | return nilfs_ioctl_sync(inode, filp, cmd, argp); |
823 | case NILFS_IOCTL_RESIZE: | ||
824 | return nilfs_ioctl_resize(inode, filp, argp); | ||
798 | case NILFS_IOCTL_SET_ALLOC_RANGE: | 825 | case NILFS_IOCTL_SET_ALLOC_RANGE: |
799 | return nilfs_ioctl_set_alloc_range(inode, argp); | 826 | return nilfs_ioctl_set_alloc_range(inode, argp); |
800 | default: | 827 | default: |
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index a8dd344303cb..09af00999ec0 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h | |||
@@ -298,6 +298,7 @@ struct nilfs_super_block **nilfs_prepare_super(struct super_block *sb, | |||
298 | int flip); | 298 | int flip); |
299 | int nilfs_commit_super(struct super_block *sb, int flag); | 299 | int nilfs_commit_super(struct super_block *sb, int flag); |
300 | int nilfs_cleanup_super(struct super_block *sb); | 300 | int nilfs_cleanup_super(struct super_block *sb); |
301 | int nilfs_resize_fs(struct super_block *sb, __u64 newsize); | ||
301 | int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt, | 302 | int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt, |
302 | struct nilfs_root **root); | 303 | struct nilfs_root **root); |
303 | int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno); | 304 | int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno); |
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 22f2e6ece1be..37b9631cc016 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c | |||
@@ -722,6 +722,73 @@ out: | |||
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 | * nilfs_sufile_get_suinfo - | 792 | * nilfs_sufile_get_suinfo - |
726 | * @sufile: inode of segment usage file | 793 | * @sufile: inode of segment usage file |
727 | * @segnum: segment number to start looking | 794 | * @segnum: segment number to start looking |
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h index 57bfee9cd02d..1eac4c6ea384 100644 --- a/fs/nilfs2/sufile.h +++ b/fs/nilfs2/sufile.h | |||
@@ -62,6 +62,7 @@ void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *, | |||
62 | void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *, | 62 | void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *, |
63 | struct buffer_head *); | 63 | struct buffer_head *); |
64 | 64 | ||
65 | int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs); | ||
65 | int nilfs_sufile_read(struct super_block *sb, size_t susize, | 66 | int nilfs_sufile_read(struct super_block *sb, size_t susize, |
66 | struct nilfs_inode *raw_inode, struct inode **inodep); | 67 | struct nilfs_inode *raw_inode, struct inode **inodep); |
67 | 68 | ||
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index eba5b53b1080..2846491071ce 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include "btnode.h" | 56 | #include "btnode.h" |
57 | #include "page.h" | 57 | #include "page.h" |
58 | #include "cpfile.h" | 58 | #include "cpfile.h" |
59 | #include "sufile.h" /* nilfs_sufile_resize(), nilfs_sufile_set_alloc_range() */ | ||
59 | #include "ifile.h" | 60 | #include "ifile.h" |
60 | #include "dat.h" | 61 | #include "dat.h" |
61 | #include "segment.h" | 62 | #include "segment.h" |
@@ -404,6 +405,77 @@ out: | |||
404 | return ret; | 405 | return ret; |
405 | } | 406 | } |
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); | ||
475 | out: | ||
476 | return ret; | ||
477 | } | ||
478 | |||
407 | static void nilfs_put_super(struct super_block *sb) | 479 | static void nilfs_put_super(struct super_block *sb) |
408 | { | 480 | { |
409 | struct the_nilfs *nilfs = sb->s_fs_info; | 481 | struct the_nilfs *nilfs = sb->s_fs_info; |
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index d2acd1a651f3..d32714094375 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c | |||
@@ -363,6 +363,24 @@ static unsigned long long nilfs_max_size(unsigned int blkbits) | |||
363 | return res; | 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 | static int nilfs_store_disk_layout(struct the_nilfs *nilfs, | 384 | static int nilfs_store_disk_layout(struct the_nilfs *nilfs, |
367 | struct nilfs_super_block *sbp) | 385 | struct nilfs_super_block *sbp) |
368 | { | 386 | { |
@@ -389,13 +407,9 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs, | |||
389 | } | 407 | } |
390 | 408 | ||
391 | nilfs->ns_first_data_block = le64_to_cpu(sbp->s_first_data_block); | 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 | nilfs->ns_r_segments_percentage = | 410 | nilfs->ns_r_segments_percentage = |
394 | le32_to_cpu(sbp->s_r_segments_percentage); | 411 | le32_to_cpu(sbp->s_r_segments_percentage); |
395 | nilfs->ns_nrsvsegs = | 412 | nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments)); |
396 | max_t(unsigned long, NILFS_MIN_NRSVSEGS, | ||
397 | DIV_ROUND_UP(nilfs->ns_nsegments * | ||
398 | nilfs->ns_r_segments_percentage, 100)); | ||
399 | nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed); | 413 | nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed); |
400 | return 0; | 414 | return 0; |
401 | } | 415 | } |
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index f4968145c2a3..9992b11312ff 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h | |||
@@ -268,6 +268,8 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev); | |||
268 | void destroy_nilfs(struct the_nilfs *nilfs); | 268 | void destroy_nilfs(struct the_nilfs *nilfs); |
269 | int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data); | 269 | int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data); |
270 | int load_nilfs(struct the_nilfs *nilfs, struct super_block *sb); | 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 | int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t); | 273 | int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t); |
272 | int nilfs_count_free_blocks(struct the_nilfs *, sector_t *); | 274 | int nilfs_count_free_blocks(struct the_nilfs *, sector_t *); |
273 | struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno); | 275 | struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno); |