btrfs: Use kvmalloc for allocating compressed path context
authorNikolay Borisov <nborisov@suse.com>
Mon, 1 Apr 2019 08:29:57 +0000 (11:29 +0300)
committerDavid Sterba <dsterba@suse.com>
Thu, 2 May 2019 11:48:19 +0000 (13:48 +0200)
Recent refactoring of cow_file_range_async means it's now possible to
request a rather large physically contiguous memory via kmalloc. The
size is dependent on the number of 512k chunks that the compressed range
consists of. David reported multiple OOM messages on such large
allocations. Fix it by switching to using kvmalloc.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index 05ff09e8a200a736f6458b9757f80170b5b935d3..b6d549c993f60405a0ca9b42caf4a98f3f2ad69a 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/magic.h>
 #include <linux/iversion.h>
 #include <linux/swap.h>
+#include <linux/sched/mm.h>
 #include <asm/unaligned.h>
 #include "ctree.h"
 #include "disk-io.h"
@@ -1172,7 +1173,7 @@ static noinline void async_cow_free(struct btrfs_work *work)
         * async_chunk's, freeing it ensures the whole array has been freed.
         */
        if (atomic_dec_and_test(async_chunk->pending))
-               kfree(async_chunk->pending);
+               kvfree(async_chunk->pending);
 }
 
 static int cow_file_range_async(struct inode *inode, struct page *locked_page,
@@ -1188,6 +1189,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
        u64 num_chunks = DIV_ROUND_UP(end - start, SZ_512K);
        int i;
        bool should_compress;
+       unsigned nofs_flag;
 
        unlock_extent(&BTRFS_I(inode)->io_tree, start, end);
 
@@ -1199,7 +1201,10 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,
                should_compress = true;
        }
 
-       ctx = kmalloc(struct_size(ctx, chunks, num_chunks), GFP_NOFS);
+       nofs_flag = memalloc_nofs_save();
+       ctx = kvmalloc(struct_size(ctx, chunks, num_chunks), GFP_KERNEL);
+       memalloc_nofs_restore(nofs_flag);
+
        if (!ctx) {
                unsigned clear_bits = EXTENT_LOCKED | EXTENT_DELALLOC |
                        EXTENT_DELALLOC_NEW | EXTENT_DEFRAG |