Skip to contentSkip to conversation

Introduce memblock simulator

PATCH Superseded Karolina Drobnik 4 participants 43 messages Jan 27, 2022 – Jan 31, 2022

Subsystems (4)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover Introduce memblock simulator

Memblock is a boot time memory allocator, which allows managing memory regions before the actual memory management is initialized. Because it is used so early during the booting process, testing and debugging it is difficult. As memblock has few kernel dependencies, it is possible to simulate its runtime behaviour in userspace after stubbing a couple of structs and functions.

This series of patches adds an initial version of a test suite for memblock, which is a part of tools/testing infrastructure. Checks included here test the basic functionality of memblock, which is memory regions management - adding/removing available regions, marking them as reserved or freeing them.

To run the tests, build the main target and run it:

$ make; ./main

A successful run produces no output. Also, It is 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.

The future work will include tests for memory allocation done via memblock_alloc_* and memblock_phys_alloc_* family of functions. It will also focus on improving the test suite by adding dedicated test targets for each group of API functions and verbose output.

For a refresher on memblock, see Boot time memory management documentation page[1] and "Getting to know memblock"[2] blog post.

Patchset structure:

* Patch 1 extracts stub definitions of kmalloc/kfree from the radix-tree testing suite, so they can be used in other tests, including memblock simulator
* Patches 2-4 update tools headers to include missing definitions needed by memblock (e.g. phys_addr_t typedef)
* Patches 5-9 add new files to tools/include, some with dummy/downsized definitions
* Patch 10 adds the basic project with local stubs that are closely connected to memblock
* Patch 11 adds a reset function, which is used as a part of the test setup. This functionality is needed, as memblock is reused in each check
* Patches 12-16 add basic test cases for the core memblock functionalities

[1] - https://www.kernel.org/doc/html/latest/core-api/boot-time-mm.html
[2] - https://insecuremode.com/post/2021/12/14/getting-to-know-memblock.html

Karolina Drobnik (16):
tools: Move gfp.h and slab.h from radix-tree to lib tools/include: Add phys_addr_t to types.h tools/include: Add _RET_IP_ and math definitions to kernel.h tools/include: Update atomic.h header tools/include: Add mm.h file
tools/include: Add cache.h stub
tools/include: Add io.h stub
tools/include: Add pfn.h stub
tools/include: Add debugfs.h stub
memblock tests: Add skeleton of the memblock simulator memblock tests: Add memblock reset function memblock tests: Add memblock_add tests
memblock tests: Add memblock_reserve tests memblock tests: Add memblock_remove tests
memblock tests: Add memblock_add_node test memblock tests: Add memblock_free tests

 MAINTAINERS                                   |   1 +
 tools/include/linux/atomic.h                  |   4 +
 tools/include/linux/cache.h                   |  10 +
 tools/include/linux/debugfs.h                 |   5 +
 tools/include/linux/gfp.h                     |  28 +
 tools/include/linux/io.h                      |   5 +
 tools/include/linux/kernel.h                  |   6 +
 tools/include/linux/mm.h                      |  42 +
 tools/include/linux/pfn.h                     |  10 +
 .../radix-tree => include}/linux/slab.h       |  25 +-
 tools/include/linux/types.h                   |   6 +
 tools/lib/slab.c                              |  38 +
 tools/testing/memblock/.gitignore             |   6 +
 tools/testing/memblock/Makefile               |  53 ++
 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                 |   8 +
 tools/testing/memblock/mmzone.c               |  20 +
 .../testing/memblock/scripts/Makefile.include |  17 +
 tools/testing/memblock/tests/basic_api.c      | 896 ++++++++++++++++++
 tools/testing/memblock/tests/basic_api.h      |  10 +
 tools/testing/memblock/tests/common.c         |  27 +
 tools/testing/memblock/tests/common.h         |  15 +
 tools/testing/radix-tree/Makefile             |   3 +-
 tools/testing/radix-tree/linux.c              |  27 -
 tools/testing/radix-tree/linux/gfp.h          |  33 -
 33 files changed, 1396 insertions(+), 68 deletions(-)
 create mode 100644 tools/include/linux/cache.h
 create mode 100644 tools/include/linux/debugfs.h
 create mode 100644 tools/include/linux/io.h
 create mode 100644 tools/include/linux/mm.h
 create mode 100644 tools/include/linux/pfn.h
 rename tools/{testing/radix-tree => include}/linux/slab.h (61%)
 create mode 100644 tools/lib/slab.c
 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
 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
 delete mode 100644 tools/testing/radix-tree/linux/gfp.h

--
2.30.2

#1 lore raw

PATCH 1/16 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.

---
 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
tools/include/linux/gfp.h +28 -0
index 22030756fbc0..4dce3cddd134 100644
@@ -1,4 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _TOOLS_INCLUDE_LINUX_GFP_H
 #define _TOOLS_INCLUDE_LINUX_GFP_H
 
+#include <linux/types.h>
+
+#define __GFP_ZERO		0x8000u
+#define __GFP_DIRECT_RECLAIM	0x400000u
+
+#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_ATOMIC		0x80000u
+#define __GFP_ACCOUNT		0x100000u
+#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 */
tools/include/linux/slab.h renamed from tools/testing/radix-tree/linux/slab.h +8 -7
similarity index 68%
rename from tools/testing/radix-tree/linux/slab.h
rename to tools/include/linux/slab.h
index 2958830ce4d7..07d7930d4003 100644
@@ -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 <linux/types.h>
 #include <linux/gfp.h>
 
-#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 */
tools/lib/slab.c new file +38 -0
new file mode 100644
index 000000000000..959997fb0652
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <stdio.h>
+#include <string.h>
+
+#include <urcu/uatomic.h>
+#include <linux/slab.h>
+#include <malloc.h>
+#include <linux/gfp.h>
+
+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);
+}
tools/testing/radix-tree/Makefile +2 -1
index aa6abfe0749c..c4ea4fbb0bfc 100644
@@ -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
tools/testing/radix-tree/linux.c +0 -27
index 2d9c59df60de..81539f543954 100644
@@ -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 *))
tools/testing/radix-tree/linux/gfp.h deleted +0 -33
deleted file mode 100644
index 32159c08a52e..000000000000
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _GFP_H
-#define _GFP_H
-
-#include <linux/types.h>
-
-#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
Signature · 1 line
2.30.2
maintainer
#2 lore raw

