Commit 4b5256f9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull memblock fixes from Mike Rapoport:
 "Fixes for nid setting in memmap_init_reserved_pages():

   - pass 'size' rather than 'end' to memblock_set_node() as that
     function expects

   - fix a corner case when memblock.reserved is doubled at
     memmap_init_reserved_pages() and the newly reserved block
     won't have nid assigned"

* tag 'fixes-2025-04-29' of git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock:
  memblock tests: add test for memblock_set_node
  mm/memblock: repeat setting reserved region nid if array is doubled
  mm/memblock: pass size instead of end to memblock_set_node()
parents ca91b950 3b394dff
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -2183,11 +2183,14 @@ static void __init memmap_init_reserved_pages(void)
	struct memblock_region *region;
	phys_addr_t start, end;
	int nid;
	unsigned long max_reserved;

	/*
	 * set nid on all reserved pages and also treat struct
	 * pages for the NOMAP regions as PageReserved
	 */
repeat:
	max_reserved = memblock.reserved.max;
	for_each_mem_region(region) {
		nid = memblock_get_region_node(region);
		start = region->base;
@@ -2196,8 +2199,15 @@ static void __init memmap_init_reserved_pages(void)
		if (memblock_is_nomap(region))
			reserve_bootmem_region(start, end, nid);

		memblock_set_node(start, end, &memblock.reserved, nid);
		memblock_set_node(start, region->size, &memblock.reserved, nid);
	}
	/*
	 * 'max' is changed means memblock.reserved has been doubled its
	 * array, which may result a new reserved region before current
	 * 'start'. Now we should repeat the procedure to set its node id.
	 */
	if (max_reserved != memblock.reserved.max)
		goto repeat;

	/*
	 * initialize struct pages for reserved regions that don't have
+102 −0
Original line number Diff line number Diff line
@@ -2434,6 +2434,107 @@ static int memblock_overlaps_region_checks(void)
	return 0;
}

#ifdef CONFIG_NUMA
static int memblock_set_node_check(void)
{
	unsigned long i, max_reserved;
	struct memblock_region *rgn;
	void *orig_region;

	PREFIX_PUSH();

	reset_memblock_regions();
	memblock_allow_resize();

	dummy_physical_memory_init();
	memblock_add(dummy_physical_memory_base(), MEM_SIZE);
	orig_region = memblock.reserved.regions;

	/* Equally Split range to node 0 and 1*/
	memblock_set_node(memblock_start_of_DRAM(),
			memblock_phys_mem_size() / 2, &memblock.memory, 0);
	memblock_set_node(memblock_start_of_DRAM() + memblock_phys_mem_size() / 2,
			memblock_phys_mem_size() / 2, &memblock.memory, 1);

	ASSERT_EQ(memblock.memory.cnt, 2);
	rgn = &memblock.memory.regions[0];
	ASSERT_EQ(rgn->base, memblock_start_of_DRAM());
	ASSERT_EQ(rgn->size, memblock_phys_mem_size() / 2);
	ASSERT_EQ(memblock_get_region_node(rgn), 0);
	rgn = &memblock.memory.regions[1];
	ASSERT_EQ(rgn->base, memblock_start_of_DRAM() + memblock_phys_mem_size() / 2);
	ASSERT_EQ(rgn->size, memblock_phys_mem_size() / 2);
	ASSERT_EQ(memblock_get_region_node(rgn), 1);

	/* Reserve 126 regions with the last one across node boundary */
	for (i = 0; i < 125; i++)
		memblock_reserve(memblock_start_of_DRAM() + SZ_16 * i, SZ_8);

	memblock_reserve(memblock_start_of_DRAM() + memblock_phys_mem_size() / 2 - SZ_8,
			SZ_16);

	/*
	 * Commit 61167ad5fecd ("mm: pass nid to reserve_bootmem_region()")
	 * do following process to set nid to each memblock.reserved region.
	 * But it may miss some region if memblock_set_node() double the
	 * array.
	 *
	 * By checking 'max', we make sure all region nid is set properly.
	 */
repeat:
	max_reserved = memblock.reserved.max;
	for_each_mem_region(rgn) {
		int nid = memblock_get_region_node(rgn);

		memblock_set_node(rgn->base, rgn->size, &memblock.reserved, nid);
	}
	if (max_reserved != memblock.reserved.max)
		goto repeat;

	/* Confirm each region has valid node set */
	for_each_reserved_mem_region(rgn) {
		ASSERT_TRUE(numa_valid_node(memblock_get_region_node(rgn)));
		if (rgn == (memblock.reserved.regions + memblock.reserved.cnt - 1))
			ASSERT_EQ(1, memblock_get_region_node(rgn));
		else
			ASSERT_EQ(0, memblock_get_region_node(rgn));
	}

	dummy_physical_memory_cleanup();

	/*
	 * The current reserved.regions is occupying a range of memory that
	 * allocated from dummy_physical_memory_init(). After free the memory,
	 * we must not use it. So restore the origin memory region to make sure
	 * the tests can run as normal and not affected by the double array.
	 */
	memblock.reserved.regions = orig_region;
	memblock.reserved.cnt = INIT_MEMBLOCK_RESERVED_REGIONS;

	test_pass_pop();

	return 0;
}

static int memblock_set_node_checks(void)
{
	prefix_reset();
	prefix_push("memblock_set_node");
	test_print("Running memblock_set_node tests...\n");

	memblock_set_node_check();

	prefix_pop();

	return 0;
}
#else
static int memblock_set_node_checks(void)
{
	return 0;
}
#endif

int memblock_basic_checks(void)
{
	memblock_initialization_check();
@@ -2444,6 +2545,7 @@ int memblock_basic_checks(void)
	memblock_bottom_up_checks();
	memblock_trim_memory_checks();
	memblock_overlaps_region_checks();
	memblock_set_node_checks();

	return 0;
}