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:
		
							parent
							
								
									ecb8bea87d
								
							
						
					
					
						commit
						1f12bd0632
					
				| @ -871,6 +871,7 @@ static int cluster_pages_for_defrag(struct inode *inode, | ||||
| 	u64 isize = i_size_read(inode); | ||||
| 	u64 page_start; | ||||
| 	u64 page_end; | ||||
| 	u64 page_cnt; | ||||
| 	int ret; | ||||
| 	int i; | ||||
| 	int i_done; | ||||
| @ -879,19 +880,21 @@ static int cluster_pages_for_defrag(struct inode *inode, | ||||
| 	struct extent_io_tree *tree; | ||||
| 	gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping); | ||||
| 
 | ||||
| 	if (isize == 0) | ||||
| 		return 0; | ||||
| 	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, | ||||
| 					   num_pages << PAGE_CACHE_SHIFT); | ||||
| 					   page_cnt << PAGE_CACHE_SHIFT); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	i_done = 0; | ||||
| 	tree = &BTRFS_I(inode)->io_tree; | ||||
| 
 | ||||
| 	/* step one, lock all the pages */ | ||||
| 	for (i = 0; i < num_pages; i++) { | ||||
| 	for (i = 0; i < page_cnt; i++) { | ||||
| 		struct page *page; | ||||
| again: | ||||
| 		page = find_or_create_page(inode->i_mapping, | ||||
| @ -913,6 +916,15 @@ again: | ||||
| 			btrfs_start_ordered_extent(inode, ordered, 1); | ||||
| 			btrfs_put_ordered_extent(ordered); | ||||
| 			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)) { | ||||
| @ -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) { | ||||
| 			unlock_page(page); | ||||
| 			page_cache_release(page); | ||||
| @ -967,12 +970,12 @@ again: | ||||
| 			  EXTENT_DO_ACCOUNTING, 0, 0, &cached_state, | ||||
| 			  GFP_NOFS); | ||||
| 
 | ||||
| 	if (i_done != num_pages) { | ||||
| 	if (i_done != page_cnt) { | ||||
| 		spin_lock(&BTRFS_I(inode)->lock); | ||||
| 		BTRFS_I(inode)->outstanding_extents++; | ||||
| 		spin_unlock(&BTRFS_I(inode)->lock); | ||||
| 		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]); | ||||
| 		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; | ||||
| 
 | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user