Re: [PATCH 01/16] tools: Move gfp.h and slab.h from radix-tree to lib

Quoted 5 lines · Karolina Drobnik
tools/include/linux/gfp.h +5 -0
+#include <linux/types.h>
+
+#define __GFP_ZERO		0x8000u
+#define __GFP_DIRECT_RECLAIM	0x400000u
+

tools/include/linux/gfp.h line 19

Quoted 10 lines
tools/include/linux/gfp.h +10 -0
+#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_ATOMIC		0x80000u
+#define __GFP_ACCOUNT		0x100000u
+#define __GFP_KSWAPD_RECLAIM	0x2000000u

Why have you reordered the bits here?

tools/testing/radix-tree/linux/gfp.h line 18

Quoted 12 lines
tools/testing/radix-tree/linux/gfp.h deleted +0 -12
-#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

The original ordering makes more sense to me (but then it would; I did it)

#3 lore raw

Re: [PATCH 01/16] tools: Move gfp.h and slab.h from radix-tree to lib

Hi Matthew,

Many thanks for your review.

Quoted 10 lines · Matthew Wilcox
Quoted 9 lines · Karolina Drobnik
tools/include/linux/gfp.h +9 -0
+#include <linux/types.h>
+
+#define __GFP_ZERO             0x8000u
+#define __GFP_DIRECT_RECLAIM   0x400000u
+
+#define __GFP_BITS_SHIFT 26
+#define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
+
+#define __GFP_HIGH             0x20u
Quoted 8 lines

tools/include/linux/gfp.h line 19

Quoted 6 lines
tools/include/linux/gfp.h +6 -0
+#define __GFP_IO               0x40u
+#define __GFP_FS               0x80u
+#define __GFP_NOWARN           0x200u
+#define __GFP_ATOMIC           0x80000u
+#define __GFP_ACCOUNT          0x100000u
+#define __GFP_KSWAPD_RECLAIM   0x2000000u

Why have you reordered the bits here?

I probably mixed them up when I tried to merge the stubs I did for memblock and the files that were already there. I'll fix it in v2.

Quoted 16 lines

tools/testing/radix-tree/linux/gfp.h line 18

Quoted 12 lines
tools/testing/radix-tree/linux/gfp.h deleted +0 -12
-#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

The original ordering makes more sense to me (but then it would; I did
it)

maintainer
#4 lore raw

Re: [PATCH 01/16] tools: Move gfp.h and slab.h from radix-tree to lib

In reply to Karolina Drobnik
Quoted 35 lines · Karolina Drobnik
tools/testing/radix-tree/linux.c +0 -20
index 2d9c59df60de..81539f543954 100644
@@ -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)

tools/testing/radix-tree/linux.c void kmem_cache_free(struct kmem_cache *cachep, void *objp) · line 82

Quoted 10 lines
tools/testing/radix-tree/linux.c +0 -7
-		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 *))

I don't think it makes much sense to move kmalloc() and not to move the kmem_cache* functions. They're all provided by slab in the kernel proper, so while you don't use them, I think keeping all the memory allocation functions together is preferable.

#5 lore raw

Re: [PATCH 01/16] tools: Move gfp.h and slab.h from radix-tree to lib

Quoted 47 lines · Matthew Wilcox
Quoted 46 lines · Karolina Drobnik
tools/testing/radix-tree/linux.c +0 -0
index 2d9c59df60de..81539f543954 100644
@@ -14,7 +14,6 @@


 int nr_allocated;
 int preempt_count;
-int kmalloc_verbose;
 int test_verbose;

 struct kmem_cache {

tools/testing/radix-tree/linux.c +0 -0
@@ -78,32 +77,6 @@ void kmem_cache_free(struct kmem_cache *cachep,
 void *objp)
         pthread_mutex_unlock(&cachep->lock);
  }

tools/testing/radix-tree/linux.c +0 -26
-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

Quoted 8 lines
Quoted 2 lines

int align,
                unsigned int flags, void (*ctor)(void *))

I don't think it makes much sense to move kmalloc() and not to move the kmem_cache* functions.  They're all provided by slab in the kernel
proper, so while you don't use them, I think keeping all the memory allocation functions together is preferable.

I wasn't sure if these functions would be used by other test suites, so I left them here. But I can move them if you think it's better to keep them together.

#6 lore raw

Re: [PATCH 01/16] tools: Move gfp.h and slab.h from radix-tree to lib

Quoted 7 lines · Karolina Drobnik
Quoted 5 lines · Matthew Wilcox

I don't think it makes much sense to move kmalloc() and not to move the kmem_cache* functions.  They're all provided by slab in the kernel
proper, so while you don't use them, I think keeping all the memory allocation functions together is preferable.

Quoted 6 lines

I wasn't sure if these functions would be used by other test suites, so
I left them here. But I can move them if you think it's better to keep
them together.

OK, now I can see why this wouldn't work - kmem_cache_alloc and kmem_cache_free operate on radix_tree_node struct, making it specific to this test suite.

So, for this patch, I'm going to tidy up the gfp.h header but leave radix-tree/linux.c and lib/slab.c as they are now.

#7 lore raw

PATCH 2/16 tools/include: Add phys_addr_t to types.h

In reply to Karolina Drobnik

Update types.h file to include phys_addr_t typedef so it can be used in testing.

---
 tools/include/linux/types.h | 6 ++++++
 1 file changed, 6 insertions(+)
tools/include/linux/types.h +6 -0
index 6e14a533ab4e..35cedaf191e8 100644
@@ -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;
Signature · 1 line
2.30.2
#8 lore raw

PATCH 3/16 tools/include: Add _RET_IP_ and math definitions to kernel.h

In reply to Karolina Drobnik

Add max_t, min_t and clamp functions, together with _RET_IP_ definition, so they can be used in testing.

---
 tools/include/linux/kernel.h | 6 ++++++
 1 file changed, 6 insertions(+)
tools/include/linux/kernel.h +6 -0
index 9701e8307db0..4b0673bf52c2 100644
@@ -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)
Signature · 1 line
2.30.2
maintainer
#9 lore raw

Re: [PATCH 03/16] tools/include: Add _RET_IP_ and math definitions to kernel.h

Quoted 2 lines · Karolina Drobnik

Add max_t, min_t and clamp functions, together with _RET_IP_ definition, so they can be used in testing.

Rather than adding our own definitions of min/max/clamp, have you considered using #include "../../../include/linux/minmax.h"? In my experience reusing this kind of "leaf" header works out better than duplicating it.

#10 lore raw

Re: [PATCH 03/16] tools/include: Add _RET_IP_ and math definitions to kernel.h

Quoted 6 lines · Matthew Wilcox
Quoted 2 lines · Karolina Drobnik

Add max_t, min_t and clamp functions, together with _RET_IP_ definition, so they can be used in testing.

Rather than adding our own definitions of min/max/clamp, have you considered using #include "../../../include/linux/minmax.h"?

I tried doing it in the very beginning and couldn't get it to compile. Now, I see it's because in minmax.h we use __UNIQUE_ID, which is undefined because of "#ifdef __KERNEL__" in include/linux/compiler.h.

Quoted 2 lines

In my experience reusing this kind of "leaf" header works out better than duplicating it.

I like this approach as well, and I tried to use it in other memblock headers. Still, as it's not that straightforward here, and I went for duplication.

#11 lore raw

PATCH 4/16 tools/include: Update atomic.h header

In reply to Karolina Drobnik

Add atomic_long_t typedef and atomic_long_set function so they
can be used in testing.

---
 tools/include/linux/atomic.h | 4 ++++
 1 file changed, 4 insertions(+)
tools/include/linux/atomic.h +4 -0
index 00a6c4ca562b..5d2431889606 100644
@@ -4,6 +4,10 @@
 
 #include <asm/atomic.h>
 
+typedef atomic_t atomic_long_t;
+
+void atomic_long_set(atomic_long_t *v, long i);
+
 /* atomic_cmpxchg_relaxed */
 #ifndef atomic_cmpxchg_relaxed
 #define  atomic_cmpxchg_relaxed		atomic_cmpxchg
Signature · 1 line
2.30.2
maintainer
#12 lore raw

Re: [PATCH 04/16] tools/include: Update atomic.h header

Quoted 13 lines · Karolina Drobnik

Add atomic_long_t typedef and atomic_long_set function so they
can be used in testing.

---
 tools/include/linux/atomic.h | 4 ++++
 1 file changed, 4 insertions(+)
tools/include/linux/atomic.h +0 -0
index 00a6c4ca562b..5d2431889606 100644

tools/include/linux/atomic.h line 7

Quoted 4 lines
tools/include/linux/atomic.h +1 -0
@@ -4,6 +4,10 @@
 
 #include <asm/atomic.h>
 
+typedef atomic_t atomic_long_t;

Given this:

typedef struct {
        int counter;
} atomic_t;

your definition seems wrong. Why not add atomic_long_t to types.h, adjacent to atomic_t?

#13 lore raw

Re: [PATCH 04/16] tools/include: Update atomic.h header

Quoted 22 lines · Matthew Wilcox
Quoted 14 lines · Karolina Drobnik

Add atomic_long_t typedef and atomic_long_set function so they can be used in testing.

---
 tools/include/linux/atomic.h | 4 ++++

 1 file changed, 4 insertions(+)
tools/include/linux/atomic.h +0 -0
index 00a6c4ca562b..5d2431889606 100644
@@ -4,6 +4,10 @@
Quoted 4 lines


 #include <asm/atomic.h>

+typedef atomic_t atomic_long_t;

Given this:

Quoted 6 lines

typedef struct {
        int counter;
} atomic_t;

your definition seems wrong.  Why not add atomic_long_t to types.h, adjacent to atomic_t?

Hmm, that sounds like a better idea. Will do that, thank you.

#14 lore raw

PATCH 5/16 tools/include: Add mm.h file

In reply to Karolina Drobnik

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.

---
 tools/include/linux/mm.h | 42 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 tools/include/linux/mm.h
tools/include/linux/mm.h new file +42 -0
new file mode 100644
index 000000000000..a03d9bba5151
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TOOLS_LINUX_MM_H
+#define _TOOLS_LINUX_MM_H
+
+#include <linux/mmzone.h>
+#include <uapi/linux/const.h>
+
+#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

--
2.30.2

#15 lore raw

PATCH 6/16 tools/include: Add cache.h stub

In reply to Karolina Drobnik

Add a dummy version of the cache header.

---
 tools/include/linux/cache.h | 10 ++++++++++
 1 file changed, 10 insertions(+)
 create mode 100644 tools/include/linux/cache.h
tools/include/linux/cache.h new file +10 -0
new file mode 100644
index 000000000000..ff010a11f47d
@@ -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		BIT(L1_CACHE_SHIFT)
+
+#define SMP_CACHE_BYTES L1_CACHE_BYTES
+
+#endif
Signature · 1 line
2.30.2
maintainer
#16 lore raw

Re: [PATCH 06/16] tools/include: Add cache.h stub

tools/include/linux/cache.h line 10

Quoted 12 lines · Karolina Drobnik

+++ b/tools/include/linux/cache.h

tools/include/linux/cache.h new file +10 -0
@@ -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		BIT(L1_CACHE_SHIFT)
+
+#define SMP_CACHE_BYTES L1_CACHE_BYTES
+
+#endif

You've added an implicit dependency on include/vdso/bits.h which seems unpleasant ...

#17 lore raw

Re: [PATCH 06/16] tools/include: Add cache.h stub

Quoted 17 lines · Matthew Wilcox

tools/include/linux/cache.h line 10

Quoted 12 lines · Karolina Drobnik

+++ b/tools/include/linux/cache.h

tools/include/linux/cache.h new file +10 -0
@@ -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         BIT(L1_CACHE_SHIFT)
+
+#define SMP_CACHE_BYTES L1_CACHE_BYTES
+
+#endif

You've added an implicit dependency on include/vdso/bits.h which seems
unpleasant ...

I'll admit that I just tried to make checkpatch.pl happy with this change. But you're right, adding such a dependency is undesirable. I can define it as (1 << L1_CACHE_SHIFT) and ignore the warning.

Is it fine to do so for tools/testing code?

maintainer
#18 lore raw

Re: [PATCH 06/16] tools/include: Add cache.h stub

Quoted 19 lines · Karolina Drobnik
Quoted 17 lines · Matthew Wilcox

tools/include/linux/cache.h line 10

Quoted 12 lines · Karolina Drobnik

+++ b/tools/include/linux/cache.h

tools/include/linux/cache.h new file +10 -0
@@ -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         BIT(L1_CACHE_SHIFT)
+
+#define SMP_CACHE_BYTES L1_CACHE_BYTES
+
+#endif

