Btrfs: fix the mismatch of page->mapping
commit 600a45e1d5
(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>
This commit is contained in:
@@ -871,6 +871,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
|
|||||||
u64 isize = i_size_read(inode);
|
u64 isize = i_size_read(inode);
|
||||||
u64 page_start;
|
u64 page_start;
|
||||||
u64 page_end;
|
u64 page_end;
|
||||||
|
u64 page_cnt;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
int i_done;
|
int i_done;
|
||||||
@@ -879,19 +880,21 @@ static int cluster_pages_for_defrag(struct inode *inode,
|
|||||||
struct extent_io_tree *tree;
|
struct extent_io_tree *tree;
|
||||||
gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
|
gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);
|
||||||
|
|
||||||
if (isize == 0)
|
|
||||||
return 0;
|
|
||||||
file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
|
file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
|
||||||
|
if (!isize || start_index > file_end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
page_cnt = min_t(u64, (u64)num_pages, (u64)file_end - start_index + 1);
|
||||||
|
|
||||||
ret = btrfs_delalloc_reserve_space(inode,
|
ret = btrfs_delalloc_reserve_space(inode,
|
||||||
num_pages << PAGE_CACHE_SHIFT);
|
page_cnt << PAGE_CACHE_SHIFT);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
i_done = 0;
|
i_done = 0;
|
||||||
tree = &BTRFS_I(inode)->io_tree;
|
tree = &BTRFS_I(inode)->io_tree;
|
||||||
|
|
||||||
/* step one, lock all the pages */
|
/* step one, lock all the pages */
|
||||||
for (i = 0; i < num_pages; i++) {
|
for (i = 0; i < page_cnt; i++) {
|
||||||
struct page *page;
|
struct page *page;
|
||||||
again:
|
again:
|
||||||
page = find_or_create_page(inode->i_mapping,
|
page = find_or_create_page(inode->i_mapping,
|
||||||
@@ -913,6 +916,15 @@ again:
|
|||||||
btrfs_start_ordered_extent(inode, ordered, 1);
|
btrfs_start_ordered_extent(inode, ordered, 1);
|
||||||
btrfs_put_ordered_extent(ordered);
|
btrfs_put_ordered_extent(ordered);
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
|
/*
|
||||||
|
* we unlocked the page above, so we need check if
|
||||||
|
* it was released or not.
|
||||||
|
*/
|
||||||
|
if (page->mapping != inode->i_mapping) {
|
||||||
|
unlock_page(page);
|
||||||
|
page_cache_release(page);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PageUptodate(page)) {
|
if (!PageUptodate(page)) {
|
||||||
@@ -926,15 +938,6 @@ again:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isize = i_size_read(inode);
|
|
||||||
file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
|
|
||||||
if (!isize || page->index > file_end) {
|
|
||||||
/* whoops, we blew past eof, skip this page */
|
|
||||||
unlock_page(page);
|
|
||||||
page_cache_release(page);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page->mapping != inode->i_mapping) {
|
if (page->mapping != inode->i_mapping) {
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
@@ -967,12 +970,12 @@ again:
|
|||||||
EXTENT_DO_ACCOUNTING, 0, 0, &cached_state,
|
EXTENT_DO_ACCOUNTING, 0, 0, &cached_state,
|
||||||
GFP_NOFS);
|
GFP_NOFS);
|
||||||
|
|
||||||
if (i_done != num_pages) {
|
if (i_done != page_cnt) {
|
||||||
spin_lock(&BTRFS_I(inode)->lock);
|
spin_lock(&BTRFS_I(inode)->lock);
|
||||||
BTRFS_I(inode)->outstanding_extents++;
|
BTRFS_I(inode)->outstanding_extents++;
|
||||||
spin_unlock(&BTRFS_I(inode)->lock);
|
spin_unlock(&BTRFS_I(inode)->lock);
|
||||||
btrfs_delalloc_release_space(inode,
|
btrfs_delalloc_release_space(inode,
|
||||||
(num_pages - i_done) << PAGE_CACHE_SHIFT);
|
(page_cnt - i_done) << PAGE_CACHE_SHIFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -997,7 +1000,7 @@ out:
|
|||||||
unlock_page(pages[i]);
|
unlock_page(pages[i]);
|
||||||
page_cache_release(pages[i]);
|
page_cache_release(pages[i]);
|
||||||
}
|
}
|
||||||
btrfs_delalloc_release_space(inode, num_pages << PAGE_CACHE_SHIFT);
|
btrfs_delalloc_release_space(inode, page_cnt << PAGE_CACHE_SHIFT);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user