diff options
author | Liu Bo <liubo2009@cn.fujitsu.com> | 2012-03-29 09:57:44 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2012-03-29 09:57:44 -0400 |
commit | 1f12bd063285b059cb63315d1424dae1ddd87a64 (patch) | |
tree | d0c9c7be653b3306f9cfb76ba9805f36c9295e37 /fs/btrfs/ioctl.c | |
parent | ecb8bea87d05fd2d1fc0718e1e4bbf09c7c6045a (diff) |
Btrfs: fix the mismatch of page->mapping
commit 600a45e1d5e376f679ff9ecc4ce9452710a6d27c
(Btrfs: fix deadlock on page lock when doing auto-defragment)
fixes the deadlock on page, but it also introduces another bug.
A page may have been truncated after unlock & lock.
So we need to find it again to get the right one.
And since we've held i_mutex lock, inode size remains unchanged and
we can drop isize overflow checks.
Signed-off-by: Liu Bo <liubo2009@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 35 |
1 files changed, 19 insertions, 16 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 45910d4b8f65..de46fa218590 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -871,6 +871,7 @@ static int cluster_pages_for_defrag(struct inode *inode, | |||
871 | u64 isize = i_size_read(inode); | 871 | u64 isize = i_size_read(inode); |
872 | u64 page_start; | 872 | u64 page_start; |
873 | u64 page_end; | 873 | u64 page_end; |
874 | u64 page_cnt; | ||
874 | int ret; | 875 | int ret; |
875 | int i; | 876 | int i; |
876 | int i_done; | 877 | int i_done; |
@@ -879,19 +880,21 @@ static int cluster_pages_for_defrag(struct inode *inode, | |||
879 | struct extent_io_tree *tree; | 880 | struct extent_io_tree *tree; |
880 | gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); | 881 | gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); |
881 | 882 | ||
882 | if (isize == 0) | ||
883 | return 0; | ||
884 | file_end = (isize - 1) >> PAGE_CACHE_SHIFT; | 883 | file_end = (isize - 1) >> PAGE_CACHE_SHIFT; |
884 | if (!isize || start_index > file_end) | ||
885 | return 0; | ||
886 | |||
887 | page_cnt = min_t(u64, (u64)num_pages, (u64)file_end - start_index + 1); | ||
885 | 888 | ||
886 | ret = btrfs_delalloc_reserve_space(inode, | 889 | ret = btrfs_delalloc_reserve_space(inode, |
887 | num_pages << PAGE_CACHE_SHIFT); | 890 | page_cnt << PAGE_CACHE_SHIFT); |
888 | if (ret) | 891 | if (ret) |
889 | return ret; | 892 | return ret; |
890 | i_done = 0; | 893 | i_done = 0; |
891 | tree = &BTRFS_I(inode)->io_tree; | 894 | tree = &BTRFS_I(inode)->io_tree; |
892 | 895 | ||
893 | /* step one, lock all the pages */ | 896 | /* step one, lock all the pages */ |
894 | for (i = 0; i < num_pages; i++) { | 897 | for (i = 0; i < page_cnt; i++) { |
895 | struct page *page; | 898 | struct page *page; |
896 | again: | 899 | again: |
897 | page = find_or_create_page(inode->i_mapping, | 900 | page = find_or_create_page(inode->i_mapping, |
@@ -913,6 +916,15 @@ again: | |||
913 | btrfs_start_ordered_extent(inode, ordered, 1); | 916 | btrfs_start_ordered_extent(inode, ordered, 1); |
914 | btrfs_put_ordered_extent(ordered); | 917 | btrfs_put_ordered_extent(ordered); |
915 | lock_page(page); | 918 | lock_page(page); |
919 | /* | ||
920 | * we unlocked the page above, so we need check if | ||
921 | * it was released or not. | ||
922 | */ | ||
923 | if (page->mapping != inode->i_mapping) { | ||
924 | unlock_page(page); | ||
925 | page_cache_release(page); | ||
926 | goto again; | ||
927 | } | ||
916 | } | 928 | } |
917 | 929 | ||
918 | if (!PageUptodate(page)) { | 930 | if (!PageUptodate(page)) { |
@@ -926,15 +938,6 @@ again: | |||
926 | } | 938 | } |
927 | } | 939 | } |
928 | 940 | ||
929 | isize = i_size_read(inode); | ||
930 | file_end = (isize - 1) >> PAGE_CACHE_SHIFT; | ||
931 | if (!isize || page->index > file_end) { | ||
932 | /* whoops, we blew past eof, skip this page */ | ||
933 | unlock_page(page); | ||
934 | page_cache_release(page); | ||
935 | break; | ||
936 | } | ||
937 | |||
938 | if (page->mapping != inode->i_mapping) { | 941 | if (page->mapping != inode->i_mapping) { |
939 | unlock_page(page); | 942 | unlock_page(page); |
940 | page_cache_release(page); | 943 | page_cache_release(page); |
@@ -967,12 +970,12 @@ again: | |||
967 | EXTENT_DO_ACCOUNTING, 0, 0, &cached_state, | 970 | EXTENT_DO_ACCOUNTING, 0, 0, &cached_state, |
968 | GFP_NOFS); | 971 | GFP_NOFS); |
969 | 972 | ||
970 | if (i_done != num_pages) { | 973 | if (i_done != page_cnt) { |
971 | spin_lock(&BTRFS_I(inode)->lock); | 974 | spin_lock(&BTRFS_I(inode)->lock); |
972 | BTRFS_I(inode)->outstanding_extents++; | 975 | BTRFS_I(inode)->outstanding_extents++; |
973 | spin_unlock(&BTRFS_I(inode)->lock); | 976 | spin_unlock(&BTRFS_I(inode)->lock); |
974 | btrfs_delalloc_release_space(inode, | 977 | btrfs_delalloc_release_space(inode, |
975 | (num_pages - i_done) << PAGE_CACHE_SHIFT); | 978 | (page_cnt - i_done) << PAGE_CACHE_SHIFT); |
976 | } | 979 | } |
977 | 980 | ||
978 | 981 | ||
@@ -997,7 +1000,7 @@ out: | |||
997 | unlock_page(pages[i]); | 1000 | unlock_page(pages[i]); |
998 | page_cache_release(pages[i]); | 1001 | page_cache_release(pages[i]); |
999 | } | 1002 | } |
1000 | btrfs_delalloc_release_space(inode, num_pages << PAGE_CACHE_SHIFT); | 1003 | btrfs_delalloc_release_space(inode, page_cnt << PAGE_CACHE_SHIFT); |
1001 | return ret; | 1004 | return ret; |
1002 | 1005 | ||
1003 | } | 1006 | } |