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

kmsan: remove hard-coded GFP_KERNEL flags

kmsan_vmap_pages_range_noflush() allocates its temp s_pages/o_pages arrays
with GFP_KERNEL, which may sleep.  This is inconsistent with vmalloc() as
it will support non-blocking requests later.

Plumb gfp_mask through the kmsan_vmap_pages_range_noflush(), so it can use
it internally for its demand.

Please note, the subsequent __vmap_pages_range_noflush() still uses
GFP_KERNEL and can sleep.  If a caller runs under reclaim constraints,
sleeping is forbidden, it must establish the appropriate memalloc scope
API.

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


Signed-off-by: default avatarUladzislau Rezki (Sony) <urezki@gmail.com>
Reviewed-by: default avatarAlexander Potapenko <glider@google.com>
Cc: Marco Elver <elver@google.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent ad435e79
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -133,6 +133,7 @@ void kmsan_kfree_large(const void *ptr);
 * @prot:	page protection flags used for vmap.
 * @pages:	array of pages.
 * @page_shift:	page_shift passed to vmap_range_noflush().
 * @gfp_mask:	gfp_mask to use internally.
 *
 * KMSAN maps shadow and origin pages of @pages into contiguous ranges in
 * vmalloc metadata address range. Returns 0 on success, callers must check
@@ -142,7 +143,8 @@ int __must_check kmsan_vmap_pages_range_noflush(unsigned long start,
						unsigned long end,
						pgprot_t prot,
						struct page **pages,
						unsigned int page_shift);
						unsigned int page_shift,
						gfp_t gfp_mask);

/**
 * kmsan_vunmap_kernel_range_noflush() - Notify KMSAN about a vunmap.
@@ -347,7 +349,7 @@ static inline void kmsan_kfree_large(const void *ptr)

static inline int __must_check kmsan_vmap_pages_range_noflush(
	unsigned long start, unsigned long end, pgprot_t prot,
	struct page **pages, unsigned int page_shift)
	struct page **pages, unsigned int page_shift, gfp_t gfp_mask)
{
	return 0;
}
+2 −2
Original line number Diff line number Diff line
@@ -1355,7 +1355,7 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
#ifdef CONFIG_MMU
void __init vmalloc_init(void);
int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,
                pgprot_t prot, struct page **pages, unsigned int page_shift);
	pgprot_t prot, struct page **pages, unsigned int page_shift, gfp_t gfp_mask);
unsigned int get_vm_area_page_order(struct vm_struct *vm);
#else
static inline void vmalloc_init(void)
@@ -1364,7 +1364,7 @@ static inline void vmalloc_init(void)

static inline
int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,
                pgprot_t prot, struct page **pages, unsigned int page_shift)
	pgprot_t prot, struct page **pages, unsigned int page_shift, gfp_t gfp_mask)
{
	return -EINVAL;
}
+3 −3
Original line number Diff line number Diff line
@@ -215,7 +215,7 @@ void kmsan_free_page(struct page *page, unsigned int order)

int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,
				   pgprot_t prot, struct page **pages,
				   unsigned int page_shift)
				   unsigned int page_shift, gfp_t gfp_mask)
{
	unsigned long shadow_start, origin_start, shadow_end, origin_end;
	struct page **s_pages, **o_pages;
@@ -230,8 +230,8 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,
		return 0;

	nr = (end - start) / PAGE_SIZE;
	s_pages = kcalloc(nr, sizeof(*s_pages), GFP_KERNEL);
	o_pages = kcalloc(nr, sizeof(*o_pages), GFP_KERNEL);
	s_pages = kcalloc(nr, sizeof(*s_pages), gfp_mask);
	o_pages = kcalloc(nr, sizeof(*o_pages), gfp_mask);
	if (!s_pages || !o_pages) {
		err = -ENOMEM;
		goto ret;
+1 −1
Original line number Diff line number Diff line
@@ -194,7 +194,7 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
			    int nr_pages)
{
	return vmap_pages_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT),
					PAGE_KERNEL, pages, PAGE_SHIFT);
			PAGE_KERNEL, pages, PAGE_SHIFT, GFP_KERNEL);
}

/**
+17 −9
Original line number Diff line number Diff line
@@ -671,16 +671,28 @@ int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
}

int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
		pgprot_t prot, struct page **pages, unsigned int page_shift)
		pgprot_t prot, struct page **pages, unsigned int page_shift,
		gfp_t gfp_mask)
{
	int ret = kmsan_vmap_pages_range_noflush(addr, end, prot, pages,
						 page_shift);
						page_shift, gfp_mask);

	if (ret)
		return ret;
	return __vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
}

static int __vmap_pages_range(unsigned long addr, unsigned long end,
		pgprot_t prot, struct page **pages, unsigned int page_shift,
		gfp_t gfp_mask)
{
	int err;

	err = vmap_pages_range_noflush(addr, end, prot, pages, page_shift, gfp_mask);
	flush_cache_vmap(addr, end);
	return err;
}

/**
 * vmap_pages_range - map pages to a kernel virtual address
 * @addr: start of the VM area to map
@@ -696,11 +708,7 @@ int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
int vmap_pages_range(unsigned long addr, unsigned long end,
		pgprot_t prot, struct page **pages, unsigned int page_shift)
{
	int err;

	err = vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
	flush_cache_vmap(addr, end);
	return err;
	return __vmap_pages_range(addr, end, prot, pages, page_shift, GFP_KERNEL);
}

static int check_sparse_vm_area(struct vm_struct *area, unsigned long start,
@@ -3839,8 +3847,8 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
	 */
	flags = memalloc_apply_gfp_scope(gfp_mask);
	do {
		ret = vmap_pages_range(addr, addr + size, prot, area->pages,
			page_shift);
		ret = __vmap_pages_range(addr, addr + size, prot, area->pages,
				page_shift, nested_gfp);
		if (nofail && (ret < 0))
			schedule_timeout_uninterruptible(1);
	} while (nofail && (ret < 0));