Although __vmalloc() doesn't support GFP_NOFS[1], ext4_kvmalloc/kvzalloc caller may be under GFP_NOFS context (e.g. fs/ext4/resize.c::add_new_gdb). In such cases, the memory reclaim can re-entr the filesystem and potentially deadlock. To prevent the memory relcaim re-entring the filesystem, use memalloc_nofs_save/restore that gets __vmalloc() to drop __GFP_FS flag. [1] linux-tree/Documentation/core-api/gfp-mask-fs-io.rst Signed-off-by: Naoto Kobayashi <naoto.kobayashi4c@xxxxxxxxx> --- fs/ext4/super.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 46b6d5b150ac..4a3c9ee63a34 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -43,6 +43,7 @@ #include <linux/uaccess.h> #include <linux/iversion.h> #include <linux/unicode.h> +#include <linux/sched/mm.h> #include <linux/kthread.h> #include <linux/freezer.h> @@ -204,13 +205,32 @@ void ext4_superblock_csum_set(struct super_block *sb) es->s_checksum = ext4_superblock_csum(sb, es); } +static inline void *ext4_vmalloc(size_t size, gfp_t flags, pgprot_t prot) +{ + unsigned int nofs_flag = 0; + void *ret; + + /** + * Although __vmalloc() doesn't support GFP_NOFS, we may be under + * GFP_NOFS context here. Hence we need to use memalloc_nofs_save() + * to prevent memory reclaim re-entring the filesystem here and + * potentially deadlocking. + */ + if (!(flags & __GFP_FS)) + nofs_flag = memalloc_nofs_save(); + ret = __vmalloc(size, flags, prot); + if (!(flags & __GFP_FS)) + memalloc_nofs_restore(nofs_flag); + return ret; +} + void *ext4_kvmalloc(size_t size, gfp_t flags) { void *ret; ret = kmalloc(size, flags | __GFP_NOWARN); if (!ret) - ret = __vmalloc(size, flags, PAGE_KERNEL); + ret = ext4_vmalloc(size, flags, PAGE_KERNEL); return ret; } @@ -220,7 +240,7 @@ void *ext4_kvzalloc(size_t size, gfp_t flags) ret = kzalloc(size, flags | __GFP_NOWARN); if (!ret) - ret = __vmalloc(size, flags | __GFP_ZERO, PAGE_KERNEL); + ret = ext4_vmalloc(size, flags | __GFP_ZERO, PAGE_KERNEL); return ret; } -- 2.16.5