You've added an implicit dependency on include/vdso/bits.h which seems
unpleasant ...

Quoted 5 lines

I'll admit that I just tried to make checkpatch.pl happy with this change. But you're right, adding such a dependency is undesirable. I can define it as (1 << L1_CACHE_SHIFT) and ignore the warning.

Is it fine to do so for tools/testing code?

checkpatch warnings are recommendations. Only fix checkpatch errors.

#19 lore raw

PATCH 7/16 tools/include: Add io.h stub

In reply to Karolina Drobnik

Add a dummy io.h header.

---
 tools/include/linux/io.h | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 tools/include/linux/io.h
tools/include/linux/io.h new file +5 -0
new file mode 100644
index 000000000000..e129871fe661
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TOOLS_IO_H
+#define _TOOLS_IO_H
+
+#endif
Signature · 1 line
2.30.2
maintainer
#20 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

Quoted 1 line · Karolina Drobnik

Add a dummy io.h header.

Rather begs the question of what memblock.c needs from linux/io.h. Wouldn't it be better to:

+++ b/mm/memblock.c
@@ -18,7 +18,6 @@
#include <linux/memblock.h>

#include <asm/sections.h>
-#include <linux/io.h>

#include "internal.h"

(allmodconfig on x86-64 builds fine with this; I have not done an extended sweep of other arches / build options).

#21 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

Quoted 9 lines · Matthew Wilcox
Quoted 1 line · Karolina Drobnik

Add a dummy io.h header.

Rather begs the question of what memblock.c needs from linux/io.h.

Wouldn't it be better to:

+++ b/mm/memblock.c

Review line 23

Quoted 7 lines
(quoted diff) +0 -1
@@ -18,7 +18,6 @@
  #include <linux/memblock.h>
 
  #include <asm/sections.h>
-#include <linux/io.h>
 
  #include "internal.h"
 

That was something I considered in the very beginning, but didn't have a chance to verify it works for all architectures. I can take a look after I'm finished with other v2 changes.

Quoted 2 lines

(allmodconfig on x86-64 builds fine with this; I have not done an extended sweep of other arches / build options).

maintainer
#22 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

Quoted 15 lines · Karolina Drobnik
Quoted 14 lines · Matthew Wilcox
Quoted 1 line · Karolina Drobnik

Add a dummy io.h header.

Rather begs the question of what memblock.c needs from linux/io.h.

Wouldn't it be better to:

+++ b/mm/memblock.c

(quoted diff) +0 -1
@@ -18,7 +18,6 @@
  #include <linux/memblock.h>
 
  #include <asm/sections.h>
-#include <linux/io.h>
 
Quoted 9 lines

Review line 23

Quoted 2 lines
(quoted diff) +0 -0
  #include "internal.h"
 

That was something I considered in the very beginning, but didn't have a chance to verify it works for all architectures. I can take a look after I'm finished with other v2 changes.

Quoted 2 lines

(allmodconfig on x86-64 builds fine with this; I have not done an extended sweep of other arches / build options).

I did a sweep for defconfigs for all arches and all were fine.

Karolina, please send the formal patch. Let's see how kbuild bot reacts.

Signature · 2 lines
Sincerely yours,
Mike.
maintainer
#23 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

Quoted 23 lines · Mike Rapoport
Quoted 22 lines · Karolina Drobnik
Quoted 9 lines · Matthew Wilcox
Quoted 1 line · Karolina Drobnik

Add a dummy io.h header.

Rather begs the question of what memblock.c needs from linux/io.h.

Wouldn't it be better to:

+++ b/mm/memblock.c

Review line 23

Quoted 7 lines
(quoted diff) +0 -1
@@ -18,7 +18,6 @@
  #include <linux/memblock.h>
 
  #include <asm/sections.h>
-#include <linux/io.h>
 
  #include "internal.h"
 

That was something I considered in the very beginning, but didn't have a chance to verify it works for all architectures. I can take a look after I'm finished with other v2 changes.

Quoted 4 lines
Quoted 2 lines
Quoted 2 lines

(allmodconfig on x86-64 builds fine with this; I have not done an extended sweep of other arches / build options).

I did a sweep for defconfigs for all arches and all were fine.

Thanks for doing the sweep, Mike.

I think I found a deeper problem which is masked due to our maze of header files:

include/asm-generic/io.h:#ifndef virt_to_phys include/asm-generic/io.h:#define virt_to_phys virt_to_phys

so there's an assumption that <asm/io.h> defines virt_to_phys(). You can see that in a number of architectures, eg:

arch/alpha/include/asm/io.h:static inline unsigned long virt_to_phys(volatile void *address) arch/ia64/include/asm/io.h:#define virt_to_phys virt_to_phys arch/mips/include/asm/io.h:#define virt_to_phys virt_to_phys arch/nios2/include/asm/io.h:#define virt_to_phys(vaddr) \
arch/parisc/include/asm/io.h:#define virt_to_phys(a) ((unsigned long)__pa(a)) arch/powerpc/include/asm/io.h:#define virt_to_phys virt_to_phys arch/sh/include/asm/io.h:#define virt_to_phys(address) ((unsigned long)(address)) arch/x86/include/asm/io.h:#define virt_to_phys virt_to_phys

That's clearly not the right place to define it. Two architectures put it in asm/memory.h:

arch/arm/include/asm/memory.h:#define virt_to_phys virt_to_phys arch/arm64/include/asm/memory.h:#define virt_to_phys virt_to_phys

then:

arch/m68k/include/asm/virtconvert.h:#define virt_to_phys virt_to_phys
arch/sparc/include/asm/page_32.h:#define virt_to_phys __pa
arch/sparc/include/asm/page_64.h:#define virt_to_phys __pa

This needs to be properly sorted out, but I don't want to tell Karolina that's now her job as a prerequisite for merging this patchset; that would be unfair.

Cc'ing Arnd. This is the kind of awful mess that he loves fixing ;-)

maintainer
#24 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

Quoted 52 lines · Matthew Wilcox
Quoted 23 lines · Mike Rapoport
Quoted 22 lines · Karolina Drobnik
Quoted 9 lines · Matthew Wilcox
Quoted 1 line · Karolina Drobnik

Add a dummy io.h header.

Rather begs the question of what memblock.c needs from linux/io.h.

Wouldn't it be better to:

+++ b/mm/memblock.c

Review line 23

