mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	btrfs: Drop EXTENT_UPTODATE check in hole punching and direct locking
In these instances, we are trying to determine if a page has been accessed since we began the operation for the sake of retry. This is easily accomplished by doing a gang lookup in the page mapping radix tree, and it saves us the dependency on the flag (so that we might eventually delete it). btrfs_page_exists_in_range borrows heavily from find_get_page, replacing the radix tree look up with a gang lookup of 1, so that we can find the next highest page >= index and see if it falls into our lock range. Signed-off-by: Chris Mason <clm@fb.com> Signed-off-by: Alex Gartrell <agartrell@fb.com>
This commit is contained in:
		
							parent
							
								
									0e378df15c
								
							
						
					
					
						commit
						fc4adbff82
					
				
					 3 changed files with 71 additions and 7 deletions
				
			
		| 
						 | 
					@ -284,4 +284,6 @@ static inline void btrfs_inode_resume_unlocked_dio(struct inode *inode)
 | 
				
			||||||
		  &BTRFS_I(inode)->runtime_flags);
 | 
							  &BTRFS_I(inode)->runtime_flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool btrfs_page_exists_in_range(struct inode *inode, loff_t start, loff_t end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2266,9 +2266,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
 | 
				
			||||||
		if ((!ordered ||
 | 
							if ((!ordered ||
 | 
				
			||||||
		    (ordered->file_offset + ordered->len <= lockstart ||
 | 
							    (ordered->file_offset + ordered->len <= lockstart ||
 | 
				
			||||||
		     ordered->file_offset > lockend)) &&
 | 
							     ordered->file_offset > lockend)) &&
 | 
				
			||||||
		     !test_range_bit(&BTRFS_I(inode)->io_tree, lockstart,
 | 
							     !btrfs_page_exists_in_range(inode, lockstart, lockend)) {
 | 
				
			||||||
				     lockend, EXTENT_UPTODATE, 0,
 | 
					 | 
				
			||||||
				     cached_state)) {
 | 
					 | 
				
			||||||
			if (ordered)
 | 
								if (ordered)
 | 
				
			||||||
				btrfs_put_ordered_extent(ordered);
 | 
									btrfs_put_ordered_extent(ordered);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6735,6 +6735,71 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len,
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool btrfs_page_exists_in_range(struct inode *inode, loff_t start, loff_t end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct radix_tree_root *root = &inode->i_mapping->page_tree;
 | 
				
			||||||
 | 
						int found = false;
 | 
				
			||||||
 | 
						void **pagep = NULL;
 | 
				
			||||||
 | 
						struct page *page = NULL;
 | 
				
			||||||
 | 
						int start_idx;
 | 
				
			||||||
 | 
						int end_idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start_idx = start >> PAGE_CACHE_SHIFT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * end is the last byte in the last page.  end == start is legal
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						end_idx = end >> PAGE_CACHE_SHIFT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Most of the code in this while loop is lifted from
 | 
				
			||||||
 | 
						 * find_get_page.  It's been modified to begin searching from a
 | 
				
			||||||
 | 
						 * page and return just the first page found in that range.  If the
 | 
				
			||||||
 | 
						 * found idx is less than or equal to the end idx then we know that
 | 
				
			||||||
 | 
						 * a page exists.  If no pages are found or if those pages are
 | 
				
			||||||
 | 
						 * outside of the range then we're fine (yay!) */
 | 
				
			||||||
 | 
						while (page == NULL &&
 | 
				
			||||||
 | 
						       radix_tree_gang_lookup_slot(root, &pagep, NULL, start_idx, 1)) {
 | 
				
			||||||
 | 
							page = radix_tree_deref_slot(pagep);
 | 
				
			||||||
 | 
							if (unlikely(!page))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (radix_tree_exception(page)) {
 | 
				
			||||||
 | 
								if (radix_tree_deref_retry(page))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * Otherwise, shmem/tmpfs must be storing a swap entry
 | 
				
			||||||
 | 
								 * here as an exceptional entry: so return it without
 | 
				
			||||||
 | 
								 * attempting to raise page count.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								break; /* TODO: Is this relevant for this use case? */
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!page_cache_get_speculative(page))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Has the page moved?
 | 
				
			||||||
 | 
							 * This is part of the lockless pagecache protocol. See
 | 
				
			||||||
 | 
							 * include/linux/pagemap.h for details.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (unlikely(page != *pagep)) {
 | 
				
			||||||
 | 
								page_cache_release(page);
 | 
				
			||||||
 | 
								page = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (page) {
 | 
				
			||||||
 | 
							if (page->index <= end_idx)
 | 
				
			||||||
 | 
								found = true;
 | 
				
			||||||
 | 
							page_cache_release(page);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						return found;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
 | 
					static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
 | 
				
			||||||
			      struct extent_state **cached_state, int writing)
 | 
								      struct extent_state **cached_state, int writing)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -6759,10 +6824,9 @@ static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
 | 
				
			||||||
		 * invalidate needs to happen so that reads after a write do not
 | 
							 * invalidate needs to happen so that reads after a write do not
 | 
				
			||||||
		 * get stale data.
 | 
							 * get stale data.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (!ordered && (!writing ||
 | 
							if (!ordered &&
 | 
				
			||||||
		    !test_range_bit(&BTRFS_I(inode)->io_tree,
 | 
							    (!writing ||
 | 
				
			||||||
				    lockstart, lockend, EXTENT_UPTODATE, 0,
 | 
							     !btrfs_page_exists_in_range(inode, lockstart, lockend)))
 | 
				
			||||||
				    *cached_state)))
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
 | 
							unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue