Commit 86e968d8 authored by Uladzislau Rezki (Sony)'s avatar Uladzislau Rezki (Sony) Committed by Andrew Morton
Browse files

mm/vmalloc: support non-blocking GFP flags in alloc_vmap_area()

alloc_vmap_area() currently assumes that sleeping is allowed during
allocation.  This is not true for callers which pass non-blocking GFP
flags, such as GFP_ATOMIC or GFP_NOWAIT.

This patch adds logic to detect whether the given gfp_mask permits
blocking.  It avoids invoking might_sleep() or falling back to reclaim
path if blocking is not allowed.

This makes alloc_vmap_area() safer for use in non-sleeping contexts, where
previously it could hit unexpected sleeps, trigger warnings.

It is a preparation and adjustment step to later allow both GFP_ATOMIC and
GFP_NOWAIT allocations in this series.

Link: https://lkml.kernel.org/r/20251007122035.56347-4-urezki@gmail.com


Signed-off-by: default avatarUladzislau Rezki (Sony) <urezki@gmail.com>
Acked-by: default avatarMichal Hocko <mhocko@suse.com>
Reviewed-by: default avatarBaoquan He <bhe@redhat.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Marco Elver <elver@google.com>
Cc: Michal Hocko <mhocko@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent e781c1c0
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -2017,6 +2017,7 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
	unsigned long freed;
	unsigned long addr;
	unsigned int vn_id;
	bool allow_block;
	int purged = 0;
	int ret;

@@ -2028,7 +2029,8 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,

	/* Only reclaim behaviour flags are relevant. */
	gfp_mask = gfp_mask & GFP_RECLAIM_MASK;
	might_sleep();
	allow_block = gfpflags_allow_blocking(gfp_mask);
	might_sleep_if(allow_block);

	/*
	 * If a VA is obtained from a global heap(if it fails here)
@@ -2062,6 +2064,7 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
		 * This is not a fast path.  Check if yielding is needed. This
		 * is the only reschedule point in the vmalloc() path.
		 */
		if (allow_block)
			cond_resched();
	}

@@ -2071,9 +2074,17 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
	 * If an allocation fails, the error value is
	 * returned. Therefore trigger the overflow path.
	 */
	if (IS_ERR_VALUE(addr))
	if (IS_ERR_VALUE(addr)) {
		if (allow_block)
			goto overflow;

		/*
		 * We can not trigger any reclaim logic because
		 * sleeping is not allowed, thus fail an allocation.
		 */
		goto out_free_va;
	}

	va->va_start = addr;
	va->va_end = addr + size;
	va->vm = NULL;
@@ -2122,6 +2133,7 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
		pr_warn("vmalloc_node_range for size %lu failed: Address range restricted to %#lx - %#lx\n",
				size, vstart, vend);

out_free_va:
	kmem_cache_free(vmap_area_cachep, va);
	return ERR_PTR(-EBUSY);
}