diff options
author | Filipe Manana <fdmanana@suse.com> | 2014-10-06 17:14:24 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-11-20 20:14:26 -0500 |
commit | 40ae837b43565c47ee171e704d05947fd5c2bae9 (patch) | |
tree | 520debe05ef897cfd6a1031ff16033d82c9eb8e2 /fs | |
parent | fce2a4e6b2da1bcc03837aff53b5bcb02bee4cee (diff) |
Btrfs: don't leak pages and memory on compressed write error
In inode.c:submit_compressed_extents(), if we fail before calling
btrfs_submit_compressed_write(), or when that function fails, we
were freeing the async_extent structure without releasing its pages
and freeing the pages array.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/inode.c | 28 |
1 files changed, 19 insertions, 9 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ec68eaef05d7..b759585fbc6a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -633,6 +633,22 @@ free_pages_out: | |||
633 | goto out; | 633 | goto out; |
634 | } | 634 | } |
635 | 635 | ||
636 | static void free_async_extent_pages(struct async_extent *async_extent) | ||
637 | { | ||
638 | int i; | ||
639 | |||
640 | if (!async_extent->pages) | ||
641 | return; | ||
642 | |||
643 | for (i = 0; i < async_extent->nr_pages; i++) { | ||
644 | WARN_ON(async_extent->pages[i]->mapping); | ||
645 | page_cache_release(async_extent->pages[i]); | ||
646 | } | ||
647 | kfree(async_extent->pages); | ||
648 | async_extent->nr_pages = 0; | ||
649 | async_extent->pages = NULL; | ||
650 | } | ||
651 | |||
636 | /* | 652 | /* |
637 | * phase two of compressed writeback. This is the ordered portion | 653 | * phase two of compressed writeback. This is the ordered portion |
638 | * of the code, which only gets called in the order the work was | 654 | * of the code, which only gets called in the order the work was |
@@ -709,15 +725,7 @@ retry: | |||
709 | async_extent->compressed_size, | 725 | async_extent->compressed_size, |
710 | 0, alloc_hint, &ins, 1, 1); | 726 | 0, alloc_hint, &ins, 1, 1); |
711 | if (ret) { | 727 | if (ret) { |
712 | int i; | 728 | free_async_extent_pages(async_extent); |
713 | |||
714 | for (i = 0; i < async_extent->nr_pages; i++) { | ||
715 | WARN_ON(async_extent->pages[i]->mapping); | ||
716 | page_cache_release(async_extent->pages[i]); | ||
717 | } | ||
718 | kfree(async_extent->pages); | ||
719 | async_extent->nr_pages = 0; | ||
720 | async_extent->pages = NULL; | ||
721 | 729 | ||
722 | if (ret == -ENOSPC) { | 730 | if (ret == -ENOSPC) { |
723 | unlock_extent(io_tree, async_extent->start, | 731 | unlock_extent(io_tree, async_extent->start, |
@@ -827,6 +835,7 @@ retry: | |||
827 | extent_clear_unlock_delalloc(inode, start, end, NULL, 0, | 835 | extent_clear_unlock_delalloc(inode, start, end, NULL, 0, |
828 | PAGE_END_WRITEBACK | | 836 | PAGE_END_WRITEBACK | |
829 | PAGE_SET_ERROR); | 837 | PAGE_SET_ERROR); |
838 | free_async_extent_pages(async_extent); | ||
830 | } | 839 | } |
831 | alloc_hint = ins.objectid + ins.offset; | 840 | alloc_hint = ins.objectid + ins.offset; |
832 | kfree(async_extent); | 841 | kfree(async_extent); |
@@ -848,6 +857,7 @@ out_free: | |||
848 | PAGE_UNLOCK | PAGE_CLEAR_DIRTY | | 857 | PAGE_UNLOCK | PAGE_CLEAR_DIRTY | |
849 | PAGE_SET_WRITEBACK | PAGE_END_WRITEBACK | | 858 | PAGE_SET_WRITEBACK | PAGE_END_WRITEBACK | |
850 | PAGE_SET_ERROR); | 859 | PAGE_SET_ERROR); |
860 | free_async_extent_pages(async_extent); | ||
851 | kfree(async_extent); | 861 | kfree(async_extent); |
852 | goto again; | 862 | goto again; |
853 | } | 863 | } |