diff options
author | Joel Becker <joel.becker@oracle.com> | 2010-07-06 17:36:06 -0400 |
---|---|---|
committer | Joel Becker <joel.becker@oracle.com> | 2010-07-08 16:24:49 -0400 |
commit | a4bfb4cf11fd2211b788af59dc8a8b4394bca227 (patch) | |
tree | c06f45ac5a992575a0e47dbc80bdf187db030cca /fs/ocfs2/file.c | |
parent | 1739da40543ed2129050ccfa8a076a851ab6ed00 (diff) |
ocfs2: When zero extending, do it by page.
ocfs2_zero_extend() does its zeroing block by block, but it calls a
function named ocfs2_write_zero_page(). Let's have
ocfs2_write_zero_page() handle the page level. From
ocfs2_zero_extend()'s perspective, it is now page-at-a-time.
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Cc: stable@kernel.org
Diffstat (limited to 'fs/ocfs2/file.c')
-rw-r--r-- | fs/ocfs2/file.c | 118 |
1 files changed, 84 insertions, 34 deletions
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 6a13ea64c447..4cfc976a9067 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c | |||
@@ -724,28 +724,55 @@ leave: | |||
724 | return status; | 724 | return status; |
725 | } | 725 | } |
726 | 726 | ||
727 | /* | ||
728 | * While a write will already be ordering the data, a truncate will not. | ||
729 | * Thus, we need to explicitly order the zeroed pages. | ||
730 | */ | ||
731 | static handle_t *ocfs2_zero_start_ordered_transaction(struct inode *inode) | ||
732 | { | ||
733 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
734 | handle_t *handle = NULL; | ||
735 | int ret = 0; | ||
736 | |||
737 | if (!ocfs2_should_order_data(inode)) | ||
738 | goto out; | ||
739 | |||
740 | handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); | ||
741 | if (IS_ERR(handle)) { | ||
742 | ret = -ENOMEM; | ||
743 | mlog_errno(ret); | ||
744 | goto out; | ||
745 | } | ||
746 | |||
747 | ret = ocfs2_jbd2_file_inode(handle, inode); | ||
748 | if (ret < 0) | ||
749 | mlog_errno(ret); | ||
750 | |||
751 | out: | ||
752 | if (ret) { | ||
753 | if (!IS_ERR(handle)) | ||
754 | ocfs2_commit_trans(osb, handle); | ||
755 | handle = ERR_PTR(ret); | ||
756 | } | ||
757 | return handle; | ||
758 | } | ||
759 | |||
727 | /* Some parts of this taken from generic_cont_expand, which turned out | 760 | /* Some parts of this taken from generic_cont_expand, which turned out |
728 | * to be too fragile to do exactly what we need without us having to | 761 | * to be too fragile to do exactly what we need without us having to |
729 | * worry about recursive locking in ->write_begin() and ->write_end(). */ | 762 | * worry about recursive locking in ->write_begin() and ->write_end(). */ |
730 | static int ocfs2_write_zero_page(struct inode *inode, | 763 | static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from, |
731 | u64 size) | 764 | u64 abs_to) |
732 | { | 765 | { |
733 | struct address_space *mapping = inode->i_mapping; | 766 | struct address_space *mapping = inode->i_mapping; |
734 | struct page *page; | 767 | struct page *page; |
735 | unsigned long index; | 768 | unsigned long index = abs_from >> PAGE_CACHE_SHIFT; |
736 | unsigned int offset; | ||
737 | handle_t *handle = NULL; | 769 | handle_t *handle = NULL; |
738 | int ret; | 770 | int ret; |
771 | unsigned zero_from, zero_to, block_start, block_end; | ||
739 | 772 | ||
740 | offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */ | 773 | BUG_ON(abs_from >= abs_to); |
741 | /* ugh. in prepare/commit_write, if from==to==start of block, we | 774 | BUG_ON(abs_to > (((u64)index + 1) << PAGE_CACHE_SHIFT)); |
742 | ** skip the prepare. make sure we never send an offset for the start | 775 | BUG_ON(abs_from & (inode->i_blkbits - 1)); |
743 | ** of a block | ||
744 | */ | ||
745 | if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) { | ||
746 | offset++; | ||
747 | } | ||
748 | index = size >> PAGE_CACHE_SHIFT; | ||
749 | 776 | ||
750 | page = grab_cache_page(mapping, index); | 777 | page = grab_cache_page(mapping, index); |
751 | if (!page) { | 778 | if (!page) { |
@@ -754,31 +781,51 @@ static int ocfs2_write_zero_page(struct inode *inode, | |||
754 | goto out; | 781 | goto out; |
755 | } | 782 | } |
756 | 783 | ||
757 | ret = ocfs2_prepare_write_nolock(inode, page, offset, offset); | 784 | /* Get the offsets within the page that we want to zero */ |
758 | if (ret < 0) { | 785 | zero_from = abs_from & (PAGE_CACHE_SIZE - 1); |
759 | mlog_errno(ret); | 786 | zero_to = abs_to & (PAGE_CACHE_SIZE - 1); |
760 | goto out_unlock; | 787 | if (!zero_to) |
761 | } | 788 | zero_to = PAGE_CACHE_SIZE; |
762 | 789 | ||
763 | if (ocfs2_should_order_data(inode)) { | 790 | /* We know that zero_from is block aligned */ |
764 | handle = ocfs2_start_walk_page_trans(inode, page, offset, | 791 | for (block_start = zero_from; block_start < zero_to; |
765 | offset); | 792 | block_start = block_end) { |
766 | if (IS_ERR(handle)) { | 793 | block_end = block_start + (1 << inode->i_blkbits); |
767 | ret = PTR_ERR(handle); | 794 | |
768 | handle = NULL; | 795 | /* |
796 | * block_start is block-aligned. Bump it by one to | ||
797 | * force ocfs2_{prepare,commit}_write() to zero the | ||
798 | * whole block. | ||
799 | */ | ||
800 | ret = ocfs2_prepare_write_nolock(inode, page, | ||
801 | block_start + 1, | ||
802 | block_start + 1); | ||
803 | if (ret < 0) { | ||
804 | mlog_errno(ret); | ||
769 | goto out_unlock; | 805 | goto out_unlock; |
770 | } | 806 | } |
771 | } | ||
772 | 807 | ||
773 | /* must not update i_size! */ | 808 | if (!handle) { |
774 | ret = block_commit_write(page, offset, offset); | 809 | handle = ocfs2_zero_start_ordered_transaction(inode); |
775 | if (ret < 0) | 810 | if (IS_ERR(handle)) { |
776 | mlog_errno(ret); | 811 | ret = PTR_ERR(handle); |
777 | else | 812 | handle = NULL; |
778 | ret = 0; | 813 | break; |
814 | } | ||
815 | } | ||
816 | |||
817 | /* must not update i_size! */ | ||
818 | ret = block_commit_write(page, block_start + 1, | ||
819 | block_start + 1); | ||
820 | if (ret < 0) | ||
821 | mlog_errno(ret); | ||
822 | else | ||
823 | ret = 0; | ||
824 | } | ||
779 | 825 | ||
780 | if (handle) | 826 | if (handle) |
781 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); | 827 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); |
828 | |||
782 | out_unlock: | 829 | out_unlock: |
783 | unlock_page(page); | 830 | unlock_page(page); |
784 | page_cache_release(page); | 831 | page_cache_release(page); |
@@ -790,18 +837,21 @@ static int ocfs2_zero_extend(struct inode *inode, | |||
790 | u64 zero_to_size) | 837 | u64 zero_to_size) |
791 | { | 838 | { |
792 | int ret = 0; | 839 | int ret = 0; |
793 | u64 start_off; | 840 | u64 start_off, next_off; |
794 | struct super_block *sb = inode->i_sb; | 841 | struct super_block *sb = inode->i_sb; |
795 | 842 | ||
796 | start_off = ocfs2_align_bytes_to_blocks(sb, i_size_read(inode)); | 843 | start_off = ocfs2_align_bytes_to_blocks(sb, i_size_read(inode)); |
797 | while (start_off < zero_to_size) { | 844 | while (start_off < zero_to_size) { |
798 | ret = ocfs2_write_zero_page(inode, start_off); | 845 | next_off = (start_off & PAGE_CACHE_MASK) + PAGE_CACHE_SIZE; |
846 | if (next_off > zero_to_size) | ||
847 | next_off = zero_to_size; | ||
848 | ret = ocfs2_write_zero_page(inode, start_off, next_off); | ||
799 | if (ret < 0) { | 849 | if (ret < 0) { |
800 | mlog_errno(ret); | 850 | mlog_errno(ret); |
801 | goto out; | 851 | goto out; |
802 | } | 852 | } |
803 | 853 | ||
804 | start_off += sb->s_blocksize; | 854 | start_off = next_off; |
805 | 855 | ||
806 | /* | 856 | /* |
807 | * Very large extends have the potential to lock up | 857 | * Very large extends have the potential to lock up |