]> git.hungrycats.org Git - linux/commitdiff
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)
committerZygo Blaxell <zblaxell@serenity.furryterror.org>
Fri, 23 Jan 2015 02:50:34 +0000 (21:50 -0500)
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>
(cherry picked from commit 40ae837b43565c47ee171e704d05947fd5c2bae9)

fs/btrfs/inode.c

index 43651692a9509b5431c5a2cfca0e5f0ad17bb6b1..d1a52850d17d5cea24cebe8716f0c4ba652f44c7 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);
@@ -844,6 +853,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;
 }