aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/move_extent.c
diff options
context:
space:
mode:
authorAkira Fujita <a-fujita@rs.jp.nec.com>2009-11-23 07:25:48 -0500
committerTheodore Ts'o <tytso@mit.edu>2009-11-23 07:25:48 -0500
commitf868a48d06f8886cb0367568a12367fa4f21ea0d (patch)
tree9b15c2fa850f6c34589f80f8e41fad095aa04c43 /fs/ext4/move_extent.c
parent503358ae01b70ce6909d19dd01287093f6b6271c (diff)
ext4: fix the returned block count if EXT4_IOC_MOVE_EXT fails
If the EXT4_IOC_MOVE_EXT ioctl fails, the number of blocks that were exchanged before the failure should be returned to the userspace caller. Unfortunately, currently if the block size is not the same as the page size, the returned block count that is returned is the page-aligned block count instead of the actual block count. This commit addresses this bug. Signed-off-by: Akira Fujita <a-fujita@rs.jp.nec.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/move_extent.c')
-rw-r--r--fs/ext4/move_extent.c139
1 files changed, 73 insertions, 66 deletions
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 25b6b1457360..83f8c9e47c60 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -661,6 +661,7 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext,
661 * @donor_inode: donor inode 661 * @donor_inode: donor inode
662 * @from: block offset of orig_inode 662 * @from: block offset of orig_inode
663 * @count: block count to be replaced 663 * @count: block count to be replaced
664 * @err: pointer to save return value
664 * 665 *
665 * Replace original inode extents and donor inode extents page by page. 666 * Replace original inode extents and donor inode extents page by page.
666 * We implement this replacement in the following three steps: 667 * We implement this replacement in the following three steps:
@@ -671,19 +672,18 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext,
671 * 3. Change the block information of donor inode to point at the saved 672 * 3. Change the block information of donor inode to point at the saved
672 * original inode blocks in the dummy extents. 673 * original inode blocks in the dummy extents.
673 * 674 *
674 * Return 0 on success, or a negative error value on failure. 675 * Return replaced block count.
675 */ 676 */
676static int 677static int
677mext_replace_branches(handle_t *handle, struct inode *orig_inode, 678mext_replace_branches(handle_t *handle, struct inode *orig_inode,
678 struct inode *donor_inode, ext4_lblk_t from, 679 struct inode *donor_inode, ext4_lblk_t from,
679 ext4_lblk_t count) 680 ext4_lblk_t count, int *err)
680{ 681{
681 struct ext4_ext_path *orig_path = NULL; 682 struct ext4_ext_path *orig_path = NULL;
682 struct ext4_ext_path *donor_path = NULL; 683 struct ext4_ext_path *donor_path = NULL;
683 struct ext4_extent *oext, *dext; 684 struct ext4_extent *oext, *dext;
684 struct ext4_extent tmp_dext, tmp_oext; 685 struct ext4_extent tmp_dext, tmp_oext;
685 ext4_lblk_t orig_off = from, donor_off = from; 686 ext4_lblk_t orig_off = from, donor_off = from;
686 int err = 0;
687 int depth; 687 int depth;
688 int replaced_count = 0; 688 int replaced_count = 0;
689 int dext_alen; 689 int dext_alen;
@@ -691,13 +691,13 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
691 mext_double_down_write(orig_inode, donor_inode); 691 mext_double_down_write(orig_inode, donor_inode);
692 692
693 /* Get the original extent for the block "orig_off" */ 693 /* Get the original extent for the block "orig_off" */
694 err = get_ext_path(orig_inode, orig_off, &orig_path); 694 *err = get_ext_path(orig_inode, orig_off, &orig_path);
695 if (err) 695 if (*err)
696 goto out; 696 goto out;
697 697
698 /* Get the donor extent for the head */ 698 /* Get the donor extent for the head */
699 err = get_ext_path(donor_inode, donor_off, &donor_path); 699 *err = get_ext_path(donor_inode, donor_off, &donor_path);
700 if (err) 700 if (*err)
701 goto out; 701 goto out;
702 depth = ext_depth(orig_inode); 702 depth = ext_depth(orig_inode);
703 oext = orig_path[depth].p_ext; 703 oext = orig_path[depth].p_ext;
@@ -707,9 +707,9 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
707 dext = donor_path[depth].p_ext; 707 dext = donor_path[depth].p_ext;
708 tmp_dext = *dext; 708 tmp_dext = *dext;
709 709
710 err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off, 710 *err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
711 donor_off, count); 711 donor_off, count);
712 if (err) 712 if (*err)
713 goto out; 713 goto out;
714 714
715 /* Loop for the donor extents */ 715 /* Loop for the donor extents */
@@ -718,7 +718,7 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
718 if (!dext) { 718 if (!dext) {
719 ext4_error(donor_inode->i_sb, __func__, 719 ext4_error(donor_inode->i_sb, __func__,
720 "The extent for donor must be found"); 720 "The extent for donor must be found");
721 err = -EIO; 721 *err = -EIO;
722 goto out; 722 goto out;
723 } else if (donor_off != le32_to_cpu(tmp_dext.ee_block)) { 723 } else if (donor_off != le32_to_cpu(tmp_dext.ee_block)) {
724 ext4_error(donor_inode->i_sb, __func__, 724 ext4_error(donor_inode->i_sb, __func__,
@@ -726,20 +726,20 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
726 "extent(%u) should be equal", 726 "extent(%u) should be equal",
727 donor_off, 727 donor_off,
728 le32_to_cpu(tmp_dext.ee_block)); 728 le32_to_cpu(tmp_dext.ee_block));
729 err = -EIO; 729 *err = -EIO;
730 goto out; 730 goto out;
731 } 731 }
732 732
733 /* Set donor extent to orig extent */ 733 /* Set donor extent to orig extent */
734 err = mext_leaf_block(handle, orig_inode, 734 *err = mext_leaf_block(handle, orig_inode,
735 orig_path, &tmp_dext, &orig_off); 735 orig_path, &tmp_dext, &orig_off);
736 if (err < 0) 736 if (*err)
737 goto out; 737 goto out;
738 738
739 /* Set orig extent to donor extent */ 739 /* Set orig extent to donor extent */
740 err = mext_leaf_block(handle, donor_inode, 740 *err = mext_leaf_block(handle, donor_inode,
741 donor_path, &tmp_oext, &donor_off); 741 donor_path, &tmp_oext, &donor_off);
742 if (err < 0) 742 if (*err)
743 goto out; 743 goto out;
744 744
745 dext_alen = ext4_ext_get_actual_len(&tmp_dext); 745 dext_alen = ext4_ext_get_actual_len(&tmp_dext);
@@ -753,35 +753,25 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
753 753
754 if (orig_path) 754 if (orig_path)
755 ext4_ext_drop_refs(orig_path); 755 ext4_ext_drop_refs(orig_path);
756 err = get_ext_path(orig_inode, orig_off, &orig_path); 756 *err = get_ext_path(orig_inode, orig_off, &orig_path);
757 if (err) 757 if (*err)
758 goto out; 758 goto out;
759 depth = ext_depth(orig_inode); 759 depth = ext_depth(orig_inode);
760 oext = orig_path[depth].p_ext; 760 oext = orig_path[depth].p_ext;
761 if (le32_to_cpu(oext->ee_block) +
762 ext4_ext_get_actual_len(oext) <= orig_off) {
763 err = 0;
764 goto out;
765 }
766 tmp_oext = *oext; 761 tmp_oext = *oext;
767 762
768 if (donor_path) 763 if (donor_path)
769 ext4_ext_drop_refs(donor_path); 764 ext4_ext_drop_refs(donor_path);
770 err = get_ext_path(donor_inode, donor_off, &donor_path); 765 *err = get_ext_path(donor_inode, donor_off, &donor_path);
771 if (err) 766 if (*err)
772 goto out; 767 goto out;
773 depth = ext_depth(donor_inode); 768 depth = ext_depth(donor_inode);
774 dext = donor_path[depth].p_ext; 769 dext = donor_path[depth].p_ext;
775 if (le32_to_cpu(dext->ee_block) +
776 ext4_ext_get_actual_len(dext) <= donor_off) {
777 err = 0;
778 goto out;
779 }
780 tmp_dext = *dext; 770 tmp_dext = *dext;
781 771
782 err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off, 772 *err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
783 donor_off, count - replaced_count); 773 donor_off, count - replaced_count);
784 if (err) 774 if (*err)
785 goto out; 775 goto out;
786 } 776 }
787 777
@@ -796,7 +786,7 @@ out:
796 } 786 }
797 787
798 mext_double_up_write(orig_inode, donor_inode); 788 mext_double_up_write(orig_inode, donor_inode);
799 return err; 789 return replaced_count;
800} 790}
801 791
802/** 792/**
@@ -808,16 +798,17 @@ out:
808 * @data_offset_in_page: block index where data swapping starts 798 * @data_offset_in_page: block index where data swapping starts
809 * @block_len_in_page: the number of blocks to be swapped 799 * @block_len_in_page: the number of blocks to be swapped
810 * @uninit: orig extent is uninitialized or not 800 * @uninit: orig extent is uninitialized or not
801 * @err: pointer to save return value
811 * 802 *
812 * Save the data in original inode blocks and replace original inode extents 803 * Save the data in original inode blocks and replace original inode extents
813 * with donor inode extents by calling mext_replace_branches(). 804 * with donor inode extents by calling mext_replace_branches().
814 * Finally, write out the saved data in new original inode blocks. Return 0 805 * Finally, write out the saved data in new original inode blocks. Return
815 * on success, or a negative error value on failure. 806 * replaced block count.
816 */ 807 */
817static int 808static int
818move_extent_per_page(struct file *o_filp, struct inode *donor_inode, 809move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
819 pgoff_t orig_page_offset, int data_offset_in_page, 810 pgoff_t orig_page_offset, int data_offset_in_page,
820 int block_len_in_page, int uninit) 811 int block_len_in_page, int uninit, int *err)
821{ 812{
822 struct inode *orig_inode = o_filp->f_dentry->d_inode; 813 struct inode *orig_inode = o_filp->f_dentry->d_inode;
823 struct address_space *mapping = orig_inode->i_mapping; 814 struct address_space *mapping = orig_inode->i_mapping;
@@ -829,9 +820,11 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
829 long long offs = orig_page_offset << PAGE_CACHE_SHIFT; 820 long long offs = orig_page_offset << PAGE_CACHE_SHIFT;
830 unsigned long blocksize = orig_inode->i_sb->s_blocksize; 821 unsigned long blocksize = orig_inode->i_sb->s_blocksize;
831 unsigned int w_flags = 0; 822 unsigned int w_flags = 0;
832 unsigned int tmp_data_len, data_len; 823 unsigned int tmp_data_size, data_size, replaced_size;
833 void *fsdata; 824 void *fsdata;
834 int ret, i, jblocks; 825 int i, jblocks;
826 int err2 = 0;
827 int replaced_count = 0;
835 int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits; 828 int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits;
836 829
837 /* 830 /*
@@ -841,8 +834,8 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
841 jblocks = ext4_writepage_trans_blocks(orig_inode) * 2; 834 jblocks = ext4_writepage_trans_blocks(orig_inode) * 2;
842 handle = ext4_journal_start(orig_inode, jblocks); 835 handle = ext4_journal_start(orig_inode, jblocks);
843 if (IS_ERR(handle)) { 836 if (IS_ERR(handle)) {
844 ret = PTR_ERR(handle); 837 *err = PTR_ERR(handle);
845 return ret; 838 return 0;
846 } 839 }
847 840
848 if (segment_eq(get_fs(), KERNEL_DS)) 841 if (segment_eq(get_fs(), KERNEL_DS))
@@ -858,9 +851,9 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
858 * Just swap data blocks between orig and donor. 851 * Just swap data blocks between orig and donor.
859 */ 852 */
860 if (uninit) { 853 if (uninit) {
861 ret = mext_replace_branches(handle, orig_inode, 854 replaced_count = mext_replace_branches(handle, orig_inode,
862 donor_inode, orig_blk_offset, 855 donor_inode, orig_blk_offset,
863 block_len_in_page); 856 block_len_in_page, err);
864 857
865 /* Clear the inode cache not to refer to the old data */ 858 /* Clear the inode cache not to refer to the old data */
866 ext4_ext_invalidate_cache(orig_inode); 859 ext4_ext_invalidate_cache(orig_inode);
@@ -870,27 +863,28 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
870 863
871 offs = (long long)orig_blk_offset << orig_inode->i_blkbits; 864 offs = (long long)orig_blk_offset << orig_inode->i_blkbits;
872 865
873 /* Calculate data_len */ 866 /* Calculate data_size */
874 if ((orig_blk_offset + block_len_in_page - 1) == 867 if ((orig_blk_offset + block_len_in_page - 1) ==
875 ((orig_inode->i_size - 1) >> orig_inode->i_blkbits)) { 868 ((orig_inode->i_size - 1) >> orig_inode->i_blkbits)) {
876 /* Replace the last block */ 869 /* Replace the last block */
877 tmp_data_len = orig_inode->i_size & (blocksize - 1); 870 tmp_data_size = orig_inode->i_size & (blocksize - 1);
878 /* 871 /*
879 * If data_len equal zero, it shows data_len is multiples of 872 * If data_size equal zero, it shows data_size is multiples of
880 * blocksize. So we set appropriate value. 873 * blocksize. So we set appropriate value.
881 */ 874 */
882 if (tmp_data_len == 0) 875 if (tmp_data_size == 0)
883 tmp_data_len = blocksize; 876 tmp_data_size = blocksize;
884 877
885 data_len = tmp_data_len + 878 data_size = tmp_data_size +
886 ((block_len_in_page - 1) << orig_inode->i_blkbits); 879 ((block_len_in_page - 1) << orig_inode->i_blkbits);
887 } else { 880 } else
888 data_len = block_len_in_page << orig_inode->i_blkbits; 881 data_size = block_len_in_page << orig_inode->i_blkbits;
889 } 882
883 replaced_size = data_size;
890 884
891 ret = a_ops->write_begin(o_filp, mapping, offs, data_len, w_flags, 885 *err = a_ops->write_begin(o_filp, mapping, offs, data_size, w_flags,
892 &page, &fsdata); 886 &page, &fsdata);
893 if (unlikely(ret < 0)) 887 if (unlikely(*err < 0))
894 goto out; 888 goto out;
895 889
896 if (!PageUptodate(page)) { 890 if (!PageUptodate(page)) {
@@ -911,10 +905,17 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
911 /* Release old bh and drop refs */ 905 /* Release old bh and drop refs */
912 try_to_release_page(page, 0); 906 try_to_release_page(page, 0);
913 907
914 ret = mext_replace_branches(handle, orig_inode, donor_inode, 908 replaced_count = mext_replace_branches(handle, orig_inode, donor_inode,
915 orig_blk_offset, block_len_in_page); 909 orig_blk_offset, block_len_in_page,
916 if (ret < 0) 910 &err2);
917 goto out; 911 if (err2) {
912 if (replaced_count) {
913 block_len_in_page = replaced_count;
914 replaced_size =
915 block_len_in_page << orig_inode->i_blkbits;
916 } else
917 goto out;
918 }
918 919
919 /* Clear the inode cache not to refer to the old data */ 920 /* Clear the inode cache not to refer to the old data */
920 ext4_ext_invalidate_cache(orig_inode); 921 ext4_ext_invalidate_cache(orig_inode);
@@ -928,16 +929,16 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
928 bh = bh->b_this_page; 929 bh = bh->b_this_page;
929 930
930 for (i = 0; i < block_len_in_page; i++) { 931 for (i = 0; i < block_len_in_page; i++) {
931 ret = ext4_get_block(orig_inode, 932 *err = ext4_get_block(orig_inode,
932 (sector_t)(orig_blk_offset + i), bh, 0); 933 (sector_t)(orig_blk_offset + i), bh, 0);
933 if (ret < 0) 934 if (*err < 0)
934 goto out; 935 goto out;
935 936
936 if (bh->b_this_page != NULL) 937 if (bh->b_this_page != NULL)
937 bh = bh->b_this_page; 938 bh = bh->b_this_page;
938 } 939 }
939 940
940 ret = a_ops->write_end(o_filp, mapping, offs, data_len, data_len, 941 *err = a_ops->write_end(o_filp, mapping, offs, data_size, replaced_size,
941 page, fsdata); 942 page, fsdata);
942 page = NULL; 943 page = NULL;
943 944
@@ -951,7 +952,10 @@ out:
951out2: 952out2:
952 ext4_journal_stop(handle); 953 ext4_journal_stop(handle);
953 954
954 return ret < 0 ? ret : 0; 955 if (err2)
956 *err = err2;
957
958 return replaced_count;
955} 959}
956 960
957/** 961/**
@@ -1367,15 +1371,17 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1367 while (orig_page_offset <= seq_end_page) { 1371 while (orig_page_offset <= seq_end_page) {
1368 1372
1369 /* Swap original branches with new branches */ 1373 /* Swap original branches with new branches */
1370 ret1 = move_extent_per_page(o_filp, donor_inode, 1374 block_len_in_page = move_extent_per_page(
1375 o_filp, donor_inode,
1371 orig_page_offset, 1376 orig_page_offset,
1372 data_offset_in_page, 1377 data_offset_in_page,
1373 block_len_in_page, uninit); 1378 block_len_in_page, uninit,
1374 if (ret1 < 0) 1379 &ret1);
1375 goto out; 1380
1376 orig_page_offset++;
1377 /* Count how many blocks we have exchanged */ 1381 /* Count how many blocks we have exchanged */
1378 *moved_len += block_len_in_page; 1382 *moved_len += block_len_in_page;
1383 if (ret1 < 0)
1384 goto out;
1379 if (*moved_len > len) { 1385 if (*moved_len > len) {
1380 ext4_error(orig_inode->i_sb, __func__, 1386 ext4_error(orig_inode->i_sb, __func__,
1381 "We replaced blocks too much! " 1387 "We replaced blocks too much! "
@@ -1385,6 +1391,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1385 goto out; 1391 goto out;
1386 } 1392 }
1387 1393
1394 orig_page_offset++;
1388 data_offset_in_page = 0; 1395 data_offset_in_page = 0;
1389 rest_blocks -= block_len_in_page; 1396 rest_blocks -= block_len_in_page;
1390 if (rest_blocks > blocks_per_page) 1397 if (rest_blocks > blocks_per_page)