linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] Add tests for memblock allocation functions
@ 2022-02-28 14:46 Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 1/9] memblock tests: Split up reset_memblock function Karolina Drobnik
                   ` (9 more replies)
  0 siblings, 10 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Patches are on top of memblock/for-next.

This series adds test coverage for memblock allocation functions that return
virtual addresses:
  * memblock_alloc
  * memblock_alloc_try_nid
  * memblock_alloc_from

These tests are a part of memblock simulator, a user space test suite that uses
the actual memblock code[1]. All the tests are run as one, monolithic test, that
exercises both basic operations and allocation functions of memblock.

To simplify implementation of the memory allocation tests, reset_memblock() was
split into two functions - reset_memblock_attributes() and
reset_memblock_regions(). The former sets default values for region array names,
allocation direction flag (bottom_up) and current memory limit. The latter only
resets region arrays and corresponding counters.

In addition to these, the patch set introduces a data structure that simulates
physical memory, test_memory. It is a simple wrapper around a pointer to the
memory block allocated via malloc(), which is added to memblock as available
memory. This is required because the tested functions interact with the memory
before allocating it.

Patchset structure:
  * Patch 1 splits reset_memblock() into two functions -
    reset_memblock_regions() and reset_memblock_attributes(), and updates
    basic API tests to use the new definitions
  * Patch 2 adds test_memory struct and functions to add (or release) dummy
    physical memory
  * Patches 3 - 8 add test cases for memblock_alloc, memblock_alloc_try_nid and
    memblock_alloc_from for both allocation directions (default top down and
    bottom up)
  * Patch 9 adds README and TODO files

[1] https://lore.kernel.org/linux-mm/cover.1643796665.git.karolinadrobnik@gmail.com/

Karolina Drobnik (9):
  memblock tests: Split up reset_memblock function
  memblock tests: Add simulation of physical memory
  memblock tests: Add memblock_alloc tests for top down
  memblock tests: Add memblock_alloc tests for bottom up
  memblock tests: Add memblock_alloc_from tests for top down
  memblock tests: Add memblock_alloc_from tests for bottom up
  memblock tests: Add memblock_alloc_try_nid tests for top down
  memblock tests: Add memblock_alloc_try_nid tests for bottom up
  memblock tests: Add TODO and README files

 tools/testing/memblock/Makefile               |    3 +-
 tools/testing/memblock/README                 |  114 ++
 tools/testing/memblock/TODO                   |   28 +
 tools/testing/memblock/main.c                 |    7 +
 tools/testing/memblock/tests/alloc_api.c      |  742 +++++++++++
 tools/testing/memblock/tests/alloc_api.h      |    9 +
 .../memblock/tests/alloc_helpers_api.c        |  393 ++++++
 .../memblock/tests/alloc_helpers_api.h        |    9 +
 tools/testing/memblock/tests/alloc_nid_api.c  | 1167 +++++++++++++++++
 tools/testing/memblock/tests/alloc_nid_api.h  |    9 +
 tools/testing/memblock/tests/basic_api.c      |   49 +-
 tools/testing/memblock/tests/basic_api.h      |    1 -
 tools/testing/memblock/tests/common.c         |   33 +-
 tools/testing/memblock/tests/common.h         |   21 +-
 14 files changed, 2550 insertions(+), 35 deletions(-)
 create mode 100644 tools/testing/memblock/README
 create mode 100644 tools/testing/memblock/TODO
 create mode 100644 tools/testing/memblock/tests/alloc_api.c
 create mode 100644 tools/testing/memblock/tests/alloc_api.h
 create mode 100644 tools/testing/memblock/tests/alloc_helpers_api.c
 create mode 100644 tools/testing/memblock/tests/alloc_helpers_api.h
 create mode 100644 tools/testing/memblock/tests/alloc_nid_api.c
 create mode 100644 tools/testing/memblock/tests/alloc_nid_api.h


base-commit: f30b002ccfee8c60c8feb590e145c0b5e8fa4c67
--
2.30.2



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/9] memblock tests: Split up reset_memblock function
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 2/9] memblock tests: Add simulation of physical memory Karolina Drobnik
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

All memblock data structure fields are reset in one function. In some
test cases, it's preferred to reset memory region arrays without
modifying other values like allocation direction flag.

Extract two functions from reset_memblock, so it's possible to reset
different parts of memblock:
  - reset_memblock_regions    - reset region arrays and their counters
  - reset_memblock_attributes - set other fields to their default values

Update checks in basic_api.c to use new definitions. Remove
reset_memblock call from memblock_initialization_check, so the true
initial values are tested.

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/tests/basic_api.c | 48 ++++++++++++------------
 tools/testing/memblock/tests/common.c    | 14 ++++---
 tools/testing/memblock/tests/common.h    |  3 +-
 3 files changed, 33 insertions(+), 32 deletions(-)

diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c
index fbb989f6ddbf..d5035a3dcce8 100644
--- a/tools/testing/memblock/tests/basic_api.c
+++ b/tools/testing/memblock/tests/basic_api.c
@@ -8,8 +8,6 @@

 static int memblock_initialization_check(void)
 {
-	reset_memblock();
-
 	assert(memblock.memory.regions);
 	assert(memblock.memory.cnt == 1);
 	assert(memblock.memory.max == EXPECTED_MEMBLOCK_REGIONS);
@@ -43,7 +41,7 @@ static int memblock_add_simple_check(void)
 		.size = SZ_4M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r.base, r.size);

 	assert(rgn->base == r.base);
@@ -72,7 +70,7 @@ static int memblock_add_node_simple_check(void)
 		.size = SZ_16M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add_node(r.base, r.size, 1, MEMBLOCK_HOTPLUG);

 	assert(rgn->base == r.base);
@@ -110,7 +108,7 @@ static int memblock_add_disjoint_check(void)
 		.size = SZ_8K
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_add(r2.base, r2.size);

@@ -151,7 +149,7 @@ static int memblock_add_overlap_top_check(void)

 	total_size = (r1.base - r2.base) + r1.size;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_add(r2.base, r2.size);

@@ -190,7 +188,7 @@ static int memblock_add_overlap_bottom_check(void)

 	total_size = (r2.base - r1.base) + r2.size;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_add(r2.base, r2.size);

@@ -225,7 +223,7 @@ static int memblock_add_within_check(void)
 		.size = SZ_1M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_add(r2.base, r2.size);

@@ -249,7 +247,7 @@ static int memblock_add_twice_check(void)
 		.size = SZ_2M
 	};

-	reset_memblock();
+	reset_memblock_regions();

 	memblock_add(r.base, r.size);
 	memblock_add(r.base, r.size);
@@ -290,7 +288,7 @@ static int memblock_reserve_simple_check(void)
 		.size = SZ_128M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r.base, r.size);

 	assert(rgn->base == r.base);
@@ -321,7 +319,7 @@ static int memblock_reserve_disjoint_check(void)
 		.size = SZ_512M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_reserve(r2.base, r2.size);

@@ -364,7 +362,7 @@ static int memblock_reserve_overlap_top_check(void)

 	total_size = (r1.base - r2.base) + r1.size;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_reserve(r2.base, r2.size);

@@ -404,7 +402,7 @@ static int memblock_reserve_overlap_bottom_check(void)

 	total_size = (r2.base - r1.base) + r2.size;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_reserve(r2.base, r2.size);

@@ -440,7 +438,7 @@ static int memblock_reserve_within_check(void)
 		.size = SZ_64K
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_reserve(r2.base, r2.size);

@@ -465,7 +463,7 @@ static int memblock_reserve_twice_check(void)
 		.size = SZ_2M
 	};

-	reset_memblock();
+	reset_memblock_regions();

 	memblock_reserve(r.base, r.size);
 	memblock_reserve(r.base, r.size);
@@ -511,7 +509,7 @@ static int memblock_remove_simple_check(void)
 		.size = SZ_4M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_add(r2.base, r2.size);
 	memblock_remove(r1.base, r1.size);
@@ -545,7 +543,7 @@ static int memblock_remove_absent_check(void)
 		.size = SZ_1G
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_remove(r2.base, r2.size);

@@ -585,7 +583,7 @@ static int memblock_remove_overlap_top_check(void)
 	r2_end = r2.base + r2.size;
 	total_size = r1_end - r2_end;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_remove(r2.base, r2.size);

@@ -623,7 +621,7 @@ static int memblock_remove_overlap_bottom_check(void)

 	total_size = r2.base - r1.base;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_remove(r2.base, r2.size);

@@ -665,7 +663,7 @@ static int memblock_remove_within_check(void)
 	r2_size = (r1.base + r1.size) - (r2.base + r2.size);
 	total_size = r1_size + r2_size;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_add(r1.base, r1.size);
 	memblock_remove(r2.base, r2.size);

@@ -715,7 +713,7 @@ static int memblock_free_simple_check(void)
 		.size = SZ_1M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_reserve(r2.base, r2.size);
 	memblock_free((void *)r1.base, r1.size);
@@ -749,7 +747,7 @@ static int memblock_free_absent_check(void)
 		.size = SZ_128M
 	};

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_free((void *)r2.base, r2.size);

@@ -787,7 +785,7 @@ static int memblock_free_overlap_top_check(void)

 	total_size = (r1.size + r1.base) - (r2.base + r2.size);

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_free((void *)r2.base, r2.size);

@@ -824,7 +822,7 @@ static int memblock_free_overlap_bottom_check(void)

 	total_size = r2.base - r1.base;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_free((void *)r2.base, r2.size);

@@ -867,7 +865,7 @@ static int memblock_free_within_check(void)
 	r2_size = (r1.base + r1.size) - (r2.base + r2.size);
 	total_size = r1_size + r2_size;

-	reset_memblock();
+	reset_memblock_regions();
 	memblock_reserve(r1.base, r1.size);
 	memblock_free((void *)r2.base, r2.size);

diff --git a/tools/testing/memblock/tests/common.c b/tools/testing/memblock/tests/common.c
index 03de6eab0c3c..dd7e87c589fe 100644
--- a/tools/testing/memblock/tests/common.c
+++ b/tools/testing/memblock/tests/common.c
@@ -5,23 +5,25 @@
 #define INIT_MEMBLOCK_REGIONS			128
 #define INIT_MEMBLOCK_RESERVED_REGIONS		INIT_MEMBLOCK_REGIONS

-void reset_memblock(void)
+void reset_memblock_regions(void)
 {
 	memset(memblock.memory.regions, 0,
 	       memblock.memory.cnt * sizeof(struct memblock_region));
-	memset(memblock.reserved.regions, 0,
-	       memblock.reserved.cnt * sizeof(struct memblock_region));
-
 	memblock.memory.cnt	= 1;
 	memblock.memory.max	= INIT_MEMBLOCK_REGIONS;
-	memblock.memory.name	= "memory";
 	memblock.memory.total_size = 0;

+	memset(memblock.reserved.regions, 0,
+	       memblock.reserved.cnt * sizeof(struct memblock_region));
 	memblock.reserved.cnt	= 1;
 	memblock.reserved.max	= INIT_MEMBLOCK_RESERVED_REGIONS;
-	memblock.reserved.name	= "reserved";
 	memblock.reserved.total_size = 0;
+}

+void reset_memblock_attributes(void)
+{
+	memblock.memory.name	= "memory";
+	memblock.reserved.name	= "reserved";
 	memblock.bottom_up	= false;
 	memblock.current_limit	= MEMBLOCK_ALLOC_ANYWHERE;
 }
diff --git a/tools/testing/memblock/tests/common.h b/tools/testing/memblock/tests/common.h
index 48efc4270ea1..b864c64fb60f 100644
--- a/tools/testing/memblock/tests/common.h
+++ b/tools/testing/memblock/tests/common.h
@@ -10,6 +10,7 @@ struct region {
 	phys_addr_t size;
 };

-void reset_memblock(void);
+void reset_memblock_regions(void);
+void reset_memblock_attributes(void);

 #endif
--
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/9] memblock tests: Add simulation of physical memory
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 1/9] memblock tests: Split up reset_memblock function Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 3/9] memblock tests: Add memblock_alloc tests for top down Karolina Drobnik
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Allocation functions that return virtual addresses (with an exception
of _raw variant) clear the allocated memory after reserving it. This
requires valid memory ranges in memblock.memory.

Introduce memory_block variable to store memory that can be registered
with memblock data structure. Move assert.h and size.h includes to common.h
to share them between the test files.

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/tests/basic_api.c |  1 -
 tools/testing/memblock/tests/basic_api.h |  1 -
 tools/testing/memblock/tests/common.c    | 19 +++++++++++++++++++
 tools/testing/memblock/tests/common.h    | 18 ++++++++++++++++++
 4 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c
index d5035a3dcce8..fbc1ce160303 100644
--- a/tools/testing/memblock/tests/basic_api.c
+++ b/tools/testing/memblock/tests/basic_api.c
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 #include <string.h>
 #include <linux/memblock.h>
-#include <linux/sizes.h>
 #include "basic_api.h"
 
 #define EXPECTED_MEMBLOCK_REGIONS			128
diff --git a/tools/testing/memblock/tests/basic_api.h b/tools/testing/memblock/tests/basic_api.h
index 1ceecfca1f47..1873faa54754 100644
--- a/tools/testing/memblock/tests/basic_api.h
+++ b/tools/testing/memblock/tests/basic_api.h
@@ -2,7 +2,6 @@
 #ifndef _MEMBLOCK_BASIC_H
 #define _MEMBLOCK_BASIC_H
 
-#include <assert.h>
 #include "common.h"
 
 int memblock_basic_checks(void);
diff --git a/tools/testing/memblock/tests/common.c b/tools/testing/memblock/tests/common.c
index dd7e87c589fe..62d3191f7c9a 100644
--- a/tools/testing/memblock/tests/common.c
+++ b/tools/testing/memblock/tests/common.c
@@ -5,6 +5,8 @@
 #define INIT_MEMBLOCK_REGIONS			128
 #define INIT_MEMBLOCK_RESERVED_REGIONS		INIT_MEMBLOCK_REGIONS
 
+static struct test_memory memory_block;
+
 void reset_memblock_regions(void)
 {
 	memset(memblock.memory.regions, 0,
@@ -27,3 +29,20 @@ void reset_memblock_attributes(void)
 	memblock.bottom_up	= false;
 	memblock.current_limit	= MEMBLOCK_ALLOC_ANYWHERE;
 }
+
+void setup_memblock(void)
+{
+	reset_memblock_regions();
+	memblock_add((phys_addr_t)memory_block.base, MEM_SIZE);
+}
+
+void dummy_physical_memory_init(void)
+{
+	memory_block.base = malloc(MEM_SIZE);
+	assert(memory_block.base);
+}
+
+void dummy_physical_memory_cleanup(void)
+{
+	free(memory_block.base);
+}
diff --git a/tools/testing/memblock/tests/common.h b/tools/testing/memblock/tests/common.h
index b864c64fb60f..619054d03219 100644
--- a/tools/testing/memblock/tests/common.h
+++ b/tools/testing/memblock/tests/common.h
@@ -2,8 +2,23 @@
 #ifndef _MEMBLOCK_TEST_H
 #define _MEMBLOCK_TEST_H
 
+#include <stdlib.h>
+#include <assert.h>
 #include <linux/types.h>
 #include <linux/memblock.h>
+#include <linux/sizes.h>
+
+#define MEM_SIZE SZ_16K
+
+/*
+ * Available memory registered with memblock needs to be valid for allocs
+ * test to run. This is a convenience wrapper for memory allocated in
+ * dummy_physical_memory_init() that is later registered with memblock
+ * in setup_memblock().
+ */
+struct test_memory {
+	void *base;
+};
 
 struct region {
 	phys_addr_t base;
@@ -12,5 +27,8 @@ struct region {
 
 void reset_memblock_regions(void);
 void reset_memblock_attributes(void);
+void setup_memblock(void);
+void dummy_physical_memory_init(void);
+void dummy_physical_memory_cleanup(void);
 
 #endif
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/9] memblock tests: Add memblock_alloc tests for top down
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 1/9] memblock tests: Split up reset_memblock function Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 2/9] memblock tests: Add simulation of physical memory Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 4/9] memblock tests: Add memblock_alloc tests for bottom up Karolina Drobnik
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Add checks for memblock_alloc for top down allocation direction.
The tested scenarios are:
  - Region can be allocated on the first fit (with and without
    region merging)
  - Region can be allocated on the second fit (with and without
    region merging)

Add checks for both allocation directions:
  - Region can be allocated between two already existing entries
  - Limited memory available
  - All memory is reserved
  - No available memory registered with memblock

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/Makefile          |   2 +-
 tools/testing/memblock/main.c            |   3 +
 tools/testing/memblock/tests/alloc_api.c | 428 +++++++++++++++++++++++
 tools/testing/memblock/tests/alloc_api.h |   9 +
 4 files changed, 441 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/memblock/tests/alloc_api.c
 create mode 100644 tools/testing/memblock/tests/alloc_api.h

diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile
index 29715327a2d3..5b01cfd808d0 100644
--- a/tools/testing/memblock/Makefile
+++ b/tools/testing/memblock/Makefile
@@ -6,7 +6,7 @@ CFLAGS += -I. -I../../include -Wall -O2 -fsanitize=address \
 	  -fsanitize=undefined -D CONFIG_PHYS_ADDR_T_64BIT
 LDFLAGS += -fsanitize=address -fsanitize=undefined
 TARGETS = main
-TEST_OFILES = tests/basic_api.o tests/common.o
+TEST_OFILES = tests/alloc_api.o tests/basic_api.o tests/common.o
 DEP_OFILES = memblock.o lib/slab.o mmzone.o slab.o
 OFILES = main.o $(DEP_OFILES) $(TEST_OFILES)
 EXTR_SRC = ../../../mm/memblock.c
diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c
index da65b0adee91..e7cc45dc06d4 100644
--- a/tools/testing/memblock/main.c
+++ b/tools/testing/memblock/main.c
@@ -1,8 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 #include "tests/basic_api.h"
+#include "tests/alloc_api.h"
 
 int main(int argc, char **argv)
 {
 	memblock_basic_checks();
+	memblock_alloc_checks();
+
 	return 0;
 }
diff --git a/tools/testing/memblock/tests/alloc_api.c b/tools/testing/memblock/tests/alloc_api.c
new file mode 100644
index 000000000000..22ba9a2b4eaf
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_api.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "alloc_api.h"
+
+/*
+ * A simple test that tries to allocate a small memory region.
+ * Expect to allocate an aligned region near the end of the available memory.
+ */
+static int alloc_top_down_simple_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t size = SZ_2;
+	phys_addr_t expected_start;
+
+	setup_memblock();
+
+	expected_start = memblock_end_of_DRAM() - SMP_CACHE_BYTES;
+
+	allocated_ptr = memblock_alloc(size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == size);
+	assert(rgn->base == expected_start);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory next to a reserved region that starts at
+ * the misaligned address. Expect to create two separate entries, with the new
+ * entry aligned to the provided alignment:
+ *
+ *              +
+ * |            +--------+         +--------|
+ * |            |  rgn2  |         |  rgn1  |
+ * +------------+--------+---------+--------+
+ *              ^
+ *              |
+ *              Aligned address boundary
+ *
+ * The allocation direction is top-down and region arrays are sorted from lower
+ * to higher addresses, so the new region will be the first entry in
+ * memory.reserved array. The previously reserved region does not get modified.
+ * Region counter and total size get updated.
+ */
+static int alloc_top_down_disjoint_check(void)
+{
+	/* After allocation, this will point to the "old" region */
+	struct memblock_region *rgn1 = &memblock.reserved.regions[1];
+	struct memblock_region *rgn2 = &memblock.reserved.regions[0];
+	struct region r1;
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r2_size = SZ_16;
+	/* Use custom alignment */
+	phys_addr_t alignment = SMP_CACHE_BYTES * 2;
+	phys_addr_t total_size;
+	phys_addr_t expected_start;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SZ_2;
+	r1.size = SZ_2;
+
+	total_size = r1.size + r2_size;
+	expected_start = memblock_end_of_DRAM() - alignment;
+
+	memblock_reserve(r1.base, r1.size);
+
+	allocated_ptr = memblock_alloc(r2_size, alignment);
+
+	assert(allocated_ptr);
+	assert(rgn1->size == r1.size);
+	assert(rgn1->base == r1.base);
+
+	assert(rgn2->size == r2_size);
+	assert(rgn2->base == expected_start);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there is enough space at the end
+ * of the previously reserved block (i.e. first fit):
+ *
+ *  |              +--------+--------------|
+ *  |              |   r1   |      r2      |
+ *  +--------------+--------+--------------+
+ *
+ * Expect a merge of both regions. Only the region size gets updated.
+ */
+static int alloc_top_down_before_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	/* The first region ends at the aligned address to test region merging */
+	phys_addr_t r1_size = SMP_CACHE_BYTES;
+	phys_addr_t r2_size = SZ_512;
+	phys_addr_t total_size = r1_size + r2_size;
+
+	setup_memblock();
+
+	memblock_reserve(memblock_end_of_DRAM() - total_size, r1_size);
+
+	allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == total_size);
+	assert(rgn->base == memblock_end_of_DRAM() - total_size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there is not enough space at the
+ * end of the previously reserved block (i.e. second fit):
+ *
+ *  |            +-----------+------+     |
+ *  |            |     r2    |  r1  |     |
+ *  +------------+-----------+------+-----+
+ *
+ * Expect a merge of both regions. Both the base address and size of the region
+ * get updated.
+ */
+static int alloc_top_down_after_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	struct region r1;
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r2_size = SZ_512;
+	phys_addr_t total_size;
+
+	setup_memblock();
+
+	/* The first region starts at the aligned address to test region merging */
+	r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES;
+	r1.size = SZ_8;
+
+	total_size = r1.size + r2_size;
+
+	memblock_reserve(r1.base, r1.size);
+
+	allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == total_size);
+	assert(rgn->base == r1.base - r2_size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there are two reserved regions with
+ * a gap too small to fit the new region:
+ *
+ *  |       +--------+----------+   +------|
+ *  |       |   r3   |    r2    |   |  r1  |
+ *  +-------+--------+----------+---+------+
+ *
+ * Expect to allocate a region before the one that starts at the lower address,
+ * and merge them into one. The region counter and total size fields get
+ * updated.
+ */
+static int alloc_top_down_second_fit_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	struct region r1, r2;
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r3_size = SZ_1K;
+	phys_addr_t total_size;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SZ_512;
+	r1.size = SZ_512;
+
+	r2.base = r1.base - SZ_512;
+	r2.size = SZ_256;
+
+	total_size = r1.size + r2.size + r3_size;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == r2.size + r3_size);
+	assert(rgn->base == r2.base - r3_size);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there are two reserved regions with
+ * a gap big enough to accommodate the new region:
+ *
+ *  |     +--------+--------+--------+     |
+ *  |     |   r2   |   r3   |   r1   |     |
+ *  +-----+--------+--------+--------+-----+
+ *
+ * Expect to merge all of them, creating one big entry in memblock.reserved
+ * array. The region counter and total size fields get updated.
+ */
+static int alloc_in_between_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	struct region r1, r2;
+	void *allocated_ptr = NULL;
+
+	phys_addr_t gap_size = SMP_CACHE_BYTES;
+	phys_addr_t r3_size = SZ_64;
+	/* Calculate regions size so there's just enough space for the new entry */
+	phys_addr_t rgn_size = (MEM_SIZE - (2 * gap_size + r3_size)) / 2;
+	phys_addr_t total_size;
+
+	setup_memblock();
+
+	r1.size = rgn_size;
+	r1.base = memblock_end_of_DRAM() - (gap_size + rgn_size);
+
+	r2.size = rgn_size;
+	r2.base = memblock_start_of_DRAM() + gap_size;
+
+	total_size = r1.size + r2.size + r3_size;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == total_size);
+	assert(rgn->base == r1.base - r2.size - r3_size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when the memory is filled with reserved
+ * regions with memory gaps too small to fit the new region:
+ *
+ * +-------+
+ * |  new  |
+ * +--+----+
+ *    |    +-----+    +-----+    +-----+    |
+ *    |    | res |    | res |    | res |    |
+ *    +----+-----+----+-----+----+-----+----+
+ *
+ * Expect no allocation to happen.
+ */
+static int alloc_small_gaps_generic_check(void)
+{
+	void *allocated_ptr = NULL;
+
+	phys_addr_t region_size = SZ_1K;
+	phys_addr_t gap_size = SZ_256;
+	phys_addr_t region_end;
+
+	setup_memblock();
+
+	region_end = memblock_start_of_DRAM();
+
+	while (region_end < memblock_end_of_DRAM()) {
+		memblock_reserve(region_end + gap_size, region_size);
+		region_end += gap_size + region_size;
+	}
+
+	allocated_ptr = memblock_alloc(region_size, SMP_CACHE_BYTES);
+
+	assert(!allocated_ptr);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when all memory is reserved.
+ * Expect no allocation to happen.
+ */
+static int alloc_all_reserved_generic_check(void)
+{
+	void *allocated_ptr = NULL;
+
+	setup_memblock();
+
+	/* Simulate full memory */
+	memblock_reserve(memblock_start_of_DRAM(), MEM_SIZE);
+
+	allocated_ptr = memblock_alloc(SZ_256, SMP_CACHE_BYTES);
+
+	assert(!allocated_ptr);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when the memory is almost full,
+ * with not enough space left for the new region:
+ *
+ *                                +-------+
+ *                                |  new  |
+ *                                +-------+
+ *  |-----------------------------+   |
+ *  |          reserved           |   |
+ *  +-----------------------------+---+
+ *
+ * Expect no allocation to happen.
+ */
+static int alloc_no_space_generic_check(void)
+{
+	void *allocated_ptr = NULL;
+
+	setup_memblock();
+
+	phys_addr_t available_size = SZ_256;
+	phys_addr_t reserved_size = MEM_SIZE - available_size;
+
+	/* Simulate almost-full memory */
+	memblock_reserve(memblock_start_of_DRAM(), reserved_size);
+
+	allocated_ptr = memblock_alloc(SZ_1K, SMP_CACHE_BYTES);
+
+	assert(!allocated_ptr);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when the memory is almost full,
+ * but there is just enough space left:
+ *
+ *  |---------------------------+---------|
+ *  |          reserved         |   new   |
+ *  +---------------------------+---------+
+ *
+ * Expect to allocate memory and merge all the regions. The total size field
+ * gets updated.
+ */
+static int alloc_limited_space_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t available_size = SZ_256;
+	phys_addr_t reserved_size = MEM_SIZE - available_size;
+
+	setup_memblock();
+
+	/* Simulate almost-full memory */
+	memblock_reserve(memblock_start_of_DRAM(), reserved_size);
+
+	allocated_ptr = memblock_alloc(available_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == MEM_SIZE);
+	assert(rgn->base == memblock_start_of_DRAM());
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == MEM_SIZE);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there is no available memory
+ * registered (i.e. memblock.memory has only a dummy entry).
+ * Expect no allocation to happen.
+ */
+static int alloc_no_memory_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	reset_memblock_regions();
+
+	allocated_ptr = memblock_alloc(SZ_1K, SMP_CACHE_BYTES);
+
+	assert(!allocated_ptr);
+	assert(rgn->size == 0);
+	assert(rgn->base == 0);
+	assert(memblock.reserved.total_size == 0);
+
+	return 0;
+}
+
+int memblock_alloc_checks(void)
+{
+	reset_memblock_attributes();
+	dummy_physical_memory_init();
+
+	alloc_top_down_simple_check();
+	alloc_top_down_disjoint_check();
+	alloc_top_down_before_check();
+	alloc_top_down_after_check();
+	alloc_top_down_second_fit_check();
+	alloc_in_between_generic_check();
+	alloc_small_gaps_generic_check();
+	alloc_all_reserved_generic_check();
+	alloc_no_space_generic_check();
+	alloc_limited_space_generic_check();
+	alloc_no_memory_generic_check();
+
+	dummy_physical_memory_cleanup();
+
+	return 0;
+}
diff --git a/tools/testing/memblock/tests/alloc_api.h b/tools/testing/memblock/tests/alloc_api.h
new file mode 100644
index 000000000000..585b085baf21
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_api.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _MEMBLOCK_ALLOCS_H
+#define _MEMBLOCK_ALLOCS_H
+
+#include "common.h"
+
+int memblock_alloc_checks(void);
+
+#endif
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/9] memblock tests: Add memblock_alloc tests for bottom up
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
                   ` (2 preceding siblings ...)
  2022-02-28 14:46 ` [PATCH 3/9] memblock tests: Add memblock_alloc tests for top down Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 5/9] memblock tests: Add memblock_alloc_from tests for top down Karolina Drobnik
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Add checks for memblock_alloc for bottom up allocation direction.
The tested scenarios are:
  - Region can be allocated on the first fit (with and without
    region merging)
  - Region can be allocated on the second fit (with and without
    region merging)

Add test case wrappers to test both directions in the same context.

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/tests/alloc_api.c | 322 ++++++++++++++++++++++-
 1 file changed, 318 insertions(+), 4 deletions(-)

diff --git a/tools/testing/memblock/tests/alloc_api.c b/tools/testing/memblock/tests/alloc_api.c
index 22ba9a2b4eaf..5d8acf4255d7 100644
--- a/tools/testing/memblock/tests/alloc_api.c
+++ b/tools/testing/memblock/tests/alloc_api.c
@@ -405,23 +405,337 @@ static int alloc_no_memory_generic_check(void)
 	return 0;
 }
 
-int memblock_alloc_checks(void)
+/*
+ * A simple test that tries to allocate a small memory region.
+ * Expect to allocate an aligned region at the beginning of the available
+ * memory.
+ */
+static int alloc_bottom_up_simple_check(void)
 {
-	reset_memblock_attributes();
-	dummy_physical_memory_init();
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	setup_memblock();
+
+	allocated_ptr = memblock_alloc(SZ_2, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == SZ_2);
+	assert(rgn->base == memblock_start_of_DRAM());
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == SZ_2);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory next to a reserved region that starts at
+ * the misaligned address. Expect to create two separate entries, with the new
+ * entry aligned to the provided alignment:
+ *
+ *                      +
+ *  |    +----------+   +----------+     |
+ *  |    |   rgn1   |   |   rgn2   |     |
+ *  +----+----------+---+----------+-----+
+ *                      ^
+ *                      |
+ *                      Aligned address boundary
+ *
+ * The allocation direction is bottom-up, so the new region will be the second
+ * entry in memory.reserved array. The previously reserved region does not get
+ * modified. Region counter and total size get updated.
+ */
+static int alloc_bottom_up_disjoint_check(void)
+{
+	struct memblock_region *rgn1 = &memblock.reserved.regions[0];
+	struct memblock_region *rgn2 = &memblock.reserved.regions[1];
+	struct region r1;
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r2_size = SZ_16;
+	/* Use custom alignment */
+	phys_addr_t alignment = SMP_CACHE_BYTES * 2;
+	phys_addr_t total_size;
+	phys_addr_t expected_start;
+
+	setup_memblock();
+
+	r1.base = memblock_start_of_DRAM() + SZ_2;
+	r1.size = SZ_2;
+
+	total_size = r1.size + r2_size;
+	expected_start = memblock_start_of_DRAM() + alignment;
+
+	memblock_reserve(r1.base, r1.size);
+
+	allocated_ptr = memblock_alloc(r2_size, alignment);
+
+	assert(allocated_ptr);
+
+	assert(rgn1->size == r1.size);
+	assert(rgn1->base == r1.base);
+
+	assert(rgn2->size == r2_size);
+	assert(rgn2->base == expected_start);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there is enough space at
+ * the beginning of the previously reserved block (i.e. first fit):
+ *
+ *  |------------------+--------+         |
+ *  |        r1        |   r2   |         |
+ *  +------------------+--------+---------+
+ *
+ * Expect a merge of both regions. Only the region size gets updated.
+ */
+static int alloc_bottom_up_before_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
 
+	phys_addr_t r1_size = SZ_512;
+	phys_addr_t r2_size = SZ_128;
+	phys_addr_t total_size = r1_size + r2_size;
+
+	setup_memblock();
+
+	memblock_reserve(memblock_start_of_DRAM() + r1_size, r2_size);
+
+	allocated_ptr = memblock_alloc(r1_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == total_size);
+	assert(rgn->base == memblock_start_of_DRAM());
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there is not enough space at
+ * the beginning of the previously reserved block (i.e. second fit):
+ *
+ *  |    +--------+--------------+         |
+ *  |    |   r1   |      r2      |         |
+ *  +----+--------+--------------+---------+
+ *
+ * Expect a merge of both regions. Only the region size gets updated.
+ */
+static int alloc_bottom_up_after_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	struct region r1;
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r2_size = SZ_512;
+	phys_addr_t total_size;
+
+	setup_memblock();
+
+	/* The first region starts at the aligned address to test region merging */
+	r1.base = memblock_start_of_DRAM() + SMP_CACHE_BYTES;
+	r1.size = SZ_64;
+
+	total_size = r1.size + r2_size;
+
+	memblock_reserve(r1.base, r1.size);
+
+	allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == total_size);
+	assert(rgn->base == r1.base);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory when there are two reserved regions, the
+ * first one starting at the beginning of the available memory, with a gap too
+ * small to fit the new region:
+ *
+ *  |------------+     +--------+--------+  |
+ *  |     r1     |     |   r2   |   r3   |  |
+ *  +------------+-----+--------+--------+--+
+ *
+ * Expect to allocate after the second region, which starts at the higher
+ * address, and merge them into one. The region counter and total size fields
+ * get updated.
+ */
+static int alloc_bottom_up_second_fit_check(void)
+{
+	struct memblock_region *rgn  = &memblock.reserved.regions[1];
+	struct region r1, r2;
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r3_size = SZ_1K;
+	phys_addr_t total_size;
+
+	setup_memblock();
+
+	r1.base = memblock_start_of_DRAM();
+	r1.size = SZ_512;
+
+	r2.base = r1.base + r1.size + SZ_512;
+	r2.size = SZ_256;
+
+	total_size = r1.size + r2.size + r3_size;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES);
+
+	assert(allocated_ptr);
+	assert(rgn->size == r2.size + r3_size);
+	assert(rgn->base == r2.base);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/* Test case wrappers */
+static int alloc_simple_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_top_down_simple_check();
+	memblock_set_bottom_up(true);
+	alloc_bottom_up_simple_check();
+
+	return 0;
+}
+
+static int alloc_disjoint_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_top_down_disjoint_check();
+	memblock_set_bottom_up(true);
+	alloc_bottom_up_disjoint_check();
+
+	return 0;
+}
+
+static int alloc_before_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_top_down_before_check();
+	memblock_set_bottom_up(true);
+	alloc_bottom_up_before_check();
+
+	return 0;
+}
+
+static int alloc_after_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_top_down_after_check();
-	alloc_top_down_second_fit_check();
+	memblock_set_bottom_up(true);
+	alloc_bottom_up_after_check();
+
+	return 0;
+}
+
+static int alloc_in_between_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_in_between_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_in_between_generic_check();
+
+	return 0;
+}
+
+static int alloc_second_fit_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_top_down_second_fit_check();
+	memblock_set_bottom_up(true);
+	alloc_bottom_up_second_fit_check();
+
+	return 0;
+}
+
+static int alloc_small_gaps_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_small_gaps_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_small_gaps_generic_check();
+
+	return 0;
+}
+
+static int alloc_all_reserved_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_all_reserved_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_all_reserved_generic_check();
+
+	return 0;
+}
+
+static int alloc_no_space_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_no_space_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_no_space_generic_check();
+
+	return 0;
+}
+
+static int alloc_limited_space_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_limited_space_generic_check();
+	memblock_set_bottom_up(true);
+	alloc_limited_space_generic_check();
+
+	return 0;
+}
+
+static int alloc_no_memory_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_no_memory_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_no_memory_generic_check();
 
+	return 0;
+}
+
+int memblock_alloc_checks(void)
+{
+	reset_memblock_attributes();
+	dummy_physical_memory_init();
+
+	alloc_simple_check();
+	alloc_disjoint_check();
+	alloc_before_check();
+	alloc_after_check();
+	alloc_second_fit_check();
+	alloc_small_gaps_check();
+	alloc_in_between_check();
+	alloc_all_reserved_check();
+	alloc_no_space_check();
+	alloc_limited_space_check();
+	alloc_no_memory_check();
+
 	dummy_physical_memory_cleanup();
 
 	return 0;
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/9] memblock tests: Add memblock_alloc_from tests for top down
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
                   ` (3 preceding siblings ...)
  2022-02-28 14:46 ` [PATCH 4/9] memblock tests: Add memblock_alloc tests for bottom up Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 6/9] memblock tests: Add memblock_alloc_from tests for bottom up Karolina Drobnik
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Add checks for memblock_alloc_from for default allocation direction.
The tested scenarios are:
  - Not enough space to allocate memory at the minimal address
  - Minimal address parameter is smaller than the start address
    of the available memory
  - Minimal address is too close to the available memory

Add simple memblock_alloc_from test that can be used to test both
allocation directions (minimal address is aligned or misaligned).

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/Makefile               |   3 +-
 tools/testing/memblock/main.c                 |   2 +
 .../memblock/tests/alloc_helpers_api.c        | 226 ++++++++++++++++++
 .../memblock/tests/alloc_helpers_api.h        |   9 +
 4 files changed, 239 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/memblock/tests/alloc_helpers_api.c
 create mode 100644 tools/testing/memblock/tests/alloc_helpers_api.h

diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile
index 5b01cfd808d0..89e374470009 100644
--- a/tools/testing/memblock/Makefile
+++ b/tools/testing/memblock/Makefile
@@ -6,7 +6,8 @@ CFLAGS += -I. -I../../include -Wall -O2 -fsanitize=address \
 	  -fsanitize=undefined -D CONFIG_PHYS_ADDR_T_64BIT
 LDFLAGS += -fsanitize=address -fsanitize=undefined
 TARGETS = main
-TEST_OFILES = tests/alloc_api.o tests/basic_api.o tests/common.o
+TEST_OFILES = tests/alloc_helpers_api.o tests/alloc_api.o tests/basic_api.o \
+	      tests/common.o
 DEP_OFILES = memblock.o lib/slab.o mmzone.o slab.o
 OFILES = main.o $(DEP_OFILES) $(TEST_OFILES)
 EXTR_SRC = ../../../mm/memblock.c
diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c
index e7cc45dc06d4..b63150ee554f 100644
--- a/tools/testing/memblock/main.c
+++ b/tools/testing/memblock/main.c
@@ -1,11 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 #include "tests/basic_api.h"
 #include "tests/alloc_api.h"
+#include "tests/alloc_helpers_api.h"
 
 int main(int argc, char **argv)
 {
 	memblock_basic_checks();
 	memblock_alloc_checks();
+	memblock_alloc_helpers_checks();
 
 	return 0;
 }
diff --git a/tools/testing/memblock/tests/alloc_helpers_api.c b/tools/testing/memblock/tests/alloc_helpers_api.c
new file mode 100644
index 000000000000..dc5152adcc5b
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_helpers_api.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "alloc_helpers_api.h"
+
+/*
+ * A simple test that tries to allocate a memory region above a specified,
+ * aligned address:
+ *
+ *             +
+ *  |          +-----------+         |
+ *  |          |    rgn    |         |
+ *  +----------+-----------+---------+
+ *             ^
+ *             |
+ *             Aligned min_addr
+ *
+ * Expect to allocate a cleared region at the minimal memory address.
+ */
+static int alloc_from_simple_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_16;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_end_of_DRAM() - SMP_CACHE_BYTES;
+
+	allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == min_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region above a certain address.
+ * The minimal address here is not aligned:
+ *
+ *         +      +
+ *  |      +      +---------+            |
+ *  |      |      |   rgn   |            |
+ *  +------+------+---------+------------+
+ *         ^      ^------.
+ *         |             |
+ *       min_addr        Aligned address
+ *                       boundary
+ *
+ * Expect to allocate a cleared region at the closest aligned memory address.
+ */
+static int alloc_from_misaligned_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_32;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	/* A misaligned address */
+	min_addr = memblock_end_of_DRAM() - (SMP_CACHE_BYTES * 2 - 1);
+
+	allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == memblock_end_of_DRAM() - SMP_CACHE_BYTES);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region above an address that is too
+ * close to the end of the memory:
+ *
+ *              +        +
+ *  |           +--------+---+      |
+ *  |           |   rgn  +   |      |
+ *  +-----------+--------+---+------+
+ *              ^        ^
+ *              |        |
+ *              |        min_addr
+ *              |
+ *              Aligned address
+ *              boundary
+ *
+ * Expect to prioritize granting memory over satisfying the minimal address
+ * requirement.
+ */
+static int alloc_from_top_down_high_addr_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t size = SZ_32;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	/* The address is too close to the end of the memory */
+	min_addr = memblock_end_of_DRAM() - SZ_16;
+
+	allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
+
+	assert(allocated_ptr);
+	assert(rgn->size == size);
+	assert(rgn->base == memblock_end_of_DRAM() - SMP_CACHE_BYTES);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region when there is no space
+ * available above the minimal address above a certain address:
+ *
+ *                     +
+ *  |        +---------+-------------|
+ *  |        |   rgn   |             |
+ *  +--------+---------+-------------+
+ *                     ^
+ *                     |
+ *                     min_addr
+ *
+ * Expect to prioritize granting memory over satisfying the minimal address
+ * requirement and to allocate next to the previously reserved region. The
+ * regions get merged into one.
+ */
+static int alloc_from_top_down_no_space_above_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r1_size = SZ_64;
+	phys_addr_t r2_size = SZ_2;
+	phys_addr_t total_size = r1_size + r2_size;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_end_of_DRAM() - SMP_CACHE_BYTES * 2;
+
+	/* No space above this address */
+	memblock_reserve(min_addr, r2_size);
+
+	allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
+
+	assert(allocated_ptr);
+	assert(rgn->base == min_addr - r1_size);
+	assert(rgn->size == total_size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region with a minimal address below
+ * the start address of the available memory. As the allocation is top-down,
+ * first reserve a region that will force allocation near the start.
+ * Expect successful allocation and merge of both regions.
+ */
+static int alloc_from_top_down_min_addr_cap_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r1_size = SZ_64;
+	phys_addr_t min_addr;
+	phys_addr_t start_addr;
+
+	setup_memblock();
+
+	start_addr = (phys_addr_t)memblock_start_of_DRAM();
+	min_addr = start_addr - SMP_CACHE_BYTES * 3;
+
+	memblock_reserve(start_addr + r1_size, MEM_SIZE - r1_size);
+
+	allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
+
+	assert(allocated_ptr);
+	assert(rgn->base == start_addr);
+	assert(rgn->size == MEM_SIZE);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == MEM_SIZE);
+
+	return 0;
+}
+
+int memblock_alloc_helpers_checks(void)
+{
+	reset_memblock_attributes();
+	dummy_physical_memory_init();
+
+	alloc_from_simple_generic_check();
+	alloc_from_misaligned_generic_check();
+	alloc_from_top_down_high_addr_check();
+	alloc_from_top_down_min_addr_cap_check();
+	alloc_from_top_down_no_space_above_check();
+
+	dummy_physical_memory_cleanup();
+
+	return 0;
+}
diff --git a/tools/testing/memblock/tests/alloc_helpers_api.h b/tools/testing/memblock/tests/alloc_helpers_api.h
new file mode 100644
index 000000000000..c9e4827b1623
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_helpers_api.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _MEMBLOCK_ALLOC_HELPERS_H
+#define _MEMBLOCK_ALLOC_HELPERS_H
+
+#include "common.h"
+
+int memblock_alloc_helpers_checks(void);
+
+#endif
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/9] memblock tests: Add memblock_alloc_from tests for bottom up
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
                   ` (4 preceding siblings ...)
  2022-02-28 14:46 ` [PATCH 5/9] memblock tests: Add memblock_alloc_from tests for top down Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 7/9] memblock tests: Add memblock_alloc_try_nid tests for top down Karolina Drobnik
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Add checks for memblock_alloc_from for bottom up allocation direction.
The tested scenarios are:
  - Not enough space to allocate memory at the minimal address
  - Minimal address parameter is smaller than the start address
    of the available memory
  - Minimal address parameter is too close to the end of the available
    memory

Add test case wrappers to test both directions in the same context.

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 .../memblock/tests/alloc_helpers_api.c        | 175 +++++++++++++++++-
 1 file changed, 171 insertions(+), 4 deletions(-)

diff --git a/tools/testing/memblock/tests/alloc_helpers_api.c b/tools/testing/memblock/tests/alloc_helpers_api.c
index dc5152adcc5b..963a966db461 100644
--- a/tools/testing/memblock/tests/alloc_helpers_api.c
+++ b/tools/testing/memblock/tests/alloc_helpers_api.c
@@ -209,16 +209,183 @@ static int alloc_from_top_down_min_addr_cap_check(void)
 	return 0;
 }

-int memblock_alloc_helpers_checks(void)
+/*
+ * A test that tries to allocate a memory region above an address that is too
+ * close to the end of the memory:
+ *
+ *                             +
+ *  |-----------+              +     |
+ *  |    rgn    |              |     |
+ *  +-----------+--------------+-----+
+ *  ^                          ^
+ *  |                          |
+ *  Aligned address            min_addr
+ *  boundary
+ *
+ * Expect to prioritize granting memory over satisfying the minimal address
+ * requirement. Allocation happens at beginning of the available memory.
+ */
+static int alloc_from_bottom_up_high_addr_check(void)
 {
-	reset_memblock_attributes();
-	dummy_physical_memory_init();
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t size = SZ_32;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	/* The address is too close to the end of the memory */
+	min_addr = memblock_end_of_DRAM() - SZ_8;
+
+	allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
+
+	assert(allocated_ptr);
+	assert(rgn->size == size);
+	assert(rgn->base == memblock_start_of_DRAM());
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}

+/*
+ * A test that tries to allocate a memory region when there is no space
+ * available above the minimal address above a certain address:
+ *
+ *                   +
+ *  |-----------+    +-------------------|
+ *  |    rgn    |    |                   |
+ *  +-----------+----+-------------------+
+ *                   ^
+ *                   |
+ *                   min_addr
+ *
+ * Expect to prioritize granting memory over satisfying the minimal address
+ * requirement and to allocate at the beginning of the available memory.
+ */
+static int alloc_from_bottom_up_no_space_above_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r1_size = SZ_64;
+	phys_addr_t min_addr;
+	phys_addr_t r2_size;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SZ_128;
+	r2_size = memblock_end_of_DRAM() - min_addr;
+
+	/* No space above this address */
+	memblock_reserve(min_addr - SMP_CACHE_BYTES, r2_size);
+
+	allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
+
+	assert(allocated_ptr);
+	assert(rgn->base == memblock_start_of_DRAM());
+	assert(rgn->size == r1_size);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == r1_size + r2_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region with a minimal address below
+ * the start address of the available memory. Expect to allocate a region
+ * at the beginning of the available memory.
+ */
+static int alloc_from_bottom_up_min_addr_cap_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+
+	phys_addr_t r1_size = SZ_64;
+	phys_addr_t min_addr;
+	phys_addr_t start_addr;
+
+	setup_memblock();
+
+	start_addr = (phys_addr_t)memblock_start_of_DRAM();
+	min_addr = start_addr - SMP_CACHE_BYTES * 3;
+
+	allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
+
+	assert(allocated_ptr);
+	assert(rgn->base == start_addr);
+	assert(rgn->size == r1_size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == r1_size);
+
+	return 0;
+}
+
+/* Test case wrappers */
+static int alloc_from_simple_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_from_simple_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_from_simple_generic_check();
+
+	return 0;
+}
+
+static int alloc_from_misaligned_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_from_misaligned_generic_check();
+	memblock_set_bottom_up(true);
+	alloc_from_misaligned_generic_check();
+
+	return 0;
+}
+
+static int alloc_from_high_addr_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_from_top_down_high_addr_check();
-	alloc_from_top_down_min_addr_cap_check();
+	memblock_set_bottom_up(true);
+	alloc_from_bottom_up_high_addr_check();
+
+	return 0;
+}
+
+static int alloc_from_no_space_above_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_from_top_down_no_space_above_check();
+	memblock_set_bottom_up(true);
+	alloc_from_bottom_up_no_space_above_check();
+
+	return 0;
+}
+
+static int alloc_from_min_addr_cap_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_from_top_down_min_addr_cap_check();
+	memblock_set_bottom_up(true);
+	alloc_from_bottom_up_min_addr_cap_check();
+
+	return 0;
+}
+
+int memblock_alloc_helpers_checks(void)
+{
+	reset_memblock_attributes();
+	dummy_physical_memory_init();
+
+	alloc_from_simple_check();
+	alloc_from_misaligned_check();
+	alloc_from_high_addr_check();
+	alloc_from_no_space_above_check();
+	alloc_from_min_addr_cap_check();

 	dummy_physical_memory_cleanup();

--
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 7/9] memblock tests: Add memblock_alloc_try_nid tests for top down
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
                   ` (5 preceding siblings ...)
  2022-02-28 14:46 ` [PATCH 6/9] memblock tests: Add memblock_alloc_from tests for bottom up Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 8/9] memblock tests: Add memblock_alloc_try_nid tests for bottom up Karolina Drobnik
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Add tests for memblock_alloc_try_nid for top down allocation direction.
As the definition of this function is pretty close to the core
memblock_alloc_range_nid, the test cases implemented here cover most of
the code paths related to the memory allocations.

The tested scenarios are:
  - Region can be allocated within the requested range (both with aligned
    and misaligned boundaries)
  - Region can be allocated between two already existing entries
  - Not enough space between already reserved regions
  - Memory range is too narrow but memory can be allocated before
    the maximum address
  - Edge cases:
      + Minimum address is below memblock_start_of_DRAM()
      + Maximum address is above memblock_end_of_DRAM()

Add checks for both allocation directions:
  - Region starts at the min_addr and ends at max_addr
  - Maximum address is too close to the beginning of the available
    memory
  - Memory at the range boundaries is reserved but there is enough space
    to allocate a new region

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/Makefile              |   4 +-
 tools/testing/memblock/main.c                |   2 +
 tools/testing/memblock/tests/alloc_nid_api.c | 679 +++++++++++++++++++
 tools/testing/memblock/tests/alloc_nid_api.h |   9 +
 4 files changed, 692 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/memblock/tests/alloc_nid_api.c
 create mode 100644 tools/testing/memblock/tests/alloc_nid_api.h

diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile
index 89e374470009..a698e24b35e7 100644
--- a/tools/testing/memblock/Makefile
+++ b/tools/testing/memblock/Makefile
@@ -6,8 +6,8 @@ CFLAGS += -I. -I../../include -Wall -O2 -fsanitize=address \
 	  -fsanitize=undefined -D CONFIG_PHYS_ADDR_T_64BIT
 LDFLAGS += -fsanitize=address -fsanitize=undefined
 TARGETS = main
-TEST_OFILES = tests/alloc_helpers_api.o tests/alloc_api.o tests/basic_api.o \
-	      tests/common.o
+TEST_OFILES = tests/alloc_nid_api.o tests/alloc_helpers_api.o tests/alloc_api.o \
+		  tests/basic_api.o tests/common.o
 DEP_OFILES = memblock.o lib/slab.o mmzone.o slab.o
 OFILES = main.o $(DEP_OFILES) $(TEST_OFILES)
 EXTR_SRC = ../../../mm/memblock.c
diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c
index b63150ee554f..fb183c9e76d1 100644
--- a/tools/testing/memblock/main.c
+++ b/tools/testing/memblock/main.c
@@ -2,12 +2,14 @@
 #include "tests/basic_api.h"
 #include "tests/alloc_api.h"
 #include "tests/alloc_helpers_api.h"
+#include "tests/alloc_nid_api.h"

 int main(int argc, char **argv)
 {
 	memblock_basic_checks();
 	memblock_alloc_checks();
 	memblock_alloc_helpers_checks();
+	memblock_alloc_nid_checks();

 	return 0;
 }
diff --git a/tools/testing/memblock/tests/alloc_nid_api.c b/tools/testing/memblock/tests/alloc_nid_api.c
new file mode 100644
index 000000000000..75cfca47c703
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_nid_api.c
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "alloc_nid_api.h"
+
+/*
+ * A simple test that tries to allocate a memory region within min_addr and
+ * max_addr range:
+ *
+ *        +                   +
+ *   |    +       +-----------+      |
+ *   |    |       |    rgn    |      |
+ *   +----+-------+-----------+------+
+ *        ^                   ^
+ *        |                   |
+ *        min_addr           max_addr
+ *
+ * Expect to allocate a cleared region that ends at max_addr.
+ */
+static int alloc_try_nid_top_down_simple_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_128;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+	phys_addr_t rgn_end;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SMP_CACHE_BYTES * 2;
+	max_addr = min_addr + SZ_512;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+	rgn_end = rgn->base + rgn->size;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == max_addr - size);
+	assert(rgn_end == max_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A simple test that tries to allocate a memory region within min_addr and
+ * max_addr range, where the end address is misaligned:
+ *
+ *         +       +            +
+ *  |      +       +---------+  +    |
+ *  |      |       |   rgn   |  |    |
+ *  +------+-------+---------+--+----+
+ *         ^       ^            ^
+ *         |       |            |
+ *       min_add   |            max_addr
+ *                 |
+ *                 Aligned address
+ *                 boundary
+ *
+ * Expect to allocate a cleared, aligned region that ends before max_addr.
+ */
+static int alloc_try_nid_top_down_end_misaligned_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_128;
+	phys_addr_t misalign = SZ_2;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+	phys_addr_t rgn_end;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SMP_CACHE_BYTES * 2;
+	max_addr = min_addr + SZ_512 + misalign;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+	rgn_end = rgn->base + rgn->size;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == max_addr - size - misalign);
+	assert(rgn_end < max_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A simple test that tries to allocate a memory region, which spans over the
+ * min_addr and max_addr range:
+ *
+ *         +               +
+ *  |      +---------------+       |
+ *  |      |      rgn      |       |
+ *  +------+---------------+-------+
+ *         ^               ^
+ *         |               |
+ *         min_addr        max_addr
+ *
+ * Expect to allocate a cleared region that starts at min_addr and ends at
+ * max_addr, given that min_addr is aligned.
+ */
+static int alloc_try_nid_exact_address_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_1K;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+	phys_addr_t rgn_end;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SMP_CACHE_BYTES;
+	max_addr = min_addr + size;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+	rgn_end = rgn->base + rgn->size;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == min_addr);
+	assert(rgn_end == max_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region, which can't fit into
+ * min_addr and max_addr range:
+ *
+ *           +          +     +
+ *  |        +----------+-----+    |
+ *  |        |   rgn    +     |    |
+ *  +--------+----------+-----+----+
+ *           ^          ^     ^
+ *           |          |     |
+ *           Aligned    |    max_addr
+ *           address    |
+ *           boundary   min_add
+ *
+ * Expect to drop the lower limit and allocate a cleared memory region which
+ * ends at max_addr (if the address is aligned).
+ */
+static int alloc_try_nid_top_down_narrow_range_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_256;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SZ_512;
+	max_addr = min_addr + SMP_CACHE_BYTES;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == max_addr - size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region, which can't fit into
+ * min_addr and max_addr range, with the latter being too close to the beginning
+ * of the available memory:
+ *
+ *   +-------------+
+ *   |     new     |
+ *   +-------------+
+ *         +       +
+ *         |       +              |
+ *         |       |              |
+ *         +-------+--------------+
+ *         ^       ^
+ *         |       |
+ *         |       max_addr
+ *         |
+ *         min_addr
+ *
+ * Expect no allocation to happen.
+ */
+static int alloc_try_nid_low_max_generic_check(void)
+{
+	void *allocated_ptr = NULL;
+
+	phys_addr_t size = SZ_1K;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM();
+	max_addr = min_addr + SMP_CACHE_BYTES;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+
+	assert(!allocated_ptr);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region within min_addr min_addr range,
+ * with min_addr being so close that it's next to an allocated region:
+ *
+ *          +                        +
+ *  |       +--------+---------------|
+ *  |       |   r1   |      rgn      |
+ *  +-------+--------+---------------+
+ *          ^                        ^
+ *          |                        |
+ *          min_addr                 max_addr
+ *
+ * Expect a merge of both regions. Only the region size gets updated.
+ */
+static int alloc_try_nid_min_reserved_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t r1_size = SZ_128;
+	phys_addr_t r2_size = SZ_64;
+	phys_addr_t total_size = r1_size + r2_size;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+	phys_addr_t reserved_base;
+
+	setup_memblock();
+
+	max_addr = memblock_end_of_DRAM();
+	min_addr = max_addr - r2_size;
+	reserved_base = min_addr - r1_size;
+
+	memblock_reserve(reserved_base, r1_size);
+
+	allocated_ptr = memblock_alloc_try_nid(r2_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == total_size);
+	assert(rgn->base == reserved_base);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region within min_addr and max_addr,
+ * with max_addr being so close that it's next to an allocated region:
+ *
+ *             +             +
+ *  |          +-------------+--------|
+ *  |          |     rgn     |   r1   |
+ *  +----------+-------------+--------+
+ *             ^             ^
+ *             |             |
+ *             min_addr      max_addr
+ *
+ * Expect a merge of regions. Only the region size gets updated.
+ */
+static int alloc_try_nid_max_reserved_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t r1_size = SZ_64;
+	phys_addr_t r2_size = SZ_128;
+	phys_addr_t total_size = r1_size + r2_size;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	max_addr = memblock_end_of_DRAM() - r1_size;
+	min_addr = max_addr - r2_size;
+
+	memblock_reserve(max_addr, r1_size);
+
+	allocated_ptr = memblock_alloc_try_nid(r2_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == total_size);
+	assert(rgn->base == min_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory within min_addr and max_add range, when
+ * there are two reserved regions at the borders, with a gap big enough to fit
+ * a new region:
+ *
+ *                +           +
+ *  |    +--------+   +-------+------+  |
+ *  |    |   r2   |   |  rgn  |  r1  |  |
+ *  +----+--------+---+-------+------+--+
+ *                ^           ^
+ *                |           |
+ *                min_addr    max_addr
+ *
+ * Expect to merge the new region with r1. The second region does not get
+ * updated. The total size field gets updated.
+ */
+
+static int alloc_try_nid_top_down_reserved_with_space_check(void)
+{
+	struct memblock_region *rgn1 = &memblock.reserved.regions[1];
+	struct memblock_region *rgn2 = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+	struct region r1, r2;
+
+	phys_addr_t r3_size = SZ_64;
+	phys_addr_t gap_size = SMP_CACHE_BYTES;
+	phys_addr_t total_size;
+	phys_addr_t max_addr;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES * 2;
+	r1.size = SMP_CACHE_BYTES;
+
+	r2.size = SZ_128;
+	r2.base = r1.base - (r3_size + gap_size + r2.size);
+
+	total_size = r1.size + r2.size + r3_size;
+	min_addr = r2.base + r2.size;
+	max_addr = r1.base;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc_try_nid(r3_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn1->size == r1.size + r3_size);
+	assert(rgn1->base == max_addr - r3_size);
+
+	assert(rgn2->size == r2.size);
+	assert(rgn2->base == r2.base);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory within min_addr and max_add range, when
+ * there are two reserved regions at the borders, with a gap of a size equal to
+ * the size of the new region:
+ *
+ *                 +        +
+ *  |     +--------+--------+--------+     |
+ *  |     |   r2   |   r3   |   r1   |     |
+ *  +-----+--------+--------+--------+-----+
+ *                 ^        ^
+ *                 |        |
+ *                 min_addr max_addr
+ *
+ * Expect to merge all of the regions into one. The region counter and total
+ * size fields get updated.
+ */
+static int alloc_try_nid_reserved_full_merge_generic_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+	struct region r1, r2;
+
+	phys_addr_t r3_size = SZ_64;
+	phys_addr_t total_size;
+	phys_addr_t max_addr;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES * 2;
+	r1.size = SMP_CACHE_BYTES;
+
+	r2.size = SZ_128;
+	r2.base = r1.base - (r3_size + r2.size);
+
+	total_size = r1.size + r2.size + r3_size;
+	min_addr = r2.base + r2.size;
+	max_addr = r1.base;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc_try_nid(r3_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == total_size);
+	assert(rgn->base == r2.base);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory within min_addr and max_add range, when
+ * there are two reserved regions at the borders, with a gap that can't fit
+ * a new region:
+ *
+ *                       +    +
+ *  |  +----------+------+    +------+   |
+ *  |  |    r3    |  r2  |    |  r1  |   |
+ *  +--+----------+------+----+------+---+
+ *                       ^    ^
+ *                       |    |
+ *                       |    max_addr
+ *                       |
+ *                       min_addr
+ *
+ * Expect to merge the new region with r2. The second region does not get
+ * updated. The total size counter gets updated.
+ */
+static int alloc_try_nid_top_down_reserved_no_space_check(void)
+{
+	struct memblock_region *rgn1 = &memblock.reserved.regions[1];
+	struct memblock_region *rgn2 = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+	struct region r1, r2;
+
+	phys_addr_t r3_size = SZ_256;
+	phys_addr_t gap_size = SMP_CACHE_BYTES;
+	phys_addr_t total_size;
+	phys_addr_t max_addr;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES * 2;
+	r1.size = SMP_CACHE_BYTES;
+
+	r2.size = SZ_128;
+	r2.base = r1.base - (r2.size + gap_size);
+
+	total_size = r1.size + r2.size + r3_size;
+	min_addr = r2.base + r2.size;
+	max_addr = r1.base;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc_try_nid(r3_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn1->size == r1.size);
+	assert(rgn1->base == r1.base);
+
+	assert(rgn2->size == r2.size + r3_size);
+	assert(rgn2->base == r2.base - r3_size);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory within min_addr and max_add range, but
+ * it's too narrow and everything else is reserved:
+ *
+ *            +-----------+
+ *            |    new    |
+ *            +-----------+
+ *                 +      +
+ *  |--------------+      +----------|
+ *  |      r2      |      |    r1    |
+ *  +--------------+------+----------+
+ *                 ^      ^
+ *                 |      |
+ *                 |      max_addr
+ *                 |
+ *                 min_addr
+ *
+ * Expect no allocation to happen.
+ */
+
+static int alloc_try_nid_reserved_all_generic_check(void)
+{
+	void *allocated_ptr = NULL;
+	struct region r1, r2;
+
+	phys_addr_t r3_size = SZ_256;
+	phys_addr_t gap_size = SMP_CACHE_BYTES;
+	phys_addr_t max_addr;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES;
+	r1.size = SMP_CACHE_BYTES;
+
+	r2.size = MEM_SIZE - (r1.size + gap_size);
+	r2.base = memblock_start_of_DRAM();
+
+	min_addr = r2.base + r2.size;
+	max_addr = r1.base;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc_try_nid(r3_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+
+	assert(!allocated_ptr);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region, where max_addr is
+ * bigger than the end address of the available memory. Expect to allocate
+ * a cleared region that ends before the end of the memory.
+ */
+static int alloc_try_nid_top_down_cap_max_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_256;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_end_of_DRAM() - SZ_1K;
+	max_addr = memblock_end_of_DRAM() + SZ_256;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == memblock_end_of_DRAM() - size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region, where min_addr is
+ * smaller than the start address of the available memory. Expect to allocate
+ * a cleared region that ends before the end of the memory.
+ */
+static int alloc_try_nid_top_down_cap_min_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_1K;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() - SZ_256;
+	max_addr = memblock_end_of_DRAM();
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == memblock_end_of_DRAM() - size);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+int memblock_alloc_nid_checks(void)
+{
+	reset_memblock_attributes();
+	dummy_physical_memory_init();
+
+	alloc_try_nid_top_down_simple_check();
+	alloc_try_nid_top_down_end_misaligned_check();
+	alloc_try_nid_top_down_narrow_range_check();
+	alloc_try_nid_top_down_reserved_with_space_check();
+	alloc_try_nid_top_down_reserved_no_space_check();
+	alloc_try_nid_top_down_cap_min_check();
+	alloc_try_nid_top_down_cap_max_check();
+
+	alloc_try_nid_min_reserved_generic_check();
+	alloc_try_nid_max_reserved_generic_check();
+	alloc_try_nid_exact_address_generic_check();
+	alloc_try_nid_reserved_full_merge_generic_check();
+	alloc_try_nid_reserved_all_generic_check();
+	alloc_try_nid_low_max_generic_check();
+
+	dummy_physical_memory_cleanup();
+
+	return 0;
+}
diff --git a/tools/testing/memblock/tests/alloc_nid_api.h b/tools/testing/memblock/tests/alloc_nid_api.h
new file mode 100644
index 000000000000..b35cf3c3f489
--- /dev/null
+++ b/tools/testing/memblock/tests/alloc_nid_api.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _MEMBLOCK_ALLOC_NID_H
+#define _MEMBLOCK_ALLOC_NID_H
+
+#include "common.h"
+
+int memblock_alloc_nid_checks(void);
+
+#endif
--
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 8/9] memblock tests: Add memblock_alloc_try_nid tests for bottom up
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
                   ` (6 preceding siblings ...)
  2022-02-28 14:46 ` [PATCH 7/9] memblock tests: Add memblock_alloc_try_nid tests for top down Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-02-28 14:46 ` [PATCH 9/9] memblock tests: Add TODO and README files Karolina Drobnik
  2022-03-07 18:17 ` [PATCH 0/9] Add tests for memblock allocation functions Mike Rapoport
  9 siblings, 0 replies; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Add checks for memblock_alloc_try_nid for bottom up allocation direction.
As the definition of this function is pretty close to the core
memblock_alloc_range_nid, the test cases implemented here cover most of
the code paths related to the memory allocations.

The tested scenarios are:
  - Region can be allocated within the requested range (both with aligned
    and misaligned boundaries)
  - Region can be allocated between two already existing entries
  - Not enough space between already reserved regions
  - Memory at the range boundaries is reserved but there is enough space
    to allocate a new region
  - The memory range is too narrow but memory can be allocated before
    the maximum address
  - Edge cases:
      + Minimum address is below memblock_start_of_DRAM()
      + Maximum address is above memblock_end_of_DRAM()

Add test case wrappers to test both directions in the same context.

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/tests/alloc_nid_api.c | 496 ++++++++++++++++++-
 1 file changed, 492 insertions(+), 4 deletions(-)

diff --git a/tools/testing/memblock/tests/alloc_nid_api.c b/tools/testing/memblock/tests/alloc_nid_api.c
index 75cfca47c703..03216efe3488 100644
--- a/tools/testing/memblock/tests/alloc_nid_api.c
+++ b/tools/testing/memblock/tests/alloc_nid_api.c
@@ -653,26 +653,514 @@ static int alloc_try_nid_top_down_cap_min_check(void)
 	return 0;
 }
 
-int memblock_alloc_nid_checks(void)
+/*
+ * A simple test that tries to allocate a memory region within min_addr and
+ * max_addr range:
+ *
+ *        +                       +
+ *   |    +-----------+           |      |
+ *   |    |    rgn    |           |      |
+ *   +----+-----------+-----------+------+
+ *        ^                       ^
+ *        |                       |
+ *        min_addr                max_addr
+ *
+ * Expect to allocate a cleared region that ends before max_addr.
+ */
+static int alloc_try_nid_bottom_up_simple_check(void)
 {
-	reset_memblock_attributes();
-	dummy_physical_memory_init();
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_128;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+	phys_addr_t rgn_end;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SMP_CACHE_BYTES * 2;
+	max_addr = min_addr + SZ_512;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+	rgn_end = rgn->base + rgn->size;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == min_addr);
+	assert(rgn_end < max_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A simple test that tries to allocate a memory region within min_addr and
+ * max_addr range, where the start address is misaligned:
+ *
+ *        +                     +
+ *  |     +   +-----------+     +     |
+ *  |     |   |    rgn    |     |     |
+ *  +-----+---+-----------+-----+-----+
+ *        ^   ^----.            ^
+ *        |        |            |
+ *     min_add     |            max_addr
+ *                 |
+ *                 Aligned address
+ *                 boundary
+ *
+ * Expect to allocate a cleared, aligned region that ends before max_addr.
+ */
+static int alloc_try_nid_bottom_up_start_misaligned_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_128;
+	phys_addr_t misalign = SZ_2;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+	phys_addr_t rgn_end;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + misalign;
+	max_addr = min_addr + SZ_512;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+	rgn_end = rgn->base + rgn->size;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == min_addr + (SMP_CACHE_BYTES - misalign));
+	assert(rgn_end < max_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region, which can't fit into min_addr
+ * and max_addr range:
+ *
+ *                      +    +
+ *  |---------+         +    +      |
+ *  |   rgn   |         |    |      |
+ *  +---------+---------+----+------+
+ *                      ^    ^
+ *                      |    |
+ *                      |    max_addr
+ *                      |
+ *                      min_add
+ *
+ * Expect to drop the lower limit and allocate a cleared memory region which
+ * starts at the beginning of the available memory.
+ */
+static int alloc_try_nid_bottom_up_narrow_range_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_256;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SZ_512;
+	max_addr = min_addr + SMP_CACHE_BYTES;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == memblock_start_of_DRAM());
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory within min_addr and max_add range, when
+ * there are two reserved regions at the borders, with a gap big enough to fit
+ * a new region:
+ *
+ *                +           +
+ *  |    +--------+-------+   +------+  |
+ *  |    |   r2   |  rgn  |   |  r1  |  |
+ *  +----+--------+-------+---+------+--+
+ *                ^           ^
+ *                |           |
+ *                min_addr    max_addr
+ *
+ * Expect to merge the new region with r2. The second region does not get
+ * updated. The total size field gets updated.
+ */
+
+static int alloc_try_nid_bottom_up_reserved_with_space_check(void)
+{
+	struct memblock_region *rgn1 = &memblock.reserved.regions[1];
+	struct memblock_region *rgn2 = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+	struct region r1, r2;
+
+	phys_addr_t r3_size = SZ_64;
+	phys_addr_t gap_size = SMP_CACHE_BYTES;
+	phys_addr_t total_size;
+	phys_addr_t max_addr;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES * 2;
+	r1.size = SMP_CACHE_BYTES;
+
+	r2.size = SZ_128;
+	r2.base = r1.base - (r3_size + gap_size + r2.size);
+
+	total_size = r1.size + r2.size + r3_size;
+	min_addr = r2.base + r2.size;
+	max_addr = r1.base;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc_try_nid(r3_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn1->size == r1.size);
+	assert(rgn1->base == max_addr);
+
+	assert(rgn2->size == r2.size + r3_size);
+	assert(rgn2->base == r2.base);
+
+	assert(memblock.reserved.cnt == 2);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate memory within min_addr and max_add range, when
+ * there are two reserved regions at the borders, with a gap of a size equal to
+ * the size of the new region:
+ *
+ *                         +   +
+ *  |----------+    +------+   +----+  |
+ *  |    r3    |    |  r2  |   | r1 |  |
+ *  +----------+----+------+---+----+--+
+ *                         ^   ^
+ *                         |   |
+ *                         |  max_addr
+ *                         |
+ *                         min_addr
+ *
+ * Expect to drop the lower limit and allocate memory at the beginning of the
+ * available memory. The region counter and total size fields get updated.
+ * Other regions are not modified.
+ */
+
+static int alloc_try_nid_bottom_up_reserved_no_space_check(void)
+{
+	struct memblock_region *rgn1 = &memblock.reserved.regions[2];
+	struct memblock_region *rgn2 = &memblock.reserved.regions[1];
+	struct memblock_region *rgn3 = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+	struct region r1, r2;
+
+	phys_addr_t r3_size = SZ_256;
+	phys_addr_t gap_size = SMP_CACHE_BYTES;
+	phys_addr_t total_size;
+	phys_addr_t max_addr;
+	phys_addr_t min_addr;
+
+	setup_memblock();
+
+	r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES * 2;
+	r1.size = SMP_CACHE_BYTES;
+
+	r2.size = SZ_128;
+	r2.base = r1.base - (r2.size + gap_size);
+
+	total_size = r1.size + r2.size + r3_size;
+	min_addr = r2.base + r2.size;
+	max_addr = r1.base;
+
+	memblock_reserve(r1.base, r1.size);
+	memblock_reserve(r2.base, r2.size);
+
+	allocated_ptr = memblock_alloc_try_nid(r3_size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn3->size == r3_size);
+	assert(rgn3->base == memblock_start_of_DRAM());
+
+	assert(rgn2->size == r2.size);
+	assert(rgn2->base == r2.base);
+
+	assert(rgn1->size == r1.size);
+	assert(rgn1->base == r1.base);
+
+	assert(memblock.reserved.cnt == 3);
+	assert(memblock.reserved.total_size == total_size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region, where max_addr is
+ * bigger than the end address of the available memory. Expect to allocate
+ * a cleared region that starts at the min_addr
+ */
+static int alloc_try_nid_bottom_up_cap_max_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_256;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM() + SZ_1K;
+	max_addr = memblock_end_of_DRAM() + SZ_256;
+
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == min_addr);
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/*
+ * A test that tries to allocate a memory region, where min_addr is
+ * smaller than the start address of the available memory. Expect to allocate
+ * a cleared region at the beginning of the available memory.
+ */
+static int alloc_try_nid_bottom_up_cap_min_check(void)
+{
+	struct memblock_region *rgn = &memblock.reserved.regions[0];
+	void *allocated_ptr = NULL;
+	char *b;
+
+	phys_addr_t size = SZ_1K;
+	phys_addr_t min_addr;
+	phys_addr_t max_addr;
+
+	setup_memblock();
+
+	min_addr = memblock_start_of_DRAM();
+	max_addr = memblock_end_of_DRAM() - SZ_256;
 
+	allocated_ptr = memblock_alloc_try_nid(size, SMP_CACHE_BYTES,
+					       min_addr, max_addr, NUMA_NO_NODE);
+	b = (char *)allocated_ptr;
+
+	assert(allocated_ptr);
+	assert(*b == 0);
+
+	assert(rgn->size == size);
+	assert(rgn->base == memblock_start_of_DRAM());
+
+	assert(memblock.reserved.cnt == 1);
+	assert(memblock.reserved.total_size == size);
+
+	return 0;
+}
+
+/* Test case wrappers */
+static int alloc_try_nid_simple_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_top_down_simple_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_bottom_up_simple_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_misaligned_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_top_down_end_misaligned_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_bottom_up_start_misaligned_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_narrow_range_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_top_down_narrow_range_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_bottom_up_narrow_range_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_reserved_with_space_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_top_down_reserved_with_space_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_bottom_up_reserved_with_space_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_reserved_no_space_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_top_down_reserved_no_space_check();
-	alloc_try_nid_top_down_cap_min_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_bottom_up_reserved_no_space_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_cap_max_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_top_down_cap_max_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_bottom_up_cap_max_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_cap_min_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_try_nid_top_down_cap_min_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_bottom_up_cap_min_check();
+
+	return 0;
+}
 
+static int alloc_try_nid_min_reserved_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_min_reserved_generic_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_min_reserved_generic_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_max_reserved_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_max_reserved_generic_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_max_reserved_generic_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_exact_address_check(void)
+{
+	memblock_set_bottom_up(false);
 	alloc_try_nid_exact_address_generic_check();
+	memblock_set_bottom_up(true);
+	alloc_try_nid_exact_address_generic_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_reserved_full_merge_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_try_nid_reserved_full_merge_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_try_nid_reserved_full_merge_generic_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_reserved_all_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_try_nid_reserved_all_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_try_nid_reserved_all_generic_check();
+
+	return 0;
+}
+
+static int alloc_try_nid_low_max_check(void)
+{
+	memblock_set_bottom_up(false);
+	alloc_try_nid_low_max_generic_check();
+	memblock_set_bottom_up(true);
 	alloc_try_nid_low_max_generic_check();
 
+	return 0;
+}
+
+int memblock_alloc_nid_checks(void)
+{
+	reset_memblock_attributes();
+	dummy_physical_memory_init();
+
+	alloc_try_nid_simple_check();
+	alloc_try_nid_misaligned_check();
+	alloc_try_nid_narrow_range_check();
+	alloc_try_nid_reserved_with_space_check();
+	alloc_try_nid_reserved_no_space_check();
+	alloc_try_nid_cap_max_check();
+	alloc_try_nid_cap_min_check();
+
+	alloc_try_nid_min_reserved_check();
+	alloc_try_nid_max_reserved_check();
+	alloc_try_nid_exact_address_check();
+	alloc_try_nid_reserved_full_merge_check();
+	alloc_try_nid_reserved_all_check();
+	alloc_try_nid_low_max_check();
+
 	dummy_physical_memory_cleanup();
 
 	return 0;
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 9/9] memblock tests: Add TODO and README files
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
                   ` (7 preceding siblings ...)
  2022-02-28 14:46 ` [PATCH 8/9] memblock tests: Add memblock_alloc_try_nid tests for bottom up Karolina Drobnik
@ 2022-02-28 14:46 ` Karolina Drobnik
  2022-03-07 18:13   ` Mike Rapoport
  2022-03-07 18:17 ` [PATCH 0/9] Add tests for memblock allocation functions Mike Rapoport
  9 siblings, 1 reply; 13+ messages in thread
