Btrfs: don't leak pages and memory on compressed write error
authorFilipe Manana <fdmanana@suse.com>
Mon, 6 Oct 2014 21:14:24 +0000 (22:14 +0100)
committerChris Mason <clm@fb.com>
Fri, 21 Nov 2014 01:14:26 +0000 (17:14 -0800)
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>
fs/btrfs/inode.c

index ec68eaef05d7ed39bfc3c9a10065a91b0375bfe2..b759585fbc6abf844791e675af4c981ff23d79ff 100644 (file)
@@ -633,6 +633,22 @@ free_pages_out:
        goto out;
 }
 
+static void free_async_extent_pages(struct async_extent *async_extent)
+{
+       int i;
+
+       if (!async_extent->pages)
+               return;
+
+       for (i = 0; i < async_extent->nr_pages; i++) {
+               WARN_ON(async_extent->pages[i]->mapping);
+               page_cache_release(async_extent->pages[i]);
+       }
+       kfree(async_extent->pages);
+       async_extent->nr_pages = 0;
+       async_extent->pages = NULL;
+}
+
 /*
  * phase two of compressed writeback.  This is the ordered portion
  * of the code, which only gets called in the order the work was
@@ -709,15 +725,7 @@ retry:
                                           async_extent->compressed_size,
                                           0, alloc_hint, &ins, 1, 1);
                if (ret) {
-                       int i;
-
-                       for (i = 0; i < async_extent->nr_pages; i++) {
-                               WARN_ON(async_extent->pages[i]->mapping);
-                               page_cache_release(async_extent->pages[i]);
-                       }
-                       kfree(async_extent->pages);
-                       async_extent->nr_pages = 0;
-                       async_extent->pages = NULL;
+                       free_async_extent_pages(async_extent);
 
                        if (ret == -ENOSPC) {
                                unlock_extent(io_tree, async_extent->start,
@@ -827,6 +835,7 @@ retry:
                        extent_clear_unlock_delalloc(inode, start, end, NULL, 0,
                                                     PAGE_END_WRITEBACK |
                                                     PAGE_SET_ERROR);
+                       free_async_extent_pages(async_extent);
                }
                alloc_hint = ins.objectid + ins.offset;
                kfree(async_extent);
@@ -848,6 +857,7 @@ out_free:
                                     PAGE_UNLOCK | PAGE_CLEAR_DIRTY |
                                     PAGE_SET_WRITEBACK | PAGE_END_WRITEBACK |
                                     PAGE_SET_ERROR);
+       free_async_extent_pages(async_extent);
        kfree(async_extent);
        goto again;
 }