Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nilfs2: implement resize ioctl

This adds resize ioctl which makes online resize possible.

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

+189 -5
+27
fs/nilfs2/ioctl.c
··· 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; ··· 820 795 return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); 821 796 case NILFS_IOCTL_SYNC: 822 797 return nilfs_ioctl_sync(inode, filp, cmd, argp); 798 + case NILFS_IOCTL_RESIZE: 799 + return nilfs_ioctl_resize(inode, filp, argp); 823 800 case NILFS_IOCTL_SET_ALLOC_RANGE: 824 801 return nilfs_ioctl_set_alloc_range(inode, argp); 825 802 default:
+1
fs/nilfs2/nilfs.h
··· 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);
+67
fs/nilfs2/sufile.c
··· 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
+1
fs/nilfs2/sufile.h
··· 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
+72
fs/nilfs2/super.c
··· 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" ··· 401 400 } else { 402 401 brelse(nsbh); 403 402 } 403 + out: 404 + return ret; 405 + } 406 + 407 + /** 408 + * nilfs_resize_fs - resize the filesystem 409 + * @sb: super block instance 410 + * @newsize: new size of the filesystem (in bytes) 411 + */ 412 + int nilfs_resize_fs(struct super_block *sb, __u64 newsize) 413 + { 414 + struct the_nilfs *nilfs = sb->s_fs_info; 415 + struct nilfs_super_block **sbp; 416 + __u64 devsize, newnsegs; 417 + loff_t sb2off; 418 + int ret; 419 + 420 + ret = -ERANGE; 421 + devsize = i_size_read(sb->s_bdev->bd_inode); 422 + if (newsize > devsize) 423 + goto out; 424 + 425 + /* 426 + * Write lock is required to protect some functions depending 427 + * on the number of segments, the number of reserved segments, 428 + * and so forth. 429 + */ 430 + down_write(&nilfs->ns_segctor_sem); 431 + 432 + sb2off = NILFS_SB2_OFFSET_BYTES(newsize); 433 + newnsegs = sb2off >> nilfs->ns_blocksize_bits; 434 + do_div(newnsegs, nilfs->ns_blocks_per_segment); 435 + 436 + ret = nilfs_sufile_resize(nilfs->ns_sufile, newnsegs); 437 + up_write(&nilfs->ns_segctor_sem); 438 + if (ret < 0) 439 + goto out; 440 + 441 + ret = nilfs_construct_segment(sb); 442 + if (ret < 0) 443 + goto out; 444 + 445 + down_write(&nilfs->ns_sem); 446 + nilfs_move_2nd_super(sb, sb2off); 447 + ret = -EIO; 448 + sbp = nilfs_prepare_super(sb, 0); 449 + if (likely(sbp)) { 450 + nilfs_set_log_cursor(sbp[0], nilfs); 451 + /* 452 + * Drop NILFS_RESIZE_FS flag for compatibility with 453 + * mount-time resize which may be implemented in a 454 + * future release. 455 + */ 456 + sbp[0]->s_state = cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & 457 + ~NILFS_RESIZE_FS); 458 + sbp[0]->s_dev_size = cpu_to_le64(newsize); 459 + sbp[0]->s_nsegments = cpu_to_le64(nilfs->ns_nsegments); 460 + if (sbp[1]) 461 + memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); 462 + ret = nilfs_commit_super(sb, NILFS_SB_COMMIT_ALL); 463 + } 464 + up_write(&nilfs->ns_sem); 465 + 466 + /* 467 + * Reset the range of allocatable segments last. This order 468 + * is important in the case of expansion because the secondary 469 + * superblock must be protected from log write until migration 470 + * completes. 471 + */ 472 + if (!ret) 473 + nilfs_sufile_set_alloc_range(nilfs->ns_sufile, 0, newnsegs - 1); 404 474 out: 405 475 return ret; 406 476 }
+19 -5
fs/nilfs2/the_nilfs.c
··· 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 { ··· 407 389 } 408 390 409 391 nilfs->ns_first_data_block = le64_to_cpu(sbp->s_first_data_block); 410 - nilfs->ns_nsegments = le64_to_cpu(sbp->s_nsegments); 411 392 nilfs->ns_r_segments_percentage = 412 393 le32_to_cpu(sbp->s_r_segments_percentage); 413 - nilfs->ns_nrsvsegs = 414 - max_t(unsigned long, NILFS_MIN_NRSVSEGS, 415 - DIV_ROUND_UP(nilfs->ns_nsegments * 416 - nilfs->ns_r_segments_percentage, 100)); 394 + nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments)); 417 395 nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed); 418 396 return 0; 419 397 }
+2
fs/nilfs2/the_nilfs.h
··· 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);