Btrfs: btrfs_release_extent_buffer_page didn't free pages of dummy extent
btrfs_release_extent_buffer_page() can't handle dummy extent that allocated by btrfs_clone_extent_buffer() properly. That is because reference count of pages that allocated by btrfs_clone_extent_buffer() was 2, 1 by alloc_page(), and another by attach_extent_buffer_page(). Running following command repeatly can check this memory leak problem btrfs inspect-internal inode-resolve 256 /mnt/btrfs Signed-off-by: Chien-Kuan Yeh <ckya@synology.com> Signed-off-by: Forrest Liu <forrestl@synology.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Tested-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
parent
6e17d30bfa
commit
5d2361db48
|
@ -4560,36 +4560,37 @@ static void btrfs_release_extent_buffer_page(struct extent_buffer *eb)
|
||||||
do {
|
do {
|
||||||
index--;
|
index--;
|
||||||
page = eb->pages[index];
|
page = eb->pages[index];
|
||||||
if (page && mapped) {
|
if (!page)
|
||||||
|
continue;
|
||||||
|
if (mapped)
|
||||||
spin_lock(&page->mapping->private_lock);
|
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 do this since we'll remove the pages after we've
|
* We need to make sure we haven't be attached
|
||||||
* removed the eb from the radix tree, so we could race
|
* to a new eb.
|
||||||
* 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) &&
|
ClearPagePrivate(page);
|
||||||
page->private == (unsigned long)eb) {
|
set_page_private(page, 0);
|
||||||
BUG_ON(test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags));
|
/* One for the page private */
|
||||||
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);
|
page_cache_release(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mapped)
|
||||||
|
spin_unlock(&page->mapping->private_lock);
|
||||||
|
|
||||||
|
/* One for when we alloced the page */
|
||||||
|
page_cache_release(page);
|
||||||
} while (index != 0);
|
} while (index != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue