diff options
Diffstat (limited to 'fs/btrfs/extent_io.c')
| -rw-r--r-- | fs/btrfs/extent_io.c | 145 | 
1 files changed, 74 insertions, 71 deletions
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index c137f98fbd24..ef25c7d048e2 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4222,6 +4222,76 @@ static void __free_extent_buffer(struct extent_buffer *eb)  	kmem_cache_free(extent_buffer_cache, eb);  } +static int extent_buffer_under_io(struct extent_buffer *eb) +{ +	return (atomic_read(&eb->io_pages) || +		test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) || +		test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)); +} + +/* + * Helper for releasing extent buffer page. + */ +static void btrfs_release_extent_buffer_page(struct extent_buffer *eb, +						unsigned long start_idx) +{ +	unsigned long index; +	unsigned long num_pages; +	struct page *page; +	int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags); + +	BUG_ON(extent_buffer_under_io(eb)); + +	num_pages = num_extent_pages(eb->start, eb->len); +	index = start_idx + num_pages; +	if (start_idx >= index) +		return; + +	do { +		index--; +		page = extent_buffer_page(eb, index); +		if (page && mapped) { +			spin_lock(&page->mapping->private_lock); +			/* +			 * We do this since we'll remove the pages after we've +			 * removed the eb from the radix tree, so we could race +			 * and have this page now attached to the new eb.  So +			 * only clear page_private if it's still connected to +			 * this eb. +			 */ +			if (PagePrivate(page) && +			    page->private == (unsigned long)eb) { +				BUG_ON(test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)); +				BUG_ON(PageDirty(page)); +				BUG_ON(PageWriteback(page)); +				/* +				 * We need to make sure we haven't be attached +				 * to a new eb. +				 */ +				ClearPagePrivate(page); +				set_page_private(page, 0); +				/* One for the page private */ +				page_cache_release(page); +			} +			spin_unlock(&page->mapping->private_lock); + +		} +		if (page) { +			/* One for when we alloced the page */ +			page_cache_release(page); +		} +	} while (index != start_idx); +} + +/* + * Helper for releasing the extent buffer. + */ +static inline void btrfs_release_extent_buffer(struct extent_buffer *eb) +{ +	btrfs_release_extent_buffer_page(eb, 0); +	__free_extent_buffer(eb); +} +  static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,  						   u64 start,  						   unsigned long len, @@ -4276,7 +4346,10 @@ struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src)  	for (i = 0; i < num_pages; i++) {  		p = alloc_page(GFP_ATOMIC); -		BUG_ON(!p); +		if (!p) { +			btrfs_release_extent_buffer(new); +			return NULL; +		}  		attach_extent_buffer_page(new, p);  		WARN_ON(PageDirty(p));  		SetPageUptodate(p); @@ -4317,76 +4390,6 @@ err:  	return NULL;  } -static int extent_buffer_under_io(struct extent_buffer *eb) -{ -	return (atomic_read(&eb->io_pages) || -		test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) || -		test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)); -} - -/* - * Helper for releasing extent buffer page. - */ -static void btrfs_release_extent_buffer_page(struct extent_buffer *eb, -						unsigned long start_idx) -{ -	unsigned long index; -	unsigned long num_pages; -	struct page *page; -	int mapped = !test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags); - -	BUG_ON(extent_buffer_under_io(eb)); - -	num_pages = num_extent_pages(eb->start, eb->len); -	index = start_idx + num_pages; -	if (start_idx >= index) -		return; - -	do { -		index--; -		page = extent_buffer_page(eb, index); -		if (page && mapped) { -			spin_lock(&page->mapping->private_lock); -			/* -			 * We do this since we'll remove the pages after we've -			 * removed the eb from the radix tree, so we could race -			 * and have this page now attached to the new eb.  So -			 * only clear page_private if it's still connected to -			 * this eb. -			 */ -			if (PagePrivate(page) && -			    page->private == (unsigned long)eb) { -				BUG_ON(test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)); -				BUG_ON(PageDirty(page)); -				BUG_ON(PageWriteback(page)); -				/* -				 * We need to make sure we haven't be attached -				 * to a new eb. -				 */ -				ClearPagePrivate(page); -				set_page_private(page, 0); -				/* One for the page private */ -				page_cache_release(page); -			} -			spin_unlock(&page->mapping->private_lock); - -		} -		if (page) { -			/* One for when we alloced the page */ -			page_cache_release(page); -		} -	} while (index != start_idx); -} - -/* - * Helper for releasing the extent buffer. - */ -static inline void btrfs_release_extent_buffer(struct extent_buffer *eb) -{ -	btrfs_release_extent_buffer_page(eb, 0); -	__free_extent_buffer(eb); -} -  static void check_buffer_tree_ref(struct extent_buffer *eb)  {  	int refs;  | 