From: Karolina Drobnik @ 2022-02-28 14:46 UTC (permalink / raw)
  To: linux-mm; +Cc: rppt, akpm, linux-kernel, Karolina Drobnik

Add description of the project, its structure and how to run it.
List what is left to implement and what the known issues are.

Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
---
 tools/testing/memblock/README | 114 ++++++++++++++++++++++++++++++++++
 tools/testing/memblock/TODO   |  28 +++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 tools/testing/memblock/README
 create mode 100644 tools/testing/memblock/TODO

diff --git a/tools/testing/memblock/README b/tools/testing/memblock/README
new file mode 100644
index 000000000000..40c0ce50e7c2
--- /dev/null
+++ b/tools/testing/memblock/README
@@ -0,0 +1,114 @@
+=========================
+   Memblock simulator
+=========================
+
+
+Introduction
+---------------------
+
+Memblock is a boot time memory allocator[1] that manages memory regions before
+the actual memory management is initialized. Its APIs allow to register physical
+memory regions, mark them as available or reserved, allocate a block of memory
+within the requested range and/or in specific NUMA node, and many more.
+
+Because it is used so early in the booting process, testing and debugging it is
+difficult. This test suite, usually referred as memblock simulator, is
+an attempt at testing the memblock mechanism. It runs one monolithic test that
+consist of a series of checks that exercise both the basic operations and
+allocation functionalities of memblock. The main data structure of the boot time
+memory allocator is initialized at the build time, so the checks here reuse its
+instance throughout the duration of the test. To ensure that tests don't affect
+each other, region arrays are reset in between.
+
+As this project uses the actual memblock code and has to run in user space, some
+of the kernel definitions were stubbed in the introductory patch[2]. Most of
+them don't match the kernel implementation, so one should consult them first
+before making any significant changes to the project.
+
+
+Usage
+---------------------
+
+To run the tests, build the main target and run it:
+
+$ make; ./main
+
+A successful run produces no output. It is also possible to override different
+configuration parameters. For example, to simulate enabled NUMA, use:
+
+$ make NUMA=1
+
+For the full list of options, see `make help`.
+
+
+Project structure
+---------------------
+
+The project has one target, main, which calls a group of checks for basic and
+allocation functions. Tests for each group are defined in dedicated files, as it
+can be seen here:
+
+memblock
+|-- asm       ------------------,
+|-- lib                         |-- implement function and struct stubs
+|-- linux     ------------------'
+|-- scripts
+|    |-- Makefile.include        -- handles `make` parameters
+|-- tests
+|    |-- alloc_api.(c|h)         -- memblock_alloc tests
+|    |-- alloc_helpers_api.(c|h) -- memblock_alloc_from tests
+|    |-- alloc_nid_api.(c|h)     -- memblock_alloc_try_nid tests
+|    |-- basic_api.(c|h)         -- memblock_add/memblock_reserve/... tests
+|    |-- common.(c|h)            -- helper functions for resetting memblock;
+|-- main.c        --------------.   dummy physical memory definition
+|-- Makefile                     `- test runner
+|-- README
+|-- TODO
+|-- .gitignore
+
+
+Simulating physical memory
+--------------------------
+
+Some allocation functions clear the memory in the process, so it is required for
+memblock to track valid memory ranges. To achieve this, the test suite registers
+with memblock memory stored by test_memory struct. It is a small wrapper that
+points to a block of memory allocated via malloc. For each group of allocation
+tests, dummy physical memory is allocated, added to memblock, and then released
+at the end of the test run. The structure of a test runner checking allocation
+functions is as follows:
+
+int memblock_alloc_foo_checks(void)
+{
+	reset_memblock_attributes();     /* data structure reset */
+	dummy_physical_memory_init();    /* allocate and register memory */
+
+	(...allocation checks...)
+
+	dummy_physical_memory_cleanup(); /* free the memory */
+}
+
+There's no need to explicitly free the dummy memory from memblock via
+memblock_free() call. The entry will be erased by reset_memblock_regions(),
+called at the beginning of each test.
+
+
+Known issues
+---------------------
+
+1. Requesting a specific NUMA node via memblock_alloc_node() does not work as
+   intended. Once the fix is in place, tests for this function can be added.
+
+2. Tests for memblock_alloc_low() can't be easily implemented. The function uses
+   ARCH_LOW_ADDRESS_LIMIT marco, which can't be changed to point at the low
+   memory of the memory_block.
+
+
+References
+---------------------
+
+1. Boot time memory management documentation page:
+   https://www.kernel.org/doc/html/latest/core-api/boot-time-mm.html
+
+2. Introduce memblock simulator, lore link:
+https://lore.kernel.org/linux-mm/cover.1643796665.git.karolinadrobnik@gmail.com/
diff --git a/tools/testing/memblock/TODO b/tools/testing/memblock/TODO
new file mode 100644
index 000000000000..c25b2fdec45e
--- /dev/null
+++ b/tools/testing/memblock/TODO
@@ -0,0 +1,28 @@
+TODO
+=====
+
+1. Add verbose output (e.g., what is being tested and how many tests cases are
+   passing)
+
+2. Add flags to Makefile:
+   + verbosity level
+   + enable memblock_dbg() messages (i.e. pass "-D CONFIG_DEBUG_MEMORY_INIT"
+     flag)
+
+3. Add tests trying to memblock_add() or memblock_reserve() 129th region.
+   This will trigger memblock_double_array(), make sure it succeeds.
+   *Important:* These tests require valid memory ranges, use dummy physical
+                memory block from common.c to implement them. It is also very
+                likely that the current MEM_SIZE won't be enough for these
+                test cases. Use realloc to adjust the size accordingly.
+
+4. Add test cases using this functions (implement them for both directions):
+   + memblock_alloc_raw()
+   + memblock_alloc_exact_nid_raw()
+   + memblock_alloc_try_nid_raw()
+
+5. Add tests for memblock_alloc_node() to check if the correct NUMA node is set
+   for the new region
+
+6. Update comments in tests/basic_api.c to match the style used in
+   tests/alloc_*.c
--
2.30.2



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 9/9] memblock tests: Add TODO and README files
  2022-02-28 14:46 ` [PATCH 9/9] memblock tests: Add TODO and README files Karolina Drobnik
@ 2022-03-07 18:13   ` Mike Rapoport
  0 siblings, 0 replies; 13+ messages in thread
From: Mike Rapoport @ 2022-03-07 18:13 UTC (permalink / raw)
  To: Karolina Drobnik; +Cc: linux-mm, akpm, linux-kernel

On Mon, Feb 28, 2022 at 03:46:51PM +0100, Karolina Drobnik wrote:
> Add description of the project, its structure and how to run it.
> List what is left to implement and what the known issues are.
> 
> Signed-off-by: Karolina Drobnik <karolinadrobnik@gmail.com>
> ---
>  tools/testing/memblock/README | 114 ++++++++++++++++++++++++++++++++++
>  tools/testing/memblock/TODO   |  28 +++++++++
>  2 files changed, 142 insertions(+)
>  create mode 100644 tools/testing/memblock/README
>  create mode 100644 tools/testing/memblock/TODO
> 
> diff --git a/tools/testing/memblock/README b/tools/testing/memblock/README
> new file mode 100644
> index 000000000000..40c0ce50e7c2
> --- /dev/null
> +++ b/tools/testing/memblock/README
> @@ -0,0 +1,114 @@
> +=========================
> +   Memblock simulator
> +=========================
> +
> +
> +Introduction
> +---------------------

Please make shorten the underline to match section title

> +Memblock is a boot time memory allocator[1] that manages memory regions before
> +the actual memory management is initialized. Its APIs allow to register physical
> +memory regions, mark them as available or reserved, allocate a block of memory
> +within the requested range and/or in specific NUMA node, and many more.
> +
> +Because it is used so early in the booting process, testing and debugging it is
> +difficult. This test suite, usually referred as memblock simulator, is
> +an attempt at testing the memblock mechanism. It runs one monolithic test that
> +consist of a series of checks that exercise both the basic operations and
> +allocation functionalities of memblock. The main data structure of the boot time
> +memory allocator is initialized at the build time, so the checks here reuse its
> +instance throughout the duration of the test. To ensure that tests don't affect
> +each other, region arrays are reset in between.
> +
> +As this project uses the actual memblock code and has to run in user space, some
> +of the kernel definitions were stubbed in the introductory patch[2]. Most of

The patch commit sha in memblock/for-next is stable, please use that.

> +them don't match the kernel implementation, so one should consult them first
> +before making any significant changes to the project.
> +
> +
> +Usage
> +---------------------
> +
> +To run the tests, build the main target and run it:
> +
> +$ make; ./main

I'd rather use

	make && ./make

> +
> +A successful run produces no output. It is also possible to override different
> +configuration parameters. For example, to simulate enabled NUMA, use:
> +
> +$ make NUMA=1
> +
> +For the full list of options, see `make help`.
> +
> +
> +Project structure
> +---------------------
> +
> +The project has one target, main, which calls a group of checks for basic and
> +allocation functions. Tests for each group are defined in dedicated files, as it
> +can be seen here:
> +
> +memblock
> +|-- asm       ------------------,
> +|-- lib                         |-- implement function and struct stubs
> +|-- linux     ------------------'
> +|-- scripts
> +|    |-- Makefile.include        -- handles `make` parameters
> +|-- tests
> +|    |-- alloc_api.(c|h)         -- memblock_alloc tests
> +|    |-- alloc_helpers_api.(c|h) -- memblock_alloc_from tests
> +|    |-- alloc_nid_api.(c|h)     -- memblock_alloc_try_nid tests
> +|    |-- basic_api.(c|h)         -- memblock_add/memblock_reserve/... tests
> +|    |-- common.(c|h)            -- helper functions for resetting memblock;
> +|-- main.c        --------------.   dummy physical memory definition
> +|-- Makefile                     `- test runner
> +|-- README
> +|-- TODO
> +|-- .gitignore
> +
> +
> +Simulating physical memory
> +--------------------------
> +
> +Some allocation functions clear the memory in the process, so it is required for
> +memblock to track valid memory ranges. To achieve this, the test suite registers
> +with memblock memory stored by test_memory struct. It is a small wrapper that
> +points to a block of memory allocated via malloc. For each group of allocation
> +tests, dummy physical memory is allocated, added to memblock, and then released
> +at the end of the test run. The structure of a test runner checking allocation
> +functions is as follows:
> +
> +int memblock_alloc_foo_checks(void)
> +{
> +	reset_memblock_attributes();     /* data structure reset */
> +	dummy_physical_memory_init();    /* allocate and register memory */
> +
> +	(...allocation checks...)
> +
> +	dummy_physical_memory_cleanup(); /* free the memory */
> +}
> +
> +There's no need to explicitly free the dummy memory from memblock via
> +memblock_free() call. The entry will be erased by reset_memblock_regions(),
> +called at the beginning of each test.
> +
> +
> +Known issues
> +---------------------
> +
> +1. Requesting a specific NUMA node via memblock_alloc_node() does not work as
> +   intended. Once the fix is in place, tests for this function can be added.
> +
> +2. Tests for memblock_alloc_low() can't be easily implemented. The function uses
> +   ARCH_LOW_ADDRESS_LIMIT marco, which can't be changed to point at the low
> +   memory of the memory_block.
> +
> +
> +References
> +---------------------
> +
> +1. Boot time memory management documentation page:
> +   https://www.kernel.org/doc/html/latest/core-api/boot-time-mm.html
> +
> +2. Introduce memblock simulator, lore link:
> +https://lore.kernel.org/linux-mm/cover.1643796665.git.karolinadrobnik@gmail.com/
> diff --git a/tools/testing/memblock/TODO b/tools/testing/memblock/TODO
> new file mode 100644
> index 000000000000..c25b2fdec45e
> --- /dev/null
> +++ b/tools/testing/memblock/TODO
> @@ -0,0 +1,28 @@
> +TODO
> +=====
> +
> +1. Add verbose output (e.g., what is being tested and how many tests cases are
> +   passing)
> +
> +2. Add flags to Makefile:
> +   + verbosity level
> +   + enable memblock_dbg() messages (i.e. pass "-D CONFIG_DEBUG_MEMORY_INIT"
> +     flag)
> +
> +3. Add tests trying to memblock_add() or memblock_reserve() 129th region.

s/129th/more than INIT_MEMBLOCK_REGIONS/

> +   This will trigger memblock_double_array(), make sure it succeeds.
> +   *Important:* These tests require valid memory ranges, use dummy physical
> +                memory block from common.c to implement them. It is also very
> +                likely that the current MEM_SIZE won't be enough for these
> +                test cases. Use realloc to adjust the size accordingly.
> +
> +4. Add test cases using this functions (implement them for both directions):
> +   + memblock_alloc_raw()
> +   + memblock_alloc_exact_nid_raw()
> +   + memblock_alloc_try_nid_raw()
> +
> +5. Add tests for memblock_alloc_node() to check if the correct NUMA node is set
> +   for the new region
> +
> +6. Update comments in tests/basic_api.c to match the style used in
> +   tests/alloc_*.c
> --
> 2.30.2
> 

-- 
Sincerely yours,
Mike.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/9] Add tests for memblock allocation functions
  2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
                   ` (8 preceding siblings ...)
  2022-02-28 14:46 ` [PATCH 9/9] memblock tests: Add TODO and README files Karolina Drobnik
@ 2022-03-07 18:17 ` Mike Rapoport
  2022-03-09 15:10   ` Mike Rapoport
  9 siblings, 1 reply; 13+ messages in thread
From: Mike Rapoport @ 2022-03-07 18:17 UTC (permalink / raw)
  To: Karolina Drobnik; +Cc: linux-mm, akpm, linux-kernel

Hi Karolina,

On Mon, Feb 28, 2022 at 03:46:42PM +0100, Karolina Drobnik wrote:
> Patches are on top of memblock/for-next.
> 
> This series adds test coverage for memblock allocation functions that return
> virtual addresses:
>   * memblock_alloc
>   * memblock_alloc_try_nid
>   * memblock_alloc_from
> 
> These tests are a part of memblock simulator, a user space test suite that uses
> the actual memblock code[1]. All the tests are run as one, monolithic test, that
> exercises both basic operations and allocation functions of memblock.
> 
> To simplify implementation of the memory allocation tests, reset_memblock() was
> split into two functions - reset_memblock_attributes() and
> reset_memblock_regions(). The former sets default values for region array names,
> allocation direction flag (bottom_up) and current memory limit. The latter only
> resets region arrays and corresponding counters.
> 
> In addition to these, the patch set introduces a data structure that simulates
> physical memory, test_memory. It is a simple wrapper around a pointer to the
> memory block allocated via malloc(), which is added to memblock as available
> memory. This is required because the tested functions interact with the memory
> before allocating it.
> 
> Patchset structure:
>   * Patch 1 splits reset_memblock() into two functions -
>     reset_memblock_regions() and reset_memblock_attributes(), and updates
>     basic API tests to use the new definitions
>   * Patch 2 adds test_memory struct and functions to add (or release) dummy
>     physical memory
>   * Patches 3 - 8 add test cases for memblock_alloc, memblock_alloc_try_nid and
>     memblock_alloc_from for both allocation directions (default top down and
>     bottom up)
>   * Patch 9 adds README and TODO files
 
Except for few minor comments about README and TODO these patches look good
to me.

The only think that I'd really like is to wrap the lines longer that 80
characters.

(scripts/checkpatch.pl --max-line-length=80 can help with that)

> [1] https://lore.kernel.org/linux-mm/cover.1643796665.git.karolinadrobnik@gmail.com/
> 
> Karolina Drobnik (9):
>   memblock tests: Split up reset_memblock function
>   memblock tests: Add simulation of physical memory
>   memblock tests: Add memblock_alloc tests for top down
>   memblock tests: Add memblock_alloc tests for bottom up
>   memblock tests: Add memblock_alloc_from tests for top down
>   memblock tests: Add memblock_alloc_from tests for bottom up
>   memblock tests: Add memblock_alloc_try_nid tests for top down
>   memblock tests: Add memblock_alloc_try_nid tests for bottom up
>   memblock tests: Add TODO and README files
> 
>  tools/testing/memblock/Makefile               |    3 +-
>  tools/testing/memblock/README                 |  114 ++
>  tools/testing/memblock/TODO                   |   28 +
>  tools/testing/memblock/main.c                 |    7 +
>  tools/testing/memblock/tests/alloc_api.c      |  742 +++++++++++
>  tools/testing/memblock/tests/alloc_api.h      |    9 +
>  .../memblock/tests/alloc_helpers_api.c        |  393 ++++++
>  .../memblock/tests/alloc_helpers_api.h        |    9 +
>  tools/testing/memblock/tests/alloc_nid_api.c  | 1167 +++++++++++++++++
>  tools/testing/memblock/tests/alloc_nid_api.h  |    9 +
>  tools/testing/memblock/tests/basic_api.c      |   49 +-
>  tools/testing/memblock/tests/basic_api.h      |    1 -
>  tools/testing/memblock/tests/common.c         |   33 +-
>  tools/testing/memblock/tests/common.h         |   21 +-
>  14 files changed, 2550 insertions(+), 35 deletions(-)
>  create mode 100644 tools/testing/memblock/README
>  create mode 100644 tools/testing/memblock/TODO
>  create mode 100644 tools/testing/memblock/tests/alloc_api.c
>  create mode 100644 tools/testing/memblock/tests/alloc_api.h
>  create mode 100644 tools/testing/memblock/tests/alloc_helpers_api.c
>  create mode 100644 tools/testing/memblock/tests/alloc_helpers_api.h
>  create mode 100644 tools/testing/memblock/tests/alloc_nid_api.c
>  create mode 100644 tools/testing/memblock/tests/alloc_nid_api.h
> 
> 
> base-commit: f30b002ccfee8c60c8feb590e145c0b5e8fa4c67
> --
> 2.30.2
> 

-- 
Sincerely yours,
Mike.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/9] Add tests for memblock allocation functions
  2022-03-07 18:17 ` [PATCH 0/9] Add tests for memblock allocation functions Mike Rapoport
@ 2022-03-09 15:10   ` Mike Rapoport
  0 siblings, 0 replies; 13+ messages in thread
From: Mike Rapoport @ 2022-03-09 15:10 UTC (permalink / raw)
  To: Karolina Drobnik; +Cc: linux-mm, akpm, linux-kernel

On Mon, Mar 07, 2022 at 08:17:39PM +0200, Mike Rapoport wrote:
> Hi Karolina,
> 
> On Mon, Feb 28, 2022 at 03:46:42PM +0100, Karolina Drobnik wrote:
> > Patches are on top of memblock/for-next.
> > 
> > This series adds test coverage for memblock allocation functions that return
> > virtual addresses:
> >   * memblock_alloc
> >   * memblock_alloc_try_nid
> >   * memblock_alloc_from
> > 
> > These tests are a part of memblock simulator, a user space test suite that uses
> > the actual memblock code[1]. All the tests are run as one, monolithic test, that
> > exercises both basic operations and allocation functions of memblock.
> > 
> > To simplify implementation of the memory allocation tests, reset_memblock() was
> > split into two functions - reset_memblock_attributes() and
> > reset_memblock_regions(). The former sets default values for region array names,
> > allocation direction flag (bottom_up) and current memory limit. The latter only
> > resets region arrays and corresponding counters.
> > 
> > In addition to these, the patch set introduces a data structure that simulates
> > physical memory, test_memory. It is a simple wrapper around a pointer to the
> > memory block allocated via malloc(), which is added to memblock as available
> > memory. This is required because the tested functions interact with the memory
> > before allocating it.
> > 
> > Patchset structure:
> >   * Patch 1 splits reset_memblock() into two functions -
> >     reset_memblock_regions() and reset_memblock_attributes(), and updates
> >     basic API tests to use the new definitions
> >   * Patch 2 adds test_memory struct and functions to add (or release) dummy
> >     physical memory
> >   * Patches 3 - 8 add test cases for memblock_alloc, memblock_alloc_try_nid and
> >     memblock_alloc_from for both allocation directions (default top down and
> >     bottom up)
> >   * Patch 9 adds README and TODO files
>  
> Except for few minor comments about README and TODO these patches look good
> to me.

I went ahead and applied this series with some formatting fixes.


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2022-03-09 15:10 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-28 14:46 [PATCH 0/9] Add tests for memblock allocation functions Karolina Drobnik
2022-02-28 14:46 ` [PATCH 1/9] memblock tests: Split up reset_memblock function Karolina Drobnik
2022-02-28 14:46 ` [PATCH 2/9] memblock tests: Add simulation of physical memory Karolina Drobnik
2022-02-28 14:46 ` [PATCH 3/9] memblock tests: Add memblock_alloc tests for top down Karolina Drobnik
2022-02-28 14:46 ` [PATCH 4/9] memblock tests: Add memblock_alloc tests for bottom up Karolina Drobnik
2022-02-28 14:46 ` [PATCH 5/9] memblock tests: Add memblock_alloc_from tests for top down Karolina Drobnik
2022-02-28 14:46 ` [PATCH 6/9] memblock tests: Add memblock_alloc_from tests for bottom up Karolina Drobnik
2022-02-28 14:46 ` [PATCH 7/9] memblock tests: Add memblock_alloc_try_nid tests for top down Karolina Drobnik
2022-02-28 14:46 ` [PATCH 8/9] memblock tests: Add memblock_alloc_try_nid tests for bottom up Karolina Drobnik
2022-02-28 14:46 ` [PATCH 9/9] memblock tests: Add TODO and README files Karolina Drobnik
2022-03-07 18:13   ` Mike Rapoport
2022-03-07 18:17 ` [PATCH 0/9] Add tests for memblock allocation functions Mike Rapoport
2022-03-09 15:10   ` Mike Rapoport

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).