Quoted 7 lines
(quoted diff) +0 -1
@@ -18,7 +18,6 @@
  #include <linux/memblock.h>
 
  #include <asm/sections.h>
-#include <linux/io.h>
 
  #include "internal.h"
 

That was something I considered in the very beginning, but didn't have a chance to verify it works for all architectures. I can take a look after I'm finished with other v2 changes.

Quoted 4 lines
Quoted 2 lines
Quoted 2 lines

(allmodconfig on x86-64 builds fine with this; I have not done an extended sweep of other arches / build options).

I did a sweep for defconfigs for all arches and all were fine.

Thanks for doing the sweep, Mike.

I think I found a deeper problem which is masked due to our maze of header files:

include/asm-generic/io.h:#ifndef virt_to_phys include/asm-generic/io.h:#define virt_to_phys virt_to_phys

so there's an assumption that <asm/io.h> defines virt_to_phys(). You can see that in a number of architectures, eg:

arch/alpha/include/asm/io.h:static inline unsigned long virt_to_phys(volatile void *address) arch/ia64/include/asm/io.h:#define virt_to_phys virt_to_phys arch/mips/include/asm/io.h:#define virt_to_phys virt_to_phys arch/nios2/include/asm/io.h:#define virt_to_phys(vaddr) \
arch/parisc/include/asm/io.h:#define virt_to_phys(a) ((unsigned long)__pa(a)) arch/powerpc/include/asm/io.h:#define virt_to_phys virt_to_phys arch/sh/include/asm/io.h:#define virt_to_phys(address) ((unsigned long)(address)) arch/x86/include/asm/io.h:#define virt_to_phys virt_to_phys

That's clearly not the right place to define it. Two architectures put it in asm/memory.h:

Quoted 8 lines

arch/arm/include/asm/memory.h:#define virt_to_phys virt_to_phys arch/arm64/include/asm/memory.h:#define virt_to_phys virt_to_phys

then:

arch/m68k/include/asm/virtconvert.h:#define virt_to_phys virt_to_phys
arch/sparc/include/asm/page_32.h:#define virt_to_phys __pa
arch/sparc/include/asm/page_64.h:#define virt_to_phys __pa

I'd say sparc picked the most appropriate header for it. memory.h could also work fine, but it's only present on some arches.
I'll take a deeper look, thanks for checking this.

Quoted 3 lines

This needs to be properly sorted out, but I don't want to tell Karolina that's now her job as a prerequisite for merging this patchset; that would be unfair.

Totally agree.

Quoted 1 line

Cc'ing Arnd. This is the kind of awful mess that he loves fixing ;-)

Heh, me too :)

Signature · 2 lines
Sincerely yours,
Mike.
#25 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

In reply to Matthew Wilcox

Hi Matthew,

Quoted 24 lines · Matthew Wilcox

I think I found a deeper problem which is masked due to our maze of header files:

include/asm-generic/io.h:#ifndef virt_to_phys include/asm-generic/io.h:#define virt_to_phys virt_to_phys

so there's an assumption that <asm/io.h> defines virt_to_phys(). You can see that in a number of architectures, eg:

arch/alpha/include/asm/io.h:static inline unsigned long virt_to_phys(volatile void *address) arch/ia64/include/asm/io.h:#define virt_to_phys virt_to_phys arch/mips/include/asm/io.h:#define virt_to_phys virt_to_phys arch/nios2/include/asm/io.h:#define virt_to_phys(vaddr) \
arch/parisc/include/asm/io.h:#define virt_to_phys(a) ((unsigned long)__pa(a))
arch/powerpc/include/asm/io.h:#define virt_to_phys virt_to_phys arch/sh/include/asm/io.h:#define virt_to_phys(address)  ((unsigned long)(address))
arch/x86/include/asm/io.h:#define virt_to_phys virt_to_phys

That's clearly not the right place to define it.  Two architectures put it in asm/memory.h:

Quoted 8 lines

arch/arm/include/asm/memory.h:#define virt_to_phys virt_to_phys arch/arm64/include/asm/memory.h:#define virt_to_phys virt_to_phys

then:

arch/m68k/include/asm/virtconvert.h:#define virt_to_phys virt_to_phys arch/sparc/include/asm/page_32.h:#define virt_to_phys           __pa arch/sparc/include/asm/page_64.h:#define virt_to_phys __pa

Oh, that's a lot...thank you for checking it. The simulator has its own mm stubs, so it shouldn't be impacted by such a clean up, correct?

Quoted 3 lines

This needs to be properly sorted out, but I don't want to tell Karolina that's now her job as a prerequisite for merging this patchset; that would be unfair.

Thanks. Also, I think I'm not be the best person to (efficiently) sort this out :)

All the best,
Karolina

#26 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

In reply to Matthew Wilcox
Quoted 32 lines · Matthew Wilcox

Thanks for doing the sweep, Mike.

I think I found a deeper problem which is masked due to our maze of header files:

include/asm-generic/io.h:#ifndef virt_to_phys include/asm-generic/io.h:#define virt_to_phys virt_to_phys

so there's an assumption that <asm/io.h> defines virt_to_phys(). You can see that in a number of architectures, eg:

arch/alpha/include/asm/io.h:static inline unsigned long virt_to_phys(volatile void *address) arch/ia64/include/asm/io.h:#define virt_to_phys virt_to_phys arch/mips/include/asm/io.h:#define virt_to_phys virt_to_phys arch/nios2/include/asm/io.h:#define virt_to_phys(vaddr) \
arch/parisc/include/asm/io.h:#define virt_to_phys(a) ((unsigned long)__pa(a)) arch/powerpc/include/asm/io.h:#define virt_to_phys virt_to_phys arch/sh/include/asm/io.h:#define virt_to_phys(address) ((unsigned long)(address)) arch/x86/include/asm/io.h:#define virt_to_phys virt_to_phys

That's clearly not the right place to define it. Two architectures put it in asm/memory.h:

arch/arm/include/asm/memory.h:#define virt_to_phys virt_to_phys arch/arm64/include/asm/memory.h:#define virt_to_phys virt_to_phys

then:

arch/m68k/include/asm/virtconvert.h:#define virt_to_phys virt_to_phys
arch/sparc/include/asm/page_32.h:#define virt_to_phys __pa
arch/sparc/include/asm/page_64.h:#define virt_to_phys __pa

Quoted 5 lines

This needs to be properly sorted out, but I don't want to tell Karolina that's now her job as a prerequisite for merging this patchset; that would be unfair.

Cc'ing Arnd. This is the kind of awful mess that he loves fixing ;-)

Adding Ingo as well. I'm in the middle of getting his fast-headers tree to work well on a couple of other architectures, and the memory.h/page.h/io.h mess is one of the tricky bits in there, both in his series and in my follow-ups.

What makes this bit even worse is that the architectures also not just inconsistent about where they put __va/__pa and virt_to_phys/phys_to_virt, they are also inconsistent about which of the two pairs is based on the other, so any way you touch it means you will break something, and changing it now will likely require a tricky rebase of Ingo's patches.

Ingo, do you happen to have patches already that could be isolated from your series to address this? Maybe we can add the linux/mm_page_address.h header first and require that each architecture puts these macros into asm/page_address.h. We need to isolate these anyway, because the page addresses are used in a lot of places that don't need to include any of the remaining headers (page.h, mm.h, memory.h, io.h) that pull in hundreds more.

Arnd

maintainer
#27 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

Quoted 45 lines · Arnd Bergmann
Quoted 32 lines · Matthew Wilcox

Thanks for doing the sweep, Mike.

I think I found a deeper problem which is masked due to our maze of header files:

include/asm-generic/io.h:#ifndef virt_to_phys include/asm-generic/io.h:#define virt_to_phys virt_to_phys

so there's an assumption that <asm/io.h> defines virt_to_phys(). You can see that in a number of architectures, eg:

arch/alpha/include/asm/io.h:static inline unsigned long virt_to_phys(volatile void *address) arch/ia64/include/asm/io.h:#define virt_to_phys virt_to_phys arch/mips/include/asm/io.h:#define virt_to_phys virt_to_phys arch/nios2/include/asm/io.h:#define virt_to_phys(vaddr) \
arch/parisc/include/asm/io.h:#define virt_to_phys(a) ((unsigned long)__pa(a)) arch/powerpc/include/asm/io.h:#define virt_to_phys virt_to_phys arch/sh/include/asm/io.h:#define virt_to_phys(address) ((unsigned long)(address)) arch/x86/include/asm/io.h:#define virt_to_phys virt_to_phys

That's clearly not the right place to define it. Two architectures put it in asm/memory.h:

arch/arm/include/asm/memory.h:#define virt_to_phys virt_to_phys arch/arm64/include/asm/memory.h:#define virt_to_phys virt_to_phys

then:

arch/m68k/include/asm/virtconvert.h:#define virt_to_phys virt_to_phys
arch/sparc/include/asm/page_32.h:#define virt_to_phys __pa
arch/sparc/include/asm/page_64.h:#define virt_to_phys __pa

Quoted 5 lines

This needs to be properly sorted out, but I don't want to tell Karolina that's now her job as a prerequisite for merging this patchset; that would be unfair.

Cc'ing Arnd. This is the kind of awful mess that he loves fixing ;-)

Adding Ingo as well. I'm in the middle of getting his fast-headers tree to work well on a couple of other architectures, and the memory.h/page.h/io.h mess is one of the tricky bits in there, both in his series and in my follow-ups.

Quoted 6 lines

What makes this bit even worse is that the architectures also not just inconsistent about where they put __va/__pa and virt_to_phys/phys_to_virt, they are also inconsistent about which of the two pairs is based on the other, so any way you touch it means you will break something, and changing it now will likely require a tricky rebase of Ingo's patches.

Hmm, whatever we'll do with these conversions, it will be tricky for Ingo's tree...

Quoted 8 lines

Ingo, do you happen to have patches already that could be isolated from your series to address this? Maybe we can add the linux/mm_page_address.h header first and require that each architecture puts these macros into asm/page_address.h. We need to isolate these anyway, because the page addresses are used in a lot of places that don't need to include any of the remaining headers (page.h, mm.h, memory.h, io.h) that pull in hundreds more.

I peeked at Ingo's tree and there is this:

commit 3426911a3f83 (headers/deps: io/arch: Move the address translation APIs from <asm/io.h> to <asm/io_extra.h>)
(https://git.kernel.org/pub/scm/linux/kernel/git/mingo/tip.git/commit/?id=3426911a3f833930d92f3aebe349f09a513375d9)

It moves virt_to_phys and phys_to_virt on x86 to a new header.

I actually liked m68k's name for a header with virt_to_phys/phys_to_virt definitions - virtconvert.h.

As an experiment I pulled out address translations from arch/arm/include/memory.h to arch/arm/include/virtconvert.h, it wasn't that bad:

 arch/arm/include/asm/dma-mapping.h |   2 +
 arch/arm/include/asm/io.h          |   1 +
 arch/arm/include/asm/memory.h      | 244 ----------------------------------
 arch/arm/include/asm/pgtable.h     |   1 +
 arch/arm/include/asm/virtconvert.h | 264 +++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/psci_smp.c         |   1 +
 6 files changed, 269 insertions(+), 244 deletions(-)

(https://git.kernel.org/rppt/linux/c/4c34ec16319fc85280aad89d7a74df845c1614fc)

Quoted 1 line

Arnd

Signature · 2 lines
Sincerely yours,
Mike.
#28 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

Quoted 18 lines · Mike Rapoport
Quoted 1 line · Arnd Bergmann

On Sun, Jan 30, 2022 at 6:53 PM Matthew Wilcox <[email protected]> wrote:

I actually liked m68k's name for a header with virt_to_phys/phys_to_virt definitions - virtconvert.h.

As an experiment I pulled out address translations from arch/arm/include/memory.h to arch/arm/include/virtconvert.h, it wasn't that bad:

 arch/arm/include/asm/dma-mapping.h |   2 +
 arch/arm/include/asm/io.h          |   1 +
 arch/arm/include/asm/memory.h      | 244 ----------------------------------
 arch/arm/include/asm/pgtable.h     |   1 +
 arch/arm/include/asm/virtconvert.h | 264 +++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/psci_smp.c         |   1 +
 6 files changed, 269 insertions(+), 244 deletions(-)
Quoted 1 line

(https://git.kernel.org/rppt/linux/c/4c34ec16319fc85280aad89d7a74df845c1614fc)

Right, that doesn't look too bad, especially since it seems you managed to avoid any further indirect inlcudes. Doing the same consistently for all architectures may end up a bit harder but would be a great help.

Arnd

#29 lore raw

Re: [PATCH 07/16] tools/include: Add io.h stub

In reply to Mike Rapoport

Hi Mike,

Quoted 26 lines · Mike Rapoport
Quoted 25 lines · Karolina Drobnik
Quoted 10 lines · Matthew Wilcox
Quoted 1 line · Karolina Drobnik

Add a dummy io.h header.

Rather begs the question of what memblock.c needs from linux/io.h.

Wouldn't it be better to:

+++ b/mm/memblock.c

Review line 23

Quoted 7 lines
(quoted diff) +0 -1
@@ -18,7 +18,6 @@
  #include <linux/memblock.h>
 
  #include <asm/sections.h>
-#include <linux/io.h>
 
  #include "internal.h"
 

That was something I considered in the very beginning, but didn't have
a chance to verify it works for all architectures. I can take a look
after I'm finished with other v2 changes.

Quoted 4 lines
Quoted 2 lines
Quoted 2 lines

(allmodconfig on x86-64 builds fine with this; I have not done an extended sweep of other arches / build options).

I did a sweep for defconfigs for all arches and all were fine.

Thanks for doing the sweep, it's very helpful.

Quoted 2 lines

Karolina, please send the formal patch. Let's see how kbuild bot reacts.

OK, will send something later today.

All the best,
Karolina

#30 lore raw

PATCH 8/16 tools/include: Add pfn.h stub

In reply to Karolina Drobnik

Add a stubbed pfn header with definitions used in testing.

---
 tools/include/linux/pfn.h | 10 ++++++++++
 1 file changed, 10 insertions(+)
 create mode 100644 tools/include/linux/pfn.h
tools/include/linux/pfn.h new file +10 -0
new file mode 100644
index 000000000000..7512a58189eb
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TOOLS_LINUX_PFN_H_
+#define _TOOLS_LINUX_PFN_H_
+
+#include <linux/mm.h>
+
+#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
Signature · 1 line
2.30.2
#31 lore raw

PATCH 9/16 tools/include: Add debugfs.h stub

In reply to Karolina Drobnik

Add a dummy debugfs.h header.

---
 tools/include/linux/debugfs.h | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 tools/include/linux/debugfs.h
tools/include/linux/debugfs.h new file +5 -0
new file mode 100644
index 000000000000..4ba06140b1be
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TOOLS_DEBUGFS_H
+#define _TOOLS_DEBUGFS_H
+
+#endif
Signature · 1 line
2.30.2
#32 lore raw

PATCH 10/16 memblock tests: Add skeleton of the memblock simulator

In reply to Karolina Drobnik

Add basic project files, together with local stubs of required headers. Update tools/include/slab.h to include definitions used by memblock.

---
 MAINTAINERS                                   |  1 +
 tools/include/linux/slab.h                    | 10 ++++
 tools/testing/memblock/.gitignore             |  6 +++
 tools/testing/memblock/Makefile               | 51 +++++++++++++++++++
 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, 280 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
MAINTAINERS +1 -0
index ea3e6c914384..602dc6e9ebad 100644
@@ -12402,6 +12402,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 <[email protected]>
tools/include/linux/slab.h +10 -0
index 07d7930d4003..f41d8a0eb1a4 100644
@@ -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);
tools/testing/memblock/.gitignore new file +6 -0
new file mode 100644
index 000000000000..5b80ece3d135
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+main
+memblock.c
+linux/memblock.h
+asm/cmpxchg.h
tools/testing/memblock/Makefile new file +51 -0

51 lines not on this page · raw message

tools/testing/memblock/asm/dma.h new file +5 -0
new file mode 100644
index 000000000000..13ff8e5d22ef
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TOOLS_DMA_H
+#define _TOOLS_DMA_H
+
+#endif
tools/testing/memblock/internal.h new file +12 -0
new file mode 100644
index 000000000000..94b52a8718b5
@@ -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
tools/testing/memblock/lib/slab.c new file +9 -0
new file mode 100644
index 000000000000..6be6020328fb
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/slab.h>
+
+enum slab_state slab_state;
+
+bool slab_is_available(void)
+{
+	return slab_state >= UP;
+}
tools/testing/memblock/linux/init.h new file +34 -0
new file mode 100644
index 000000000000..8c09788dfeb1
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_INIT_H
+#define _LINUX_INIT_H
+
+#include <linux/compiler.h>
+#include <asm/export.h>
+#include <linux/memory_hotplug.h>
+
+#define __section(section)              __attribute__((__section__(section)))
+
+#define __initconst	__section(".init.rodata")
+#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
tools/testing/memblock/linux/kernel.h new file +12 -0
new file mode 100644
index 000000000000..d2f148bd8902
@@ -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 <linux/errno.h>
+#include <string.h>
+#include <linux/printk.h>
+#include <linux/linkage.h>
+#include <linux/kconfig.h>
+
+#endif
tools/testing/memblock/linux/kmemleak.h new file +18 -0
new file mode 100644
index 000000000000..462f8c5e8aa0
@@ -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
tools/testing/memblock/linux/memory_hotplug.h new file +19 -0
new file mode 100644
index 000000000000..47988765a219
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_MEMORY_HOTPLUG_H
+#define _LINUX_MEMORY_HOTPLUG_H
+
+#include <linux/numa.h>
+#include <linux/pfn.h>
+#include <linux/cache.h>
+#include <linux/types.h>
+
+static inline bool movable_node_is_enabled(void)
+{
+#ifdef MOVABLE_NODE
+	return true;
+#else
+	return false;
+#endif
+}
+
+#endif
tools/testing/memblock/linux/mmzone.h new file +35 -0
new file mode 100644
index 000000000000..7c2eb5c9bb54
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TOOLS_MMZONE_H
+#define _TOOLS_MMZONE_H
+
+#include <linux/atomic.h>
+
+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
tools/testing/memblock/linux/printk.h new file +25 -0
new file mode 100644
index 000000000000..61af424d8c6c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _PRINTK_H
+#define _PRINTK_H
+
+#include <stdio.h>
+#include <asm/bug.h>
+
+/*
+ * 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
tools/testing/memblock/main.c new file +6 -0
new file mode 100644
index 000000000000..62958da35d0f
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+int main(int argc, char **argv)
+{
+	return 0;
+}
tools/testing/memblock/mmzone.c new file +20 -0
new file mode 100644
index 000000000000..7b0909e8b759
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/mmzone.h>
+
+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)
+{
+}
tools/testing/memblock/scripts/Makefile.include new file +17 -0
new file mode 100644
index 000000000000..699b0d6cda07
@@ -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
Signature · 1 line
2.30.2
maintainer
#33 lore raw

Re: [PATCH 10/16] memblock tests: Add skeleton of the memblock simulator

tools/testing/memblock/.gitignore line 6

Quoted 8 lines · Karolina Drobnik

+++ b/tools/testing/memblock/.gitignore

tools/testing/memblock/.gitignore new file +6 -0
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+main
+memblock.c
+linux/memblock.h
+asm/cmpxchg.h

Don't put SPDX information in a .gitignore file.

maintainer
#34 lore raw

Re: [PATCH 10/16] memblock tests: Add skeleton of the memblock simulator

In reply to Karolina Drobnik
Quoted 1 line · Karolina Drobnik

+# Memblock simulator requires AddressSanitizer (libasan) and liburcu libraries

Stale comment? You don't seem to actually use liburcu.

#35 lore raw

Re: [PATCH 10/16] memblock tests: Add skeleton of the memblock simulator

Quoted 5 lines · Matthew Wilcox
Quoted 2 lines · Karolina Drobnik

+# Memblock simulator requires AddressSanitizer (libasan) and liburcu libraries

Stale comment?  You don't seem to actually use liburcu.

I'm using uatomic_inc and uatomic_dec in tools/lib/slab.c. But, you're right, memblock simulator doesn't use liburcu per se but uses code that requires it. I wasn't sure where and how to communicate it, so I added this comment here.

maintainer
#36 lore raw

Re: [PATCH 10/16] memblock tests: Add skeleton of the memblock simulator

Quoted 11 lines · Karolina Drobnik
Quoted 5 lines · Matthew Wilcox
Quoted 2 lines · Karolina Drobnik

+# Memblock simulator requires AddressSanitizer (libasan) and liburcu libraries

Stale comment?  You don't seem to actually use liburcu.

I'm using uatomic_inc and uatomic_dec in tools/lib/slab.c. But, you're right, memblock simulator doesn't use liburcu per se but uses code that requires it. I wasn't sure where and how to communicate it, so I added this comment here.

I thought the comment was stale because you don't link against liburcu. Maybe you can use uatomic_inc() without adding -lurcu to the link step.

#37 lore raw

PATCH 11/16 memblock tests: Add memblock reset function

In reply to Karolina Drobnik

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.

---
 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
tools/testing/memblock/Makefile +3 -1
index 35105923f235..31c4ef625ede 100644
@@ -5,7 +5,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)
tools/testing/memblock/main.c +2 -0
index 62958da35d0f..da65b0adee91 100644
@@ -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;
 }
tools/testing/memblock/tests/basic_api.c new file +32 -0
new file mode 100644
index 000000000000..7f2597b3dd4d
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <string.h>
+#include <linux/memblock.h>
+#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;
+}
tools/testing/memblock/tests/basic_api.h new file +10 -0
new file mode 100644
index 000000000000..1ceecfca1f47
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _MEMBLOCK_BASIC_H
+#define _MEMBLOCK_BASIC_H
+
+#include <assert.h>
+#include "common.h"
+
+int memblock_basic_checks(void);
+
+#endif
tools/testing/memblock/tests/common.c new file +27 -0
new file mode 100644
index 000000000000..03de6eab0c3c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "tests/common.h"
+#include <string.h>
+
+#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;
+}
tools/testing/memblock/tests/common.h new file +15 -0
new file mode 100644
index 000000000000..48efc4270ea1
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _MEMBLOCK_TEST_H
+#define _MEMBLOCK_TEST_H
+
+#include <linux/types.h>
+#include <linux/memblock.h>
+
+struct region {
+	phys_addr_t base;
+	phys_addr_t size;
+};
+
+void reset_memblock(void);
+
+#endif
Signature · 1 line
2.30.2
#38 lore raw

PATCH 12/16 memblock tests: Add memblock_add tests

In reply to Karolina Drobnik

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.

---
 tools/testing/memblock/tests/basic_api.c | 215 +++++++++++++++++++++++
 1 file changed, 215 insertions(+)
tools/testing/memblock/tests/basic_api.c +215 -0
index 7f2597b3dd4d..6c047b7b31ab 100644
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 #include <string.h>
 #include <linux/memblock.h>
+#include <linux/sizes.h>
 #include "basic_api.h"
 
 #define EXPECTED_MEMBLOCK_REGIONS			128
@@ -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;
 }
Signature · 1 line
2.30.2
#39 lore raw

PATCH 13/16 memblock tests: Add memblock_reserve tests

In reply to Karolina Drobnik

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

---
 tools/testing/memblock/tests/basic_api.c | 213 +++++++++++++++++++++++
 1 file changed, 213 insertions(+)
tools/testing/memblock/tests/basic_api.c +213 -0
index 6c047b7b31ab..b055ff262d23 100644
@@ -239,9 +239,222 @@ 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;
 }
Signature · 1 line
2.30.2
#40 lore raw

PATCH 14/16 memblock tests: Add memblock_remove tests

In reply to Karolina Drobnik

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

---
 tools/testing/memblock/tests/basic_api.c | 203 +++++++++++++++++++++++
 1 file changed, 203 insertions(+)
tools/testing/memblock/tests/basic_api.c +203 -0
index b055ff262d23..96af80bf9df9 100644
@@ -450,11 +450,214 @@ 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;
 }
Signature · 1 line
2.30.2
#41 lore raw

PATCH 15/16 memblock tests: Add memblock_add_node test

In reply to Karolina Drobnik

Add a simple test for NUMA-aware variant of memblock_add function.

---
 tools/testing/memblock/tests/basic_api.c | 34 ++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
tools/testing/memblock/tests/basic_api.c +34 -0
index 96af80bf9df9..02eb88358a58 100644
@@ -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 collection
@@ -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();
Signature · 1 line
2.30.2
#42 lore raw

PATCH 16/16 memblock tests: Add memblock_free tests

In reply to Karolina Drobnik

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

---
 tools/testing/memblock/tests/basic_api.c | 199 +++++++++++++++++++++++
 1 file changed, 199 insertions(+)
tools/testing/memblock/tests/basic_api.c +199 -0
index 02eb88358a58..568b05b52883 100644
@@ -686,12 +686,211 @@ 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;
 }
Signature · 1 line
2.30.2