From aa0eab8639ff0fb1edf18b8616e6ae2c38ca5854 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:00 +0100 Subject: tools: Move gfp.h and slab.h from radix-tree to lib Merge radix-tree definitions from gfp.h and slab.h with these in tools/lib, so they can be used in other test suites. Fix style issues in slab.h. Update radix-tree test files. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/b76ddb8a12fdf9870b55c1401213e44f5e0d0da3.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/gfp.h | 28 ++++++++++++++++++++++++++ tools/include/linux/slab.h | 28 ++++++++++++++++++++++++++ tools/lib/slab.c | 38 +++++++++++++++++++++++++++++++++++ tools/testing/radix-tree/Makefile | 3 ++- tools/testing/radix-tree/linux.c | 27 ------------------------- tools/testing/radix-tree/linux/gfp.h | 33 ------------------------------ tools/testing/radix-tree/linux/slab.h | 27 ------------------------- 7 files changed, 96 insertions(+), 88 deletions(-) create mode 100644 tools/include/linux/slab.h create mode 100644 tools/lib/slab.c delete mode 100644 tools/testing/radix-tree/linux/gfp.h delete mode 100644 tools/testing/radix-tree/linux/slab.h (limited to 'tools') diff --git a/tools/include/linux/gfp.h b/tools/include/linux/gfp.h index 22030756fbc0..b238dbc9eb85 100644 --- a/tools/include/linux/gfp.h +++ b/tools/include/linux/gfp.h @@ -1,4 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _TOOLS_INCLUDE_LINUX_GFP_H #define _TOOLS_INCLUDE_LINUX_GFP_H +#include + +#define __GFP_BITS_SHIFT 26 +#define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) + +#define __GFP_HIGH 0x20u +#define __GFP_IO 0x40u +#define __GFP_FS 0x80u +#define __GFP_NOWARN 0x200u +#define __GFP_ZERO 0x8000u +#define __GFP_ATOMIC 0x80000u +#define __GFP_ACCOUNT 0x100000u +#define __GFP_DIRECT_RECLAIM 0x400000u +#define __GFP_KSWAPD_RECLAIM 0x2000000u + +#define __GFP_RECLAIM (__GFP_DIRECT_RECLAIM | __GFP_KSWAPD_RECLAIM) + +#define GFP_ZONEMASK 0x0fu +#define GFP_ATOMIC (__GFP_HIGH | __GFP_ATOMIC | __GFP_KSWAPD_RECLAIM) +#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) +#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) + +static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags) +{ + return !!(gfp_flags & __GFP_DIRECT_RECLAIM); +} + #endif /* _TOOLS_INCLUDE_LINUX_GFP_H */ diff --git a/tools/include/linux/slab.h b/tools/include/linux/slab.h new file mode 100644 index 000000000000..07d7930d4003 --- /dev/null +++ b/tools/include/linux/slab.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_SLAB_H +#define _TOOLS_SLAB_H + +#include +#include + +#define SLAB_PANIC 2 +#define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ + +#define kzalloc_node(size, flags, node) kmalloc(size, flags) + +void *kmalloc(size_t size, gfp_t gfp); +void kfree(void *p); + +static inline void *kzalloc(size_t size, gfp_t gfp) +{ + return kmalloc(size, gfp | __GFP_ZERO); +} + +void *kmem_cache_alloc(struct kmem_cache *cachep, int flags); +void kmem_cache_free(struct kmem_cache *cachep, void *objp); + +struct kmem_cache *kmem_cache_create(const char *name, unsigned int size, + unsigned int align, unsigned int flags, + void (*ctor)(void *)); + +#endif /* _TOOLS_SLAB_H */ diff --git a/tools/lib/slab.c b/tools/lib/slab.c new file mode 100644 index 000000000000..959997fb0652 --- /dev/null +++ b/tools/lib/slab.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include +#include +#include +#include + +int kmalloc_nr_allocated; +int kmalloc_verbose; + +void *kmalloc(size_t size, gfp_t gfp) +{ + void *ret; + + if (!(gfp & __GFP_DIRECT_RECLAIM)) + return NULL; + + ret = malloc(size); + uatomic_inc(&kmalloc_nr_allocated); + if (kmalloc_verbose) + printf("Allocating %p from malloc\n", ret); + if (gfp & __GFP_ZERO) + memset(ret, 0, size); + return ret; +} + +void kfree(void *p) +{ + if (!p) + return; + uatomic_dec(&kmalloc_nr_allocated); + if (kmalloc_verbose) + printf("Freeing %p to malloc\n", p); + free(p); +} diff --git a/tools/testing/radix-tree/Makefile b/tools/testing/radix-tree/Makefile index aa6abfe0749c..c4ea4fbb0bfc 100644 --- a/tools/testing/radix-tree/Makefile +++ b/tools/testing/radix-tree/Makefile @@ -5,7 +5,8 @@ CFLAGS += -I. -I../../include -g -Og -Wall -D_LGPL_SOURCE -fsanitize=address \ LDFLAGS += -fsanitize=address -fsanitize=undefined LDLIBS+= -lpthread -lurcu TARGETS = main idr-test multiorder xarray -CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o find_bit.o bitmap.o +CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o find_bit.o bitmap.o \ + slab.o OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \ regression4.o tag_check.o multiorder.o idr-test.o iteration_check.o \ iteration_check_2.o benchmark.o diff --git a/tools/testing/radix-tree/linux.c b/tools/testing/radix-tree/linux.c index 2d9c59df60de..81539f543954 100644 --- a/tools/testing/radix-tree/linux.c +++ b/tools/testing/radix-tree/linux.c @@ -14,7 +14,6 @@ int nr_allocated; int preempt_count; -int kmalloc_verbose; int test_verbose; struct kmem_cache { @@ -78,32 +77,6 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) pthread_mutex_unlock(&cachep->lock); } -void *kmalloc(size_t size, gfp_t gfp) -{ - void *ret; - - if (!(gfp & __GFP_DIRECT_RECLAIM)) - return NULL; - - ret = malloc(size); - uatomic_inc(&nr_allocated); - if (kmalloc_verbose) - printf("Allocating %p from malloc\n", ret); - if (gfp & __GFP_ZERO) - memset(ret, 0, size); - return ret; -} - -void kfree(void *p) -{ - if (!p) - return; - uatomic_dec(&nr_allocated); - if (kmalloc_verbose) - printf("Freeing %p to malloc\n", p); - free(p); -} - struct kmem_cache * kmem_cache_create(const char *name, unsigned int size, unsigned int align, unsigned int flags, void (*ctor)(void *)) diff --git a/tools/testing/radix-tree/linux/gfp.h b/tools/testing/radix-tree/linux/gfp.h deleted file mode 100644 index 32159c08a52e..000000000000 --- a/tools/testing/radix-tree/linux/gfp.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _GFP_H -#define _GFP_H - -#include - -#define __GFP_BITS_SHIFT 26 -#define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) - -#define __GFP_HIGH 0x20u -#define __GFP_IO 0x40u -#define __GFP_FS 0x80u -#define __GFP_NOWARN 0x200u -#define __GFP_ZERO 0x8000u -#define __GFP_ATOMIC 0x80000u -#define __GFP_ACCOUNT 0x100000u -#define __GFP_DIRECT_RECLAIM 0x400000u -#define __GFP_KSWAPD_RECLAIM 0x2000000u - -#define __GFP_RECLAIM (__GFP_DIRECT_RECLAIM|__GFP_KSWAPD_RECLAIM) - -#define GFP_ZONEMASK 0x0fu -#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) -#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) -#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) - - -static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags) -{ - return !!(gfp_flags & __GFP_DIRECT_RECLAIM); -} - -#endif diff --git a/tools/testing/radix-tree/linux/slab.h b/tools/testing/radix-tree/linux/slab.h deleted file mode 100644 index 2958830ce4d7..000000000000 --- a/tools/testing/radix-tree/linux/slab.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef SLAB_H -#define SLAB_H - -#include -#include - -#define SLAB_HWCACHE_ALIGN 1 -#define SLAB_PANIC 2 -#define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ - -void *kmalloc(size_t size, gfp_t); -void kfree(void *); - -static inline void *kzalloc(size_t size, gfp_t gfp) -{ - return kmalloc(size, gfp | __GFP_ZERO); -} - -void *kmem_cache_alloc(struct kmem_cache *cachep, int flags); -void kmem_cache_free(struct kmem_cache *cachep, void *objp); - -struct kmem_cache *kmem_cache_create(const char *name, unsigned int size, - unsigned int align, unsigned int flags, - void (*ctor)(void *)); - -#endif /* SLAB_H */ -- cgit v1.2.3-70-g09d2 From 884ee1e585ca01b47f66c7e5b6902c62d1af77a3 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:01 +0100 Subject: tools/include: Add phys_addr_t to types.h Update types.h file to include phys_addr_t typedef so it can be used in testing. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/f0b42be7d1fe2eb9cd299532676d9df2df9ef089.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/types.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools') diff --git a/tools/include/linux/types.h b/tools/include/linux/types.h index 6e14a533ab4e..35cedaf191e8 100644 --- a/tools/include/linux/types.h +++ b/tools/include/linux/types.h @@ -64,6 +64,12 @@ typedef __u64 __bitwise __be64; typedef __u16 __bitwise __sum16; typedef __u32 __bitwise __wsum; +#ifdef CONFIG_PHYS_ADDR_T_64BIT +typedef u64 phys_addr_t; +#else +typedef u32 phys_addr_t; +#endif + typedef struct { int counter; } atomic_t; -- cgit v1.2.3-70-g09d2 From 5cf67a6051ea2558fd7c3d39c5a808db73073e9d Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:02 +0100 Subject: tools/include: Add _RET_IP_ and math definitions to kernel.h Add max_t, min_t and clamp functions, together with _RET_IP_ definition, so they can be used in testing. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/230fea382cb1e1659cdd52a55201854d38a0a149.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/kernel.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools') diff --git a/tools/include/linux/kernel.h b/tools/include/linux/kernel.h index 9701e8307db0..4b0673bf52c2 100644 --- a/tools/include/linux/kernel.h +++ b/tools/include/linux/kernel.h @@ -15,6 +15,8 @@ #define UINT_MAX (~0U) #endif +#define _RET_IP_ ((unsigned long)__builtin_return_address(0)) + #define PERF_ALIGN(x, a) __PERF_ALIGN_MASK(x, (typeof(x))(a)-1) #define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) @@ -51,6 +53,10 @@ _min1 < _min2 ? _min1 : _min2; }) #endif +#define max_t(type, x, y) max((type)x, (type)y) +#define min_t(type, x, y) min((type)x, (type)y) +#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi) + #ifndef BUG_ON #ifdef NDEBUG #define BUG_ON(cond) do { if (cond) {} } while (0) -- cgit v1.2.3-70-g09d2 From 9c07af207c65e6fa8063d1105356568d8b03c536 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:03 +0100 Subject: tools/include: Update atomic definitions Add atomic_long_set function to atomic.h and atomic_long_t type to types.h so they can be used in testing. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/082fde69debc36bfc56cdb413d847dcd6b1e36dd.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/atomic.h | 2 ++ tools/include/linux/types.h | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'tools') diff --git a/tools/include/linux/atomic.h b/tools/include/linux/atomic.h index 00a6c4ca562b..01907b33537e 100644 --- a/tools/include/linux/atomic.h +++ b/tools/include/linux/atomic.h @@ -4,6 +4,8 @@ #include +void atomic_long_set(atomic_long_t *v, long i); + /* atomic_cmpxchg_relaxed */ #ifndef atomic_cmpxchg_relaxed #define atomic_cmpxchg_relaxed atomic_cmpxchg diff --git a/tools/include/linux/types.h b/tools/include/linux/types.h index 35cedaf191e8..5908e58c3598 100644 --- a/tools/include/linux/types.h +++ b/tools/include/linux/types.h @@ -74,6 +74,10 @@ typedef struct { int counter; } atomic_t; +typedef struct { + long counter; +} atomic_long_t; + #ifndef __aligned_u64 # define __aligned_u64 __u64 __attribute__((aligned(8))) #endif -- cgit v1.2.3-70-g09d2 From 2473bc35ba554003cdda8a3a0124f35433590fe7 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:04 +0100 Subject: tools/include: Add mm.h file Add a stubbed mm.h file with dummy page-related definitions, memory alignment and physical to virtual address conversions, so they can be used in testing. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/e8b83d96fec95f3556e80f001d9d5cbe18b8ad5f.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/mm.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tools/include/linux/mm.h (limited to 'tools') diff --git a/tools/include/linux/mm.h b/tools/include/linux/mm.h new file mode 100644 index 000000000000..a03d9bba5151 --- /dev/null +++ b/tools/include/linux/mm.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_LINUX_MM_H +#define _TOOLS_LINUX_MM_H + +#include +#include + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +#define PHYS_ADDR_MAX (~(phys_addr_t)0) + +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) +#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a)) + +#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) + +#define __va(x) ((void *)((unsigned long)(x))) +#define __pa(x) ((unsigned long)(x)) + +#define pfn_to_page(pfn) ((void *)((pfn) * PAGE_SIZE)) + +#define phys_to_virt phys_to_virt +static inline void *phys_to_virt(unsigned long address) +{ + return __va(address); +} + +void reserve_bootmem_region(phys_addr_t start, phys_addr_t end); + +static inline void totalram_pages_inc(void) +{ +} + +static inline void totalram_pages_add(long count) +{ +} + +#endif -- cgit v1.2.3-70-g09d2 From 1fef073b71c3f22e2328872324fdb187f4fb7d7d Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:05 +0100 Subject: tools/include: Add cache.h stub Add a dummy version of the cache header. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/5e02865094aaf56dd30772722799e53f4130ebc8.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/cache.h | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tools/include/linux/cache.h (limited to 'tools') diff --git a/tools/include/linux/cache.h b/tools/include/linux/cache.h new file mode 100644 index 000000000000..9e9d585f0b9d --- /dev/null +++ b/tools/include/linux/cache.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_LINUX_CACHE_H +#define _TOOLS_LINUX_CACHE_H + +#define L1_CACHE_SHIFT 5 +#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) + +#define SMP_CACHE_BYTES L1_CACHE_BYTES + +#endif -- cgit v1.2.3-70-g09d2 From 93f4e871e7e0fd0269e27f5122a70a39d8105924 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:06 +0100 Subject: tools/include: Add io.h stub Add a dummy io.h header. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/3e8d42cbd01382d956d393cf3bb2a6d639dfdd97.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/io.h | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tools/include/linux/io.h (limited to 'tools') diff --git a/tools/include/linux/io.h b/tools/include/linux/io.h new file mode 100644 index 000000000000..e129871fe661 --- /dev/null +++ b/tools/include/linux/io.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_IO_H +#define _TOOLS_IO_H + +#endif -- cgit v1.2.3-70-g09d2 From a2e3fe5f24f90e2bd98fa8bfabbf55962080b7b4 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:07 +0100 Subject: tools/include: Add pfn.h stub Add a stubbed pfn header with definitions used in testing. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/1bdc02125963f945bf90dd8cb37c404cc0688af2.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/pfn.h | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tools/include/linux/pfn.h (limited to 'tools') diff --git a/tools/include/linux/pfn.h b/tools/include/linux/pfn.h new file mode 100644 index 000000000000..7512a58189eb --- /dev/null +++ b/tools/include/linux/pfn.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_LINUX_PFN_H_ +#define _TOOLS_LINUX_PFN_H_ + +#include + +#define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((phys_addr_t)(x) << PAGE_SHIFT) +#endif -- cgit v1.2.3-70-g09d2 From 62183279ad2f52d36b40365eb3a27c7a4a2284f8 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:08 +0100 Subject: tools/include: Add debugfs.h stub Add a dummy debugfs.h header. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/80fceff27934094873cc3f0656d22e802dfcce78.1643796665.git.karolinadrobnik@gmail.com --- tools/include/linux/debugfs.h | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tools/include/linux/debugfs.h (limited to 'tools') diff --git a/tools/include/linux/debugfs.h b/tools/include/linux/debugfs.h new file mode 100644 index 000000000000..4ba06140b1be --- /dev/null +++ b/tools/include/linux/debugfs.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_DEBUGFS_H +#define _TOOLS_DEBUGFS_H + +#endif -- cgit v1.2.3-70-g09d2 From 16802e55dea9534c18a30bd8eeefea8a06337916 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:09 +0100 Subject: memblock tests: Add skeleton of the memblock simulator Add basic project files, together with local stubs of required headers. Update tools/include/slab.h to include definitions used by memblock. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/d296fceb023a04b316a31fbff9acf1e76ac684e4.1643796665.git.karolinadrobnik@gmail.com --- MAINTAINERS | 1 + tools/include/linux/slab.h | 10 +++++ tools/testing/memblock/.gitignore | 4 ++ tools/testing/memblock/Makefile | 52 +++++++++++++++++++++++++ tools/testing/memblock/asm/dma.h | 5 +++ tools/testing/memblock/internal.h | 12 ++++++ tools/testing/memblock/lib/slab.c | 9 +++++ tools/testing/memblock/linux/init.h | 34 ++++++++++++++++ tools/testing/memblock/linux/kernel.h | 12 ++++++ tools/testing/memblock/linux/kmemleak.h | 18 +++++++++ tools/testing/memblock/linux/memory_hotplug.h | 19 +++++++++ tools/testing/memblock/linux/mmzone.h | 35 +++++++++++++++++ tools/testing/memblock/linux/printk.h | 25 ++++++++++++ tools/testing/memblock/main.c | 6 +++ tools/testing/memblock/mmzone.c | 20 ++++++++++ tools/testing/memblock/scripts/Makefile.include | 17 ++++++++ 16 files changed, 279 insertions(+) create mode 100644 tools/testing/memblock/.gitignore create mode 100644 tools/testing/memblock/Makefile create mode 100644 tools/testing/memblock/asm/dma.h create mode 100644 tools/testing/memblock/internal.h create mode 100644 tools/testing/memblock/lib/slab.c create mode 100644 tools/testing/memblock/linux/init.h create mode 100644 tools/testing/memblock/linux/kernel.h create mode 100644 tools/testing/memblock/linux/kmemleak.h create mode 100644 tools/testing/memblock/linux/memory_hotplug.h create mode 100644 tools/testing/memblock/linux/mmzone.h create mode 100644 tools/testing/memblock/linux/printk.h create mode 100644 tools/testing/memblock/main.c create mode 100644 tools/testing/memblock/mmzone.c create mode 100644 tools/testing/memblock/scripts/Makefile.include (limited to 'tools') diff --git a/MAINTAINERS b/MAINTAINERS index fca970a46e77..38502bf0231a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12423,6 +12423,7 @@ S: Maintained F: Documentation/core-api/boot-time-mm.rst F: include/linux/memblock.h F: mm/memblock.c +F: tools/testing/memblock/ MEMORY CONTROLLER DRIVERS M: Krzysztof Kozlowski diff --git a/tools/include/linux/slab.h b/tools/include/linux/slab.h index 07d7930d4003..f41d8a0eb1a4 100644 --- a/tools/include/linux/slab.h +++ b/tools/include/linux/slab.h @@ -13,6 +13,16 @@ void *kmalloc(size_t size, gfp_t gfp); void kfree(void *p); +bool slab_is_available(void); + +enum slab_state { + DOWN, + PARTIAL, + PARTIAL_NODE, + UP, + FULL +}; + static inline void *kzalloc(size_t size, gfp_t gfp) { return kmalloc(size, gfp | __GFP_ZERO); diff --git a/tools/testing/memblock/.gitignore b/tools/testing/memblock/.gitignore new file mode 100644 index 000000000000..654338e0be52 --- /dev/null +++ b/tools/testing/memblock/.gitignore @@ -0,0 +1,4 @@ +main +memblock.c +linux/memblock.h +asm/cmpxchg.h diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile new file mode 100644 index 000000000000..e43ed9de9bcf --- /dev/null +++ b/tools/testing/memblock/Makefile @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Memblock simulator requires AddressSanitizer (libasan) and liburcu development +# packages installed +CFLAGS += -I. -I../../include -Wall -O2 -fsanitize=address \ + -fsanitize=undefined -D CONFIG_PHYS_ADDR_T_64BIT +LDFLAGS += -fsanitize=address -fsanitize=undefined +TARGETS = main +OFILES = main.o memblock.o lib/slab.o mmzone.o slab.o +EXTR_SRC = ../../../mm/memblock.c + +ifeq ($(BUILD), 32) + CFLAGS += -m32 + LDFLAGS += -m32 +endif + +# Process user parameters +include scripts/Makefile.include + +main: $(OFILES) + +$(OFILES): include + +include: ../../../include/linux/memblock.h ../../include/linux/*.h \ + ../../include/asm/*.h + + @mkdir -p linux + test -L linux/memblock.h || ln -s ../../../../include/linux/memblock.h linux/memblock.h + test -L asm/cmpxchg.h || ln -s ../../../arch/x86/include/asm/cmpxchg.h asm/cmpxchg.h + +memblock.c: $(EXTR_SRC) + test -L memblock.c || ln -s $(EXTR_SRC) memblock.c + +clean: + $(RM) $(TARGETS) $(OFILES) linux/memblock.h memblock.c asm/cmpxchg.h + +help: + @echo 'Memblock simulator' + @echo '' + @echo 'Available targets:' + @echo ' main - Build the memblock simulator' + @echo ' clean - Remove generated files and symlinks in the directory' + @echo '' + @echo 'Configuration:' + @echo ' make NUMA=1 - simulate enabled NUMA' + @echo ' make MOVABLE_NODE=1 - override `movable_node_is_enabled`' + @echo ' definition to simulate movable NUMA nodes' + @echo ' make 32BIT_PHYS_ADDR_T=1 - Use 32 bit physical addresses' + +vpath %.c ../../lib + +.PHONY: clean include help diff --git a/tools/testing/memblock/asm/dma.h b/tools/testing/memblock/asm/dma.h new file mode 100644 index 000000000000..13ff8e5d22ef --- /dev/null +++ b/tools/testing/memblock/asm/dma.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_DMA_H +#define _TOOLS_DMA_H + +#endif diff --git a/tools/testing/memblock/internal.h b/tools/testing/memblock/internal.h new file mode 100644 index 000000000000..94b52a8718b5 --- /dev/null +++ b/tools/testing/memblock/internal.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _MM_INTERNAL_H +#define _MM_INTERNAL_H + +struct page {}; + +void memblock_free_pages(struct page *page, unsigned long pfn, + unsigned int order) +{ +} + +#endif diff --git a/tools/testing/memblock/lib/slab.c b/tools/testing/memblock/lib/slab.c new file mode 100644 index 000000000000..6be6020328fb --- /dev/null +++ b/tools/testing/memblock/lib/slab.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +enum slab_state slab_state; + +bool slab_is_available(void) +{ + return slab_state >= UP; +} diff --git a/tools/testing/memblock/linux/init.h b/tools/testing/memblock/linux/init.h new file mode 100644 index 000000000000..828e0ee0bc6c --- /dev/null +++ b/tools/testing/memblock/linux/init.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_INIT_H +#define _LINUX_INIT_H + +#include +#include +#include + +#define __section(section) __attribute__((__section__(section))) + +#define __initconst +#define __meminit +#define __meminitdata +#define __refdata +#define __initdata + +struct obs_kernel_param { + const char *str; + int (*setup_func)(char *st); + int early; +}; + +#define __setup_param(str, unique_id, fn, early) \ + static const char __setup_str_##unique_id[] __initconst \ + __aligned(1) = str; \ + static struct obs_kernel_param __setup_##unique_id \ + __used __section(".init.setup") \ + __aligned(__alignof__(struct obs_kernel_param)) = \ + { __setup_str_##unique_id, fn, early } + +#define early_param(str, fn) \ + __setup_param(str, fn, fn, 1) + +#endif diff --git a/tools/testing/memblock/linux/kernel.h b/tools/testing/memblock/linux/kernel.h new file mode 100644 index 000000000000..d2f148bd8902 --- /dev/null +++ b/tools/testing/memblock/linux/kernel.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _MEMBLOCK_LINUX_KERNEL_H +#define _MEMBLOCK_LINUX_KERNEL_H + +#include <../../include/linux/kernel.h> +#include +#include +#include +#include +#include + +#endif diff --git a/tools/testing/memblock/linux/kmemleak.h b/tools/testing/memblock/linux/kmemleak.h new file mode 100644 index 000000000000..462f8c5e8aa0 --- /dev/null +++ b/tools/testing/memblock/linux/kmemleak.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _KMEMLEAK_H +#define _KMEMLEAK_H + +static inline void kmemleak_free_part_phys(phys_addr_t phys, size_t size) +{ +} + +static inline void kmemleak_alloc_phys(phys_addr_t phys, size_t size, + int min_count, gfp_t gfp) +{ +} + +static inline void dump_stack(void) +{ +} + +#endif diff --git a/tools/testing/memblock/linux/memory_hotplug.h b/tools/testing/memblock/linux/memory_hotplug.h new file mode 100644 index 000000000000..47988765a219 --- /dev/null +++ b/tools/testing/memblock/linux/memory_hotplug.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_MEMORY_HOTPLUG_H +#define _LINUX_MEMORY_HOTPLUG_H + +#include +#include +#include +#include + +static inline bool movable_node_is_enabled(void) +{ +#ifdef MOVABLE_NODE + return true; +#else + return false; +#endif +} + +#endif diff --git a/tools/testing/memblock/linux/mmzone.h b/tools/testing/memblock/linux/mmzone.h new file mode 100644 index 000000000000..7c2eb5c9bb54 --- /dev/null +++ b/tools/testing/memblock/linux/mmzone.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TOOLS_MMZONE_H +#define _TOOLS_MMZONE_H + +#include + +struct pglist_data *first_online_pgdat(void); +struct pglist_data *next_online_pgdat(struct pglist_data *pgdat); + +#define for_each_online_pgdat(pgdat) \ + for (pgdat = first_online_pgdat(); \ + pgdat; \ + pgdat = next_online_pgdat(pgdat)) + +enum zone_type { + __MAX_NR_ZONES +}; + +#define MAX_NR_ZONES __MAX_NR_ZONES +#define MAX_ORDER 11 +#define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1)) + +#define pageblock_order (MAX_ORDER - 1) +#define pageblock_nr_pages BIT(pageblock_order) + +struct zone { + atomic_long_t managed_pages; +}; + +typedef struct pglist_data { + struct zone node_zones[MAX_NR_ZONES]; + +} pg_data_t; + +#endif diff --git a/tools/testing/memblock/linux/printk.h b/tools/testing/memblock/linux/printk.h new file mode 100644 index 000000000000..61af424d8c6c --- /dev/null +++ b/tools/testing/memblock/linux/printk.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PRINTK_H +#define _PRINTK_H + +#include +#include + +/* + * memblock_dbg is called with u64 arguments that don't match the "%llu" + * specifier in printf. This results in warnings that cannot be fixed without + * modifying memblock.c, which we wish to avoid. As these messaged are not used + * in testing anyway, the mismatch can be ignored. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#define printk printf +#pragma GCC diagnostic push + +#define pr_info printk +#define pr_debug printk +#define pr_cont printk +#define pr_err printk +#define pr_warn printk + +#endif diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c new file mode 100644 index 000000000000..62958da35d0f --- /dev/null +++ b/tools/testing/memblock/main.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/tools/testing/memblock/mmzone.c b/tools/testing/memblock/mmzone.c new file mode 100644 index 000000000000..7b0909e8b759 --- /dev/null +++ b/tools/testing/memblock/mmzone.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include + +struct pglist_data *first_online_pgdat(void) +{ + return NULL; +} + +struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) +{ + return NULL; +} + +void reserve_bootmem_region(phys_addr_t start, phys_addr_t end) +{ +} + +void atomic_long_set(atomic_long_t *v, long i) +{ +} diff --git a/tools/testing/memblock/scripts/Makefile.include b/tools/testing/memblock/scripts/Makefile.include new file mode 100644 index 000000000000..699b0d6cda07 --- /dev/null +++ b/tools/testing/memblock/scripts/Makefile.include @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# Definitions for user-provided arguments + +# Simulate CONFIG_NUMA=y +ifeq ($(NUMA), 1) + CFLAGS += -D CONFIG_NUMA +endif + +# Simulate movable NUMA memory regions +ifeq ($(MOVABLE_NODE), 1) + CFLAGS += -D MOVABLE_NODE +endif + +# Use 32 bit physical addresses +ifeq ($(32BIT_PHYS_ADDR_T), 1) + CFLAGS += -U CONFIG_PHYS_ADDR_T_64BIT +endif -- cgit v1.2.3-70-g09d2 From f3252a22d1f59d89cca769431efa1c95d6343929 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:10 +0100 Subject: memblock tests: Add memblock reset function Memblock simulator needs to be able to reset memblock data structures between different test cases. Add a function that sets all fields to their default values. Add a test checking if memblock is being initialized to expected values. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/8c185aa7e0dd68c2c7e937c9a06c90ae413e240f.1643796665.git.karolinadrobnik@gmail.com --- tools/testing/memblock/Makefile | 4 +++- tools/testing/memblock/main.c | 2 ++ tools/testing/memblock/tests/basic_api.c | 32 ++++++++++++++++++++++++++++++++ tools/testing/memblock/tests/basic_api.h | 10 ++++++++++ tools/testing/memblock/tests/common.c | 27 +++++++++++++++++++++++++++ tools/testing/memblock/tests/common.h | 15 +++++++++++++++ 6 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tools/testing/memblock/tests/basic_api.c create mode 100644 tools/testing/memblock/tests/basic_api.h create mode 100644 tools/testing/memblock/tests/common.c create mode 100644 tools/testing/memblock/tests/common.h (limited to 'tools') diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile index e43ed9de9bcf..29715327a2d3 100644 --- a/tools/testing/memblock/Makefile +++ b/tools/testing/memblock/Makefile @@ -6,7 +6,9 @@ CFLAGS += -I. -I../../include -Wall -O2 -fsanitize=address \ -fsanitize=undefined -D CONFIG_PHYS_ADDR_T_64BIT LDFLAGS += -fsanitize=address -fsanitize=undefined TARGETS = main -OFILES = main.o memblock.o lib/slab.o mmzone.o slab.o +TEST_OFILES = 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 ifeq ($(BUILD), 32) diff --git a/tools/testing/memblock/main.c b/tools/testing/memblock/main.c index 62958da35d0f..da65b0adee91 100644 --- a/tools/testing/memblock/main.c +++ b/tools/testing/memblock/main.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include "tests/basic_api.h" int main(int argc, char **argv) { + memblock_basic_checks(); return 0; } diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c new file mode 100644 index 000000000000..7f2597b3dd4d --- /dev/null +++ b/tools/testing/memblock/tests/basic_api.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include "basic_api.h" + +#define EXPECTED_MEMBLOCK_REGIONS 128 + +static int memblock_initialization_check(void) +{ + reset_memblock(); + + assert(memblock.memory.regions); + assert(memblock.memory.cnt == 1); + assert(memblock.memory.max == EXPECTED_MEMBLOCK_REGIONS); + assert(strcmp(memblock.memory.name, "memory") == 0); + + assert(memblock.reserved.regions); + assert(memblock.reserved.cnt == 1); + assert(memblock.memory.max == EXPECTED_MEMBLOCK_REGIONS); + assert(strcmp(memblock.reserved.name, "reserved") == 0); + + assert(!memblock.bottom_up); + assert(memblock.current_limit == MEMBLOCK_ALLOC_ANYWHERE); + + return 0; +} + +int memblock_basic_checks(void) +{ + memblock_initialization_check(); + return 0; +} diff --git a/tools/testing/memblock/tests/basic_api.h b/tools/testing/memblock/tests/basic_api.h new file mode 100644 index 000000000000..1ceecfca1f47 --- /dev/null +++ b/tools/testing/memblock/tests/basic_api.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _MEMBLOCK_BASIC_H +#define _MEMBLOCK_BASIC_H + +#include +#include "common.h" + +int memblock_basic_checks(void); + +#endif diff --git a/tools/testing/memblock/tests/common.c b/tools/testing/memblock/tests/common.c new file mode 100644 index 000000000000..03de6eab0c3c --- /dev/null +++ b/tools/testing/memblock/tests/common.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "tests/common.h" +#include + +#define INIT_MEMBLOCK_REGIONS 128 +#define INIT_MEMBLOCK_RESERVED_REGIONS INIT_MEMBLOCK_REGIONS + +void reset_memblock(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; + + memblock.reserved.cnt = 1; + memblock.reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS; + memblock.reserved.name = "reserved"; + memblock.reserved.total_size = 0; + + 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 new file mode 100644 index 000000000000..48efc4270ea1 --- /dev/null +++ b/tools/testing/memblock/tests/common.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _MEMBLOCK_TEST_H +#define _MEMBLOCK_TEST_H + +#include +#include + +struct region { + phys_addr_t base; + phys_addr_t size; +}; + +void reset_memblock(void); + +#endif -- cgit v1.2.3-70-g09d2 From 1f1180d46d21506325b7217da5a546235a2263a2 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:11 +0100 Subject: memblock tests: Add memblock_add tests Add checks for adding a new region in different scenarios: - The region does not overlap with existing entries - The region overlaps with one of the previous entries: from the top (its end address is bigger than the base of the existing region) or from the bottom (its base address is smaller than the end address of one of the regions) - The region is within an already defined region - The same region is added twice to the collection of available memory regions Add checks for memblock initialization to verify it sets memblock data structures to expected values. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/b6c26525025bccec0bf7419473d4d1293eb82b3b.1643796665.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/basic_api.c | 215 +++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) (limited to 'tools') diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 7f2597b3dd4d..01dd64b9a41b 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include #include +#include #include "basic_api.h" #define EXPECTED_MEMBLOCK_REGIONS 128 @@ -25,8 +26,222 @@ static int memblock_initialization_check(void) return 0; } +/* + * A simple test that adds a memory block of a specified base address + * and size to the collection of available memory regions (memblock.memory). + * It checks if a new entry was created and if region counter and total memory + * were correctly updated. + */ +static int memblock_add_simple_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.memory.regions[0]; + + struct region r = { + .base = SZ_1G, + .size = SZ_4M + }; + + reset_memblock(); + memblock_add(r.base, r.size); + + assert(rgn->base == r.base); + assert(rgn->size == r.size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == r.size); + + return 0; +} + +/* + * A test that tries to add two memory blocks that don't overlap with one + * another. It checks if two correctly initialized entries were added to the + * collection of available memory regions (memblock.memory) and if this + * change was reflected in memblock.memory's total size and region counter. + */ +static int memblock_add_disjoint_check(void) +{ + struct memblock_region *rgn1, *rgn2; + + rgn1 = &memblock.memory.regions[0]; + rgn2 = &memblock.memory.regions[1]; + + struct region r1 = { + .base = SZ_1G, + .size = SZ_8K + }; + struct region r2 = { + .base = SZ_1G + SZ_16K, + .size = SZ_8K + }; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_add(r2.base, r2.size); + + assert(rgn1->base == r1.base); + assert(rgn1->size == r1.size); + + assert(rgn2->base == r2.base); + assert(rgn2->size == r2.size); + + assert(memblock.memory.cnt == 2); + assert(memblock.memory.total_size == r1.size + r2.size); + + return 0; +} + +/* + * A test that tries to add two memory blocks, where the second one overlaps + * with the beginning of the first entry (that is r1.base < r2.base + r2.size). + * After this, it checks if two entries are merged into one region that starts + * at r2.base and has size of two regions minus their intersection. It also + * verifies the reported total size of the available memory and region counter. + */ +static int memblock_add_overlap_top_check(void) +{ + struct memblock_region *rgn; + phys_addr_t total_size; + + rgn = &memblock.memory.regions[0]; + + struct region r1 = { + .base = SZ_512M, + .size = SZ_1G + }; + struct region r2 = { + .base = SZ_256M, + .size = SZ_512M + }; + + total_size = (r1.base - r2.base) + r1.size; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_add(r2.base, r2.size); + + assert(rgn->base == r2.base); + assert(rgn->size == total_size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == total_size); + + return 0; +} + +/* + * A test that tries to add two memory blocks, where the second one overlaps + * with the end of the first entry (that is r2.base < r1.base + r1.size). + * After this, it checks if two entries are merged into one region that starts + * at r1.base and has size of two regions minus their intersection. It verifies + * that memblock can still see only one entry and has a correct total size of + * the available memory. + */ +static int memblock_add_overlap_bottom_check(void) +{ + struct memblock_region *rgn; + phys_addr_t total_size; + + rgn = &memblock.memory.regions[0]; + + struct region r1 = { + .base = SZ_128M, + .size = SZ_512M + }; + struct region r2 = { + .base = SZ_256M, + .size = SZ_1G + }; + + total_size = (r2.base - r1.base) + r2.size; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_add(r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == total_size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == total_size); + + return 0; +} + +/* + * A test that tries to add two memory blocks, where the second one is + * within the range of the first entry (that is r1.base < r2.base && + * r2.base + r2.size < r1.base + r1.size). It checks if two entries are merged + * into one region that stays the same. The counter and total size of available + * memory are expected to not be updated. + */ +static int memblock_add_within_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.memory.regions[0]; + + struct region r1 = { + .base = SZ_8M, + .size = SZ_32M + }; + struct region r2 = { + .base = SZ_16M, + .size = SZ_1M + }; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_add(r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == r1.size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == r1.size); + + return 0; +} + +/* + * A simple test that tries to add the same memory block twice. The counter + * and total size of available memory are expected to not be updated. + */ +static int memblock_add_twice_check(void) +{ + struct region r = { + .base = SZ_16K, + .size = SZ_2M + }; + + reset_memblock(); + + memblock_add(r.base, r.size); + memblock_add(r.base, r.size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == r.size); + + return 0; +} + +static int memblock_add_checks(void) +{ + memblock_add_simple_check(); + memblock_add_disjoint_check(); + memblock_add_overlap_top_check(); + memblock_add_overlap_bottom_check(); + memblock_add_within_check(); + memblock_add_twice_check(); + + return 0; +} + int memblock_basic_checks(void) { memblock_initialization_check(); + memblock_add_checks(); return 0; } -- cgit v1.2.3-70-g09d2 From 83787a803793881a2e6cf47ea0706cc8fe4573d7 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:12 +0100 Subject: memblock tests: Add memblock_reserve tests Add checks for marking a region as reserved in different scenarios: - The region does not overlap with existing entries - The region overlaps with one of the previous entries: from the top (its end address is bigger than the base of the existing region) or from the bottom (its base address is smaller than the end address of one of the regions) - The region is within an already defined region - The same region is marked as reserved twice Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/cac867d2b6c17e53d9e977b5d6cd88cc4e9453b6.1643796665.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/basic_api.c | 217 +++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) (limited to 'tools') diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 01dd64b9a41b..70f62aae9df9 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -239,9 +239,226 @@ static int memblock_add_checks(void) return 0; } + /* + * A simple test that marks a memory block of a specified base address + * and size as reserved and to the collection of reserved memory regions + * (memblock.reserved). It checks if a new entry was created and if region + * counter and total memory size were correctly updated. + */ +static int memblock_reserve_simple_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.reserved.regions[0]; + + struct region r = { + .base = SZ_2G, + .size = SZ_128M + }; + + reset_memblock(); + memblock_reserve(r.base, r.size); + + assert(rgn->base == r.base); + assert(rgn->size == r.size); + + return 0; +} + +/* + * A test that tries to mark two memory blocks that don't overlap as reserved + * and checks if two entries were correctly added to the collection of reserved + * memory regions (memblock.reserved) and if this change was reflected in + * memblock.reserved's total size and region counter. + */ +static int memblock_reserve_disjoint_check(void) +{ + struct memblock_region *rgn1, *rgn2; + + rgn1 = &memblock.reserved.regions[0]; + rgn2 = &memblock.reserved.regions[1]; + + struct region r1 = { + .base = SZ_256M, + .size = SZ_16M + }; + struct region r2 = { + .base = SZ_512M, + .size = SZ_512M + }; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_reserve(r2.base, r2.size); + + assert(rgn1->base == r1.base); + assert(rgn1->size == r1.size); + + assert(rgn2->base == r2.base); + assert(rgn2->size == r2.size); + + assert(memblock.reserved.cnt == 2); + assert(memblock.reserved.total_size == r1.size + r2.size); + + return 0; +} + +/* + * A test that tries to mark two memory blocks as reserved, where the + * second one overlaps with the beginning of the first (that is + * r1.base < r2.base + r2.size). + * It checks if two entries are merged into one region that starts at r2.base + * and has size of two regions minus their intersection. The test also verifies + * that memblock can still see only one entry and has a correct total size of + * the reserved memory. + */ +static int memblock_reserve_overlap_top_check(void) +{ + struct memblock_region *rgn; + phys_addr_t total_size; + + rgn = &memblock.reserved.regions[0]; + + struct region r1 = { + .base = SZ_1G, + .size = SZ_1G + }; + struct region r2 = { + .base = SZ_128M, + .size = SZ_1G + }; + + total_size = (r1.base - r2.base) + r1.size; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_reserve(r2.base, r2.size); + + assert(rgn->base == r2.base); + assert(rgn->size == total_size); + + assert(memblock.reserved.cnt == 1); + assert(memblock.reserved.total_size == total_size); + + return 0; +} + +/* + * A test that tries to mark two memory blocks as reserved, where the + * second one overlaps with the end of the first entry (that is + * r2.base < r1.base + r1.size). + * It checks if two entries are merged into one region that starts at r1.base + * and has size of two regions minus their intersection. It verifies that + * memblock can still see only one entry and has a correct total size of the + * reserved memory. + */ +static int memblock_reserve_overlap_bottom_check(void) +{ + struct memblock_region *rgn; + phys_addr_t total_size; + + rgn = &memblock.reserved.regions[0]; + + struct region r1 = { + .base = SZ_2K, + .size = SZ_128K + }; + struct region r2 = { + .base = SZ_128K, + .size = SZ_128K + }; + + total_size = (r2.base - r1.base) + r2.size; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_reserve(r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == total_size); + + assert(memblock.reserved.cnt == 1); + assert(memblock.reserved.total_size == total_size); + + return 0; +} + +/* + * A test that tries to mark two memory blocks as reserved, where the second + * one is within the range of the first entry (that is + * (r1.base < r2.base) && (r2.base + r2.size < r1.base + r1.size)). + * It checks if two entries are merged into one region that stays the + * same. The counter and total size of available memory are expected to not be + * updated. + */ +static int memblock_reserve_within_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.reserved.regions[0]; + + struct region r1 = { + .base = SZ_1M, + .size = SZ_8M + }; + struct region r2 = { + .base = SZ_2M, + .size = SZ_64K + }; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_reserve(r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == r1.size); + + assert(memblock.reserved.cnt == 1); + assert(memblock.reserved.total_size == r1.size); + + return 0; +} + +/* + * A simple test that tries to reserve the same memory block twice. + * The region counter and total size of reserved memory are expected to not + * be updated. + */ +static int memblock_reserve_twice_check(void) +{ + struct region r = { + .base = SZ_16K, + .size = SZ_2M + }; + + reset_memblock(); + + memblock_reserve(r.base, r.size); + memblock_reserve(r.base, r.size); + + assert(memblock.reserved.cnt == 1); + assert(memblock.reserved.total_size == r.size); + + return 0; +} + +static int memblock_reserve_checks(void) +{ + memblock_reserve_simple_check(); + memblock_reserve_disjoint_check(); + memblock_reserve_overlap_top_check(); + memblock_reserve_overlap_bottom_check(); + memblock_reserve_within_check(); + memblock_reserve_twice_check(); + + return 0; +} + int memblock_basic_checks(void) { memblock_initialization_check(); memblock_add_checks(); + memblock_reserve_checks(); + return 0; } -- cgit v1.2.3-70-g09d2 From b4d968931e5e07cdff262bfd9b60291d072ad0d7 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:13 +0100 Subject: memblock tests: Add memblock_remove tests Add checks for removing a region from available memory in different scenarios: - The requested region matches one in the collection of available memory regions - The requested region does not exist in memblock.memory - The region overlaps with one of the entries: from the top (its end address is bigger than the base of the existing region) or from the bottom (its base address is smaller than the end address of one of the regions) - The region is within an already defined region Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/8e6aa005407bbe1a75b75e85ac04ebb51318a52a.1643796665.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/basic_api.c | 205 +++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) (limited to 'tools') diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 70f62aae9df9..8a0d2ffa4e21 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -454,11 +454,216 @@ static int memblock_reserve_checks(void) return 0; } + /* + * A simple test that tries to remove the first entry of the array of + * available memory regions. By "removing" a region we mean overwriting it + * with the next region in memblock.memory. To check this is the case, the + * test adds two memory blocks and verifies that the value of the latter + * was used to erase r1 region. It also checks if the region counter and + * total size were updated to expected values. + */ +static int memblock_remove_simple_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.memory.regions[0]; + + struct region r1 = { + .base = SZ_2K, + .size = SZ_4K + }; + struct region r2 = { + .base = SZ_128K, + .size = SZ_4M + }; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_add(r2.base, r2.size); + memblock_remove(r1.base, r1.size); + + assert(rgn->base == r2.base); + assert(rgn->size == r2.size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == r2.size); + + return 0; +} + + /* + * A test that tries to remove a region that was not registered as available + * memory (i.e. has no corresponding entry in memblock.memory). It verifies + * that array, regions counter and total size were not modified. + */ +static int memblock_remove_absent_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.memory.regions[0]; + + struct region r1 = { + .base = SZ_512K, + .size = SZ_4M + }; + struct region r2 = { + .base = SZ_64M, + .size = SZ_1G + }; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_remove(r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == r1.size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == r1.size); + + return 0; +} + +/* + * A test that tries to remove a region which overlaps with the beginning of + * the already existing entry r1 (that is r1.base < r2.base + r2.size). It + * checks if only the intersection of both regions is removed from the available + * memory pool. The test also checks if the regions counter and total size are + * updated to expected values. + */ +static int memblock_remove_overlap_top_check(void) +{ + struct memblock_region *rgn; + phys_addr_t r1_end, r2_end, total_size; + + rgn = &memblock.memory.regions[0]; + + struct region r1 = { + .base = SZ_32M, + .size = SZ_32M + }; + struct region r2 = { + .base = SZ_16M, + .size = SZ_32M + }; + + r1_end = r1.base + r1.size; + r2_end = r2.base + r2.size; + total_size = r1_end - r2_end; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_remove(r2.base, r2.size); + + assert(rgn->base == r1.base + r2.base); + assert(rgn->size == total_size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == total_size); + + return 0; +} + +/* + * A test that tries to remove a region which overlaps with the end of the + * first entry (that is r2.base < r1.base + r1.size). It checks if only the + * intersection of both regions is removed from the available memory pool. + * The test also checks if the regions counter and total size are updated to + * expected values. + */ +static int memblock_remove_overlap_bottom_check(void) +{ + struct memblock_region *rgn; + phys_addr_t total_size; + + rgn = &memblock.memory.regions[0]; + + struct region r1 = { + .base = SZ_2M, + .size = SZ_64M + }; + struct region r2 = { + .base = SZ_32M, + .size = SZ_256M + }; + + total_size = r2.base - r1.base; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_remove(r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == total_size); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == total_size); + return 0; +} + +/* + * A test that tries to remove a region which is within the range of the + * already existing entry (that is + * (r1.base < r2.base) && (r2.base + r2.size < r1.base + r1.size)). + * It checks if the region is split into two - one that ends at r2.base and + * second that starts at r2.base + size, with appropriate sizes. The test + * also checks if the region counter and total size were updated to + * expected values. + */ +static int memblock_remove_within_check(void) +{ + struct memblock_region *rgn1, *rgn2; + phys_addr_t r1_size, r2_size, total_size; + + rgn1 = &memblock.memory.regions[0]; + rgn2 = &memblock.memory.regions[1]; + + struct region r1 = { + .base = SZ_1M, + .size = SZ_32M + }; + struct region r2 = { + .base = SZ_16M, + .size = SZ_1M + }; + + r1_size = r2.base - r1.base; + r2_size = (r1.base + r1.size) - (r2.base + r2.size); + total_size = r1_size + r2_size; + + reset_memblock(); + memblock_add(r1.base, r1.size); + memblock_remove(r2.base, r2.size); + + assert(rgn1->base == r1.base); + assert(rgn1->size == r1_size); + + assert(rgn2->base == r2.base + r2.size); + assert(rgn2->size == r2_size); + + assert(memblock.memory.cnt == 2); + assert(memblock.memory.total_size == total_size); + + return 0; +} + +static int memblock_remove_checks(void) +{ + memblock_remove_simple_check(); + memblock_remove_absent_check(); + memblock_remove_overlap_top_check(); + memblock_remove_overlap_bottom_check(); + memblock_remove_within_check(); + + return 0; +} + int memblock_basic_checks(void) { memblock_initialization_check(); memblock_add_checks(); memblock_reserve_checks(); + memblock_remove_checks(); return 0; } -- cgit v1.2.3-70-g09d2 From e393c093ec7640dc42ad63fb414a9680b69f26f1 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:14 +0100 Subject: memblock tests: Add memblock_add_node test Add a simple test for NUMA-aware variant of memblock_add function. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/e2d0e6dd264c8c169242b556f7c5b12153f3dee5.1643796665.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/basic_api.c | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'tools') diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 8a0d2ffa4e21..2194a141cfe6 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -55,6 +55,39 @@ static int memblock_add_simple_check(void) return 0; } +/* + * A simple test that adds a memory block of a specified base address, size + * NUMA node and memory flags to the collection of available memory regions. + * It checks if the new entry, region counter and total memory size have + * expected values. + */ +static int memblock_add_node_simple_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.memory.regions[0]; + + struct region r = { + .base = SZ_1M, + .size = SZ_16M + }; + + reset_memblock(); + memblock_add_node(r.base, r.size, 1, MEMBLOCK_HOTPLUG); + + assert(rgn->base == r.base); + assert(rgn->size == r.size); +#ifdef CONFIG_NUMA + assert(rgn->nid == 1); +#endif + assert(rgn->flags == MEMBLOCK_HOTPLUG); + + assert(memblock.memory.cnt == 1); + assert(memblock.memory.total_size == r.size); + + return 0; +} + /* * A test that tries to add two memory blocks that don't overlap with one * another. It checks if two correctly initialized entries were added to the @@ -230,6 +263,7 @@ static int memblock_add_twice_check(void) static int memblock_add_checks(void) { memblock_add_simple_check(); + memblock_add_node_simple_check(); memblock_add_disjoint_check(); memblock_add_overlap_top_check(); memblock_add_overlap_bottom_check(); -- cgit v1.2.3-70-g09d2 From dd45dc071101a45619cbaff0f77d07abdf25caf0 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:15 +0100 Subject: memblock tests: Add memblock_free tests Add checks for removing a region from reserved memory in different scenarios: - The requested region matches one in the collection of reserved memory regions - The requested region does not exist in memblock.reserved - The region overlaps with one of the entries: from the top (its end address is bigger than the base of the existing region) or from the bottom (its base address is smaller than the end address of one of the regions) - The region is within an already defined region Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/30af95c82754ad8029404c3b528a5ef1c05d1ed6.1643796665.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/basic_api.c | 203 +++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) (limited to 'tools') diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 2194a141cfe6..fbb989f6ddbf 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -692,12 +692,215 @@ static int memblock_remove_checks(void) return 0; } +/* + * A simple test that tries to free a memory block that was marked earlier + * as reserved. By "freeing" a region we mean overwriting it with the next + * entry in memblock.reserved. To check this is the case, the test reserves + * two memory regions and verifies that the value of the latter was used to + * erase r1 region. + * The test also checks if the region counter and total size were updated. + */ +static int memblock_free_simple_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.reserved.regions[0]; + + struct region r1 = { + .base = SZ_4M, + .size = SZ_1M + }; + struct region r2 = { + .base = SZ_8M, + .size = SZ_1M + }; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_reserve(r2.base, r2.size); + memblock_free((void *)r1.base, r1.size); + + assert(rgn->base == r2.base); + assert(rgn->size == r2.size); + + assert(memblock.reserved.cnt == 1); + assert(memblock.reserved.total_size == r2.size); + + return 0; +} + + /* + * A test that tries to free a region that was not marked as reserved + * (i.e. has no corresponding entry in memblock.reserved). It verifies + * that array, regions counter and total size were not modified. + */ +static int memblock_free_absent_check(void) +{ + struct memblock_region *rgn; + + rgn = &memblock.reserved.regions[0]; + + struct region r1 = { + .base = SZ_2M, + .size = SZ_8K + }; + struct region r2 = { + .base = SZ_16M, + .size = SZ_128M + }; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_free((void *)r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == r1.size); + + assert(memblock.reserved.cnt == 1); + assert(memblock.reserved.total_size == r1.size); + + return 0; +} + +/* + * A test that tries to free a region which overlaps with the beginning of + * the already existing entry r1 (that is r1.base < r2.base + r2.size). It + * checks if only the intersection of both regions is freed. The test also + * checks if the regions counter and total size are updated to expected + * values. + */ +static int memblock_free_overlap_top_check(void) +{ + struct memblock_region *rgn; + phys_addr_t total_size; + + rgn = &memblock.reserved.regions[0]; + + struct region r1 = { + .base = SZ_8M, + .size = SZ_32M + }; + struct region r2 = { + .base = SZ_1M, + .size = SZ_8M + }; + + total_size = (r1.size + r1.base) - (r2.base + r2.size); + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_free((void *)r2.base, r2.size); + + assert(rgn->base == r2.base + r2.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 free a region which overlaps with the end of the + * first entry (that is r2.base < r1.base + r1.size). It checks if only the + * intersection of both regions is freed. The test also checks if the + * regions counter and total size are updated to expected values. + */ +static int memblock_free_overlap_bottom_check(void) +{ + struct memblock_region *rgn; + phys_addr_t total_size; + + rgn = &memblock.reserved.regions[0]; + + struct region r1 = { + .base = SZ_8M, + .size = SZ_32M + }; + struct region r2 = { + .base = SZ_32M, + .size = SZ_32M + }; + + total_size = r2.base - r1.base; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_free((void *)r2.base, r2.size); + + assert(rgn->base == r1.base); + assert(rgn->size == total_size); + + assert(memblock.reserved.cnt == 1); + assert(memblock.reserved.total_size == total_size); + + return 0; +} + +/* + * A test that tries to free a region which is within the range of the + * already existing entry (that is + * (r1.base < r2.base) && (r2.base + r2.size < r1.base + r1.size)). + * It checks if the region is split into two - one that ends at r2.base and + * second that starts at r2.base + size, with appropriate sizes. It is + * expected that the region counter and total size fields were updated t + * reflect that change. + */ +static int memblock_free_within_check(void) +{ + struct memblock_region *rgn1, *rgn2; + phys_addr_t r1_size, r2_size, total_size; + + rgn1 = &memblock.reserved.regions[0]; + rgn2 = &memblock.reserved.regions[1]; + + struct region r1 = { + .base = SZ_1M, + .size = SZ_8M + }; + struct region r2 = { + .base = SZ_4M, + .size = SZ_1M + }; + + r1_size = r2.base - r1.base; + r2_size = (r1.base + r1.size) - (r2.base + r2.size); + total_size = r1_size + r2_size; + + reset_memblock(); + memblock_reserve(r1.base, r1.size); + memblock_free((void *)r2.base, r2.size); + + assert(rgn1->base == r1.base); + assert(rgn1->size == r1_size); + + assert(rgn2->base == r2.base + r2.size); + assert(rgn2->size == r2_size); + + assert(memblock.reserved.cnt == 2); + assert(memblock.reserved.total_size == total_size); + + return 0; +} + +static int memblock_free_checks(void) +{ + memblock_free_simple_check(); + memblock_free_absent_check(); + memblock_free_overlap_top_check(); + memblock_free_overlap_bottom_check(); + memblock_free_within_check(); + + return 0; +} + int memblock_basic_checks(void) { memblock_initialization_check(); memblock_add_checks(); memblock_reserve_checks(); memblock_remove_checks(); + memblock_free_checks(); return 0; } -- cgit v1.2.3-70-g09d2 From 2a7ceac9e58167fadc3496c5f694543d4bbe03ef Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Fri, 4 Mar 2022 13:52:49 +0100 Subject: memblock tests: Fix testing with 32-bit physical addresses Building memblock simulator on x86_64 with 32BIT_PHYS_ADDR_T=1 produces "cast to pointer from integer of different size" warnings. Fix them by building the binary in 32-bit environment when using 32-bit physical addresses. Signed-off-by: Karolina Drobnik Signed-off-by: Mike Rapoport --- tools/testing/memblock/scripts/Makefile.include | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/memblock/scripts/Makefile.include b/tools/testing/memblock/scripts/Makefile.include index 699b0d6cda07..641569ccb7b0 100644 --- a/tools/testing/memblock/scripts/Makefile.include +++ b/tools/testing/memblock/scripts/Makefile.include @@ -11,7 +11,9 @@ ifeq ($(MOVABLE_NODE), 1) CFLAGS += -D MOVABLE_NODE endif -# Use 32 bit physical addresses +# Use 32 bit physical addresses. +# Remember to install 32-bit version of dependencies. ifeq ($(32BIT_PHYS_ADDR_T), 1) - CFLAGS += -U CONFIG_PHYS_ADDR_T_64BIT + CFLAGS += -m32 -U CONFIG_PHYS_ADDR_T_64BIT + LDFLAGS += -m32 endif -- cgit v1.2.3-70-g09d2 From 2c3dacba5d46ef0ff83fe6b82570c433910b63ab Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:43 +0100 Subject: memblock tests: Split up reset_memblock function 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/5cc1ba9a0ade922dbf4ba450165b81a9ed17d4a9.1646055639.git.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(-) (limited to 'tools') 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 -- cgit v1.2.3-70-g09d2 From 284d950dd6b0ea699608455e443341e82f9719c8 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:44 +0100 Subject: memblock tests: Add simulation of physical memory 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/dce115503c74a6936c44694b00014658a1bb6522.1646055639.git.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(-) (limited to 'tools') 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 #include -#include #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 #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 +#include #include #include +#include + +#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 -- cgit v1.2.3-70-g09d2 From 142eac65f3e04577451a40b3cc57e708311eed01 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:45 +0100 Subject: memblock tests: Add memblock_alloc tests for top down 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/26ccf409b8ff0394559d38d792b2afb24b55887c.1646055639.git.karolinadrobnik@gmail.com --- tools/testing/memblock/Makefile | 2 +- tools/testing/memblock/main.c | 3 + tools/testing/memblock/tests/alloc_api.c | 434 +++++++++++++++++++++++++++++++ tools/testing/memblock/tests/alloc_api.h | 9 + 4 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 tools/testing/memblock/tests/alloc_api.c create mode 100644 tools/testing/memblock/tests/alloc_api.h (limited to 'tools') 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..add4345aa289 --- /dev/null +++ b/tools/testing/memblock/tests/alloc_api.c @@ -0,0 +1,434 @@ +// 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 -- cgit v1.2.3-70-g09d2 From 0237ee2388703cf72d1e1f9ea0c73c054582f30d Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:46 +0100 Subject: memblock tests: Add memblock_alloc tests for bottom up 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/426674eee20d99dca49caf1ee0142a83dccbc98d.1646055639.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/alloc_api.c | 324 ++++++++++++++++++++++++++++++- 1 file changed, 320 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/memblock/tests/alloc_api.c b/tools/testing/memblock/tests/alloc_api.c index add4345aa289..d1aa7e15c18d 100644 --- a/tools/testing/memblock/tests/alloc_api.c +++ b/tools/testing/memblock/tests/alloc_api.c @@ -411,23 +411,339 @@ 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; -- cgit v1.2.3-70-g09d2 From 16567b5f30f6ea13a78a3b553e0c9c0c1ba34e64 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:47 +0100 Subject: memblock tests: Add memblock_alloc_from tests for top down 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/3dd645f437975fd393010b95b8faa85d2b86490a.1646055639.git.karolinadrobnik@gmail.com --- tools/testing/memblock/Makefile | 3 +- tools/testing/memblock/main.c | 2 + tools/testing/memblock/tests/alloc_helpers_api.c | 226 +++++++++++++++++++++++ tools/testing/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 (limited to 'tools') 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 -- cgit v1.2.3-70-g09d2 From 0ac06631a3bcdaed497f7b8abccf405f9eb8e0bd Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:48 +0100 Subject: memblock tests: Add memblock_alloc_from tests for bottom up 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/506cf5293c8a21c012b7ea87b14af07754d3e656.1646055639.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/alloc_helpers_api.c | 175 ++++++++++++++++++++++- 1 file changed, 171 insertions(+), 4 deletions(-) (limited to 'tools') 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(); -- cgit v1.2.3-70-g09d2 From 8f98435d674e5b7ec382914d2755849f5a95754b Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:49 +0100 Subject: memblock tests: Add memblock_alloc_try_nid tests for top down 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/d6c282e0f9f62c15bf74c216214604764232d637.1646055639.git.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 (limited to 'tools') 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..13622c0cfdb6 --- /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 -- cgit v1.2.3-70-g09d2 From 9d8f6abe980f98ffccad80b11d46df2116d7a5fc Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:50 +0100 Subject: memblock tests: Add memblock_alloc_try_nid tests for bottom up 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/1c0ba11b8da5dc8f71ad45175c536fa4be720984.1646055639.git.karolinadrobnik@gmail.com --- tools/testing/memblock/tests/alloc_nid_api.c | 503 ++++++++++++++++++++++++++- 1 file changed, 499 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/memblock/tests/alloc_nid_api.c b/tools/testing/memblock/tests/alloc_nid_api.c index 13622c0cfdb6..6390206e50e1 100644 --- a/tools/testing/memblock/tests/alloc_nid_api.c +++ b/tools/testing/memblock/tests/alloc_nid_api.c @@ -653,26 +653,521 @@ 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; -- cgit v1.2.3-70-g09d2 From 58ffc34896db2e5e49e6ae6bf8042f85504d84e8 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Mon, 28 Feb 2022 15:46:51 +0100 Subject: memblock tests: Add TODO and README files 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 Signed-off-by: Mike Rapoport Link: https://lore.kernel.org/r/d5e39b9f7dcef177ebc14282727447bc21e3b38f.1646055639.git.karolinadrobnik@gmail.com --- tools/testing/memblock/README | 107 ++++++++++++++++++++++++++++++++++++++++++ tools/testing/memblock/TODO | 28 +++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tools/testing/memblock/README create mode 100644 tools/testing/memblock/TODO (limited to 'tools') diff --git a/tools/testing/memblock/README b/tools/testing/memblock/README new file mode 100644 index 000000000000..ca6afcff013a --- /dev/null +++ b/tools/testing/memblock/README @@ -0,0 +1,107 @@ +================== +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 by the initial commit that +introduced memblock simulator (commit 16802e55dea9 ("memblock tests: Add +skeleton of the memblock simulator")) and a few preparation commits just +before it. 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 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 -- cgit v1.2.3-70-g09d2