From aa0eab8639ff0fb1edf18b8616e6ae2c38ca5854 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:00 +0100 Subject: [PATCH 01/17] 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 ++++++++++++++ .../radix-tree => include}/linux/slab.h | 15 ++++---- 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 ---------------- 6 files changed, 76 insertions(+), 68 deletions(-) rename tools/{testing/radix-tree => include}/linux/slab.h (68%) create mode 100644 tools/lib/slab.c delete mode 100644 tools/testing/radix-tree/linux/gfp.h diff --git a/tools/include/linux/gfp.h b/tools/include/linux/gfp.h index 22030756fbc0a..b238dbc9eb858 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/testing/radix-tree/linux/slab.h b/tools/include/linux/slab.h similarity index 68% rename from tools/testing/radix-tree/linux/slab.h rename to tools/include/linux/slab.h index 2958830ce4d76..07d7930d40031 100644 --- a/tools/testing/radix-tree/linux/slab.h +++ b/tools/include/linux/slab.h @@ -1,20 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef SLAB_H -#define SLAB_H +#ifndef _TOOLS_SLAB_H +#define _TOOLS_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 *); +#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); + return kmalloc(size, gfp | __GFP_ZERO); } void *kmem_cache_alloc(struct kmem_cache *cachep, int flags); @@ -24,4 +25,4 @@ struct kmem_cache *kmem_cache_create(const char *name, unsigned int size, unsigned int align, unsigned int flags, void (*ctor)(void *)); -#endif /* SLAB_H */ +#endif /* _TOOLS_SLAB_H */ diff --git a/tools/lib/slab.c b/tools/lib/slab.c new file mode 100644 index 0000000000000..959997fb0652f --- /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 aa6abfe0749c1..c4ea4fbb0bfcd 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 2d9c59df60de0..81539f5439546 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 32159c08a52e5..0000000000000 --- 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 From 884ee1e585ca01b47f66c7e5b6902c62d1af77a3 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:01 +0100 Subject: [PATCH 02/17] 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(+) diff --git a/tools/include/linux/types.h b/tools/include/linux/types.h index 6e14a533ab4e1..35cedaf191e8a 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; From 5cf67a6051ea2558fd7c3d39c5a808db73073e9d Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:02 +0100 Subject: [PATCH 03/17] 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(+) diff --git a/tools/include/linux/kernel.h b/tools/include/linux/kernel.h index 9701e8307db02..4b0673bf52c2e 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) From 9c07af207c65e6fa8063d1105356568d8b03c536 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:03 +0100 Subject: [PATCH 04/17] 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(+) diff --git a/tools/include/linux/atomic.h b/tools/include/linux/atomic.h index 00a6c4ca562b9..01907b33537e0 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 35cedaf191e8a..5908e58c35981 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 From 2473bc35ba554003cdda8a3a0124f35433590fe7 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:04 +0100 Subject: [PATCH 05/17] 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 diff --git a/tools/include/linux/mm.h b/tools/include/linux/mm.h new file mode 100644 index 0000000000000..a03d9bba51514 --- /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 From 1fef073b71c3f22e2328872324fdb187f4fb7d7d Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:05 +0100 Subject: [PATCH 06/17] 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 diff --git a/tools/include/linux/cache.h b/tools/include/linux/cache.h new file mode 100644 index 0000000000000..9e9d585f0b9df --- /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 From 93f4e871e7e0fd0269e27f5122a70a39d8105924 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:06 +0100 Subject: [PATCH 07/17] 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 diff --git a/tools/include/linux/io.h b/tools/include/linux/io.h new file mode 100644 index 0000000000000..e129871fe6610 --- /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 From a2e3fe5f24f90e2bd98fa8bfabbf55962080b7b4 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:07 +0100 Subject: [PATCH 08/17] 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 diff --git a/tools/include/linux/pfn.h b/tools/include/linux/pfn.h new file mode 100644 index 0000000000000..7512a58189ebd --- /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 From 62183279ad2f52d36b40365eb3a27c7a4a2284f8 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:08 +0100 Subject: [PATCH 09/17] 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 diff --git a/tools/include/linux/debugfs.h b/tools/include/linux/debugfs.h new file mode 100644 index 0000000000000..4ba06140b1bec --- /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 From 16802e55dea9534c18a30bd8eeefea8a06337916 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:09 +0100 Subject: [PATCH 10/17] 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 +++++++ .../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 diff --git a/MAINTAINERS b/MAINTAINERS index fca970a46e77a..38502bf0231a4 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 07d7930d40031..f41d8a0eb1a42 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 0000000000000..654338e0be52e --- /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 0000000000000..e43ed9de9bcf3 --- /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 0000000000000..13ff8e5d22ef6 --- /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 0000000000000..94b52a8718b55 --- /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 0000000000000..6be6020328fbd --- /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 0000000000000..828e0ee0bc6c4 --- /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 0000000000000..d2f148bd89022 --- /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 0000000000000..462f8c5e8aa09 --- /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 0000000000000..47988765a2197 --- /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 0000000000000..7c2eb5c9bb54d --- /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 0000000000000..61af424d8c6c3 --- /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 0000000000000..62958da35d0f8 --- /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 0000000000000..7b0909e8b759d --- /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 0000000000000..699b0d6cda074 --- /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 From f3252a22d1f59d89cca769431efa1c95d6343929 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:10 +0100 Subject: [PATCH 11/17] 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 diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile index e43ed9de9bcf3..29715327a2d3a 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 62958da35d0f8..da65b0adee91e 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 0000000000000..7f2597b3dd4db --- /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 0000000000000..1ceecfca1f47c --- /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 0000000000000..03de6eab0c3c9 --- /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 0000000000000..48efc4270ea17 --- /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 From 1f1180d46d21506325b7217da5a546235a2263a2 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:11 +0100 Subject: [PATCH 12/17] 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(+) diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 7f2597b3dd4db..01dd64b9a41b4 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; } From 83787a803793881a2e6cf47ea0706cc8fe4573d7 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:12 +0100 Subject: [PATCH 13/17] 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(+) diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 01dd64b9a41b4..70f62aae9df9b 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; } From b4d968931e5e07cdff262bfd9b60291d072ad0d7 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:13 +0100 Subject: [PATCH 14/17] 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(+) diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 70f62aae9df9b..8a0d2ffa4e21d 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; } From e393c093ec7640dc42ad63fb414a9680b69f26f1 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:14 +0100 Subject: [PATCH 15/17] 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(+) diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 8a0d2ffa4e21d..2194a141cfe64 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(); From dd45dc071101a45619cbaff0f77d07abdf25caf0 Mon Sep 17 00:00:00 2001 From: Karolina Drobnik Date: Wed, 2 Feb 2022 12:03:15 +0100 Subject: [PATCH 16/17] 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(+) diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 2194a141cfe64..fbb989f6ddbf8 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; } From f30b002ccfee8c60c8feb590e145c0b5e8fa4c67 Mon Sep 17 00:00:00 2001 From: Miaohe Lin Date: Thu, 17 Feb 2022 22:07:54 +0800 Subject: [PATCH 17/17] memblock: __next_mem_pfn_range_in_zone: remove unneeded local variable nid The nid is only used to act as output parameter of __next_mem_range. Since NULL can be passed to __next_mem_range as out_nid, we can thus remove nid by passing NULL here. Signed-off-by: Miaohe Lin [rppt: updated the commit message] Signed-off-by: Mike Rapoport --- mm/memblock.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/memblock.c b/mm/memblock.c index 1018e50566f35..11561bbff8f5f 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -1278,11 +1278,10 @@ __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone, { int zone_nid = zone_to_nid(zone); phys_addr_t spa, epa; - int nid; __next_mem_range(idx, zone_nid, MEMBLOCK_NONE, &memblock.memory, &memblock.reserved, - &spa, &epa, &nid); + &spa, &epa, NULL); while (*idx != U64_MAX) { unsigned long epfn = PFN_DOWN(epa); @@ -1309,7 +1308,7 @@ __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone, __next_mem_range(idx, zone_nid, MEMBLOCK_NONE, &memblock.memory, &memblock.reserved, - &spa, &epa, &nid); + &spa, &epa, NULL); } /* signal end of iteration */