memblock: test suite and a small cleanup
* A small cleanup of unused variable in __next_mem_pfn_range_in_zone * Initial test suite to simulate memblock behaviour in userspace -----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEeOVYVaWZL5900a/pOQOGJssO/ZEFAmI9bD4THHJwcHRAbGlu dXguaWJtLmNvbQAKCRA5A4Ymyw79kXwhB/wNXR1wUb/eD3eKD+aNa2KMY5+8csjD ghJph8wQmM9U9hsLViv3/M/H5+bY/s0riZNulKYrcmzW2BgIzF2ebcoqgfQ89YGV bLx7lMJGxG/lCglur9m6KnOF89//Owq6Vfk7Jd6jR/F+43JO/3+5siCbTo6NrbVw 3DjT/WzvaICA646foyFTh8WotnIRbB2iYX1k/vIA3gwJ2C6n7WwoKzxU3ulKMUzg hVlhcuTVnaV4mjFBbl23wC7i4l9dgPO9M4ZrTtlEsNHeV6uoFYRObwy6/q/CsBqI avwgV0bQDch+QuCteUXcqIcnBpcUAfGxgiqp2PYX4lXA4gYTbo7plTna =IemP -----END PGP SIGNATURE----- Merge tag 'memblock-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock Pull memblock updates from Mike Rapoport: "Test suite and a small cleanup: - A small cleanup of unused variable in __next_mem_pfn_range_in_zone - Initial test suite to simulate memblock behaviour in userspace" * tag 'memblock-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock: (27 commits) memblock tests: Add TODO and README files memblock tests: Add memblock_alloc_try_nid tests for bottom up memblock tests: Add memblock_alloc_try_nid tests for top down memblock tests: Add memblock_alloc_from tests for bottom up memblock tests: Add memblock_alloc_from tests for top down memblock tests: Add memblock_alloc tests for bottom up memblock tests: Add memblock_alloc tests for top down memblock tests: Add simulation of physical memory memblock tests: Split up reset_memblock function memblock tests: Fix testing with 32-bit physical addresses memblock: __next_mem_pfn_range_in_zone: remove unneeded local variable nid memblock tests: Add memblock_free tests memblock tests: Add memblock_add_node test memblock tests: Add memblock_remove tests memblock tests: Add memblock_reserve tests memblock tests: Add memblock_add tests memblock tests: Add memblock reset function memblock tests: Add skeleton of the memblock simulator tools/include: Add debugfs.h stub tools/include: Add pfn.h stub ...
This commit is contained in:
commit
02f9a04d76
42 changed files with 3934 additions and 71 deletions
|
@ -12550,6 +12550,7 @@ S: Maintained
|
||||||
F: Documentation/core-api/boot-time-mm.rst
|
F: Documentation/core-api/boot-time-mm.rst
|
||||||
F: include/linux/memblock.h
|
F: include/linux/memblock.h
|
||||||
F: mm/memblock.c
|
F: mm/memblock.c
|
||||||
|
F: tools/testing/memblock/
|
||||||
|
|
||||||
MEMORY CONTROLLER DRIVERS
|
MEMORY CONTROLLER DRIVERS
|
||||||
M: Krzysztof Kozlowski <krzk@kernel.org>
|
M: Krzysztof Kozlowski <krzk@kernel.org>
|
||||||
|
|
|
@ -1284,11 +1284,10 @@ __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone,
|
||||||
{
|
{
|
||||||
int zone_nid = zone_to_nid(zone);
|
int zone_nid = zone_to_nid(zone);
|
||||||
phys_addr_t spa, epa;
|
phys_addr_t spa, epa;
|
||||||
int nid;
|
|
||||||
|
|
||||||
__next_mem_range(idx, zone_nid, MEMBLOCK_NONE,
|
__next_mem_range(idx, zone_nid, MEMBLOCK_NONE,
|
||||||
&memblock.memory, &memblock.reserved,
|
&memblock.memory, &memblock.reserved,
|
||||||
&spa, &epa, &nid);
|
&spa, &epa, NULL);
|
||||||
|
|
||||||
while (*idx != U64_MAX) {
|
while (*idx != U64_MAX) {
|
||||||
unsigned long epfn = PFN_DOWN(epa);
|
unsigned long epfn = PFN_DOWN(epa);
|
||||||
|
@ -1315,7 +1314,7 @@ __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone,
|
||||||
|
|
||||||
__next_mem_range(idx, zone_nid, MEMBLOCK_NONE,
|
__next_mem_range(idx, zone_nid, MEMBLOCK_NONE,
|
||||||
&memblock.memory, &memblock.reserved,
|
&memblock.memory, &memblock.reserved,
|
||||||
&spa, &epa, &nid);
|
&spa, &epa, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* signal end of iteration */
|
/* signal end of iteration */
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <asm/atomic.h>
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
|
void atomic_long_set(atomic_long_t *v, long i);
|
||||||
|
|
||||||
/* atomic_cmpxchg_relaxed */
|
/* atomic_cmpxchg_relaxed */
|
||||||
#ifndef atomic_cmpxchg_relaxed
|
#ifndef atomic_cmpxchg_relaxed
|
||||||
#define atomic_cmpxchg_relaxed atomic_cmpxchg
|
#define atomic_cmpxchg_relaxed atomic_cmpxchg
|
||||||
|
|
10
tools/include/linux/cache.h
Normal file
10
tools/include/linux/cache.h
Normal file
|
@ -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
|
5
tools/include/linux/debugfs.h
Normal file
5
tools/include/linux/debugfs.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _TOOLS_DEBUGFS_H
|
||||||
|
#define _TOOLS_DEBUGFS_H
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,4 +1,32 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef _TOOLS_INCLUDE_LINUX_GFP_H
|
#ifndef _TOOLS_INCLUDE_LINUX_GFP_H
|
||||||
#define _TOOLS_INCLUDE_LINUX_GFP_H
|
#define _TOOLS_INCLUDE_LINUX_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 /* _TOOLS_INCLUDE_LINUX_GFP_H */
|
#endif /* _TOOLS_INCLUDE_LINUX_GFP_H */
|
||||||
|
|
5
tools/include/linux/io.h
Normal file
5
tools/include/linux/io.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _TOOLS_IO_H
|
||||||
|
#define _TOOLS_IO_H
|
||||||
|
|
||||||
|
#endif
|
|
@ -15,6 +15,8 @@
|
||||||
#define UINT_MAX (~0U)
|
#define UINT_MAX (~0U)
|
||||||
#endif
|
#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(x, a) __PERF_ALIGN_MASK(x, (typeof(x))(a)-1)
|
||||||
#define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
|
#define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
|
||||||
|
|
||||||
|
@ -51,6 +53,10 @@
|
||||||
_min1 < _min2 ? _min1 : _min2; })
|
_min1 < _min2 ? _min1 : _min2; })
|
||||||
#endif
|
#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
|
#ifndef BUG_ON
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define BUG_ON(cond) do { if (cond) {} } while (0)
|
#define BUG_ON(cond) do { if (cond) {} } while (0)
|
||||||
|
|
42
tools/include/linux/mm.h
Normal file
42
tools/include/linux/mm.h
Normal file
|
@ -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
|
10
tools/include/linux/pfn.h
Normal file
10
tools/include/linux/pfn.h
Normal file
|
@ -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
|
|
@ -1,20 +1,31 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef SLAB_H
|
#ifndef _TOOLS_SLAB_H
|
||||||
#define SLAB_H
|
#define _TOOLS_SLAB_H
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
|
|
||||||
#define SLAB_HWCACHE_ALIGN 1
|
|
||||||
#define SLAB_PANIC 2
|
#define SLAB_PANIC 2
|
||||||
#define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */
|
#define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */
|
||||||
|
|
||||||
void *kmalloc(size_t size, gfp_t);
|
#define kzalloc_node(size, flags, node) kmalloc(size, flags)
|
||||||
void kfree(void *);
|
|
||||||
|
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)
|
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);
|
void *kmem_cache_alloc(struct kmem_cache *cachep, int flags);
|
||||||
|
@ -24,4 +35,4 @@ struct kmem_cache *kmem_cache_create(const char *name, unsigned int size,
|
||||||
unsigned int align, unsigned int flags,
|
unsigned int align, unsigned int flags,
|
||||||
void (*ctor)(void *));
|
void (*ctor)(void *));
|
||||||
|
|
||||||
#endif /* SLAB_H */
|
#endif /* _TOOLS_SLAB_H */
|
|
@ -63,10 +63,20 @@ typedef __u64 __bitwise __be64;
|
||||||
typedef __u16 __bitwise __sum16;
|
typedef __u16 __bitwise __sum16;
|
||||||
typedef __u32 __bitwise __wsum;
|
typedef __u32 __bitwise __wsum;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||||
|
typedef u64 phys_addr_t;
|
||||||
|
#else
|
||||||
|
typedef u32 phys_addr_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int counter;
|
int counter;
|
||||||
} atomic_t;
|
} atomic_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long counter;
|
||||||
|
} atomic_long_t;
|
||||||
|
|
||||||
#ifndef __aligned_u64
|
#ifndef __aligned_u64
|
||||||
# define __aligned_u64 __u64 __attribute__((aligned(8)))
|
# define __aligned_u64 __u64 __attribute__((aligned(8)))
|
||||||
#endif
|
#endif
|
||||||
|
|
38
tools/lib/slab.c
Normal file
38
tools/lib/slab.c
Normal file
|
@ -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);
|
||||||
|
}
|
4
tools/testing/memblock/.gitignore
vendored
Normal file
4
tools/testing/memblock/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
main
|
||||||
|
memblock.c
|
||||||
|
linux/memblock.h
|
||||||
|
asm/cmpxchg.h
|
55
tools/testing/memblock/Makefile
Normal file
55
tools/testing/memblock/Makefile
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# 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
|
||||||
|
TEST_OFILES = tests/alloc_nid_api.o tests/alloc_helpers_api.o tests/alloc_api.o \
|
||||||
|
tests/basic_api.o tests/common.o
|
||||||
|
DEP_OFILES = memblock.o lib/slab.o mmzone.o slab.o
|
||||||
|
OFILES = main.o $(DEP_OFILES) $(TEST_OFILES)
|
||||||
|
EXTR_SRC = ../../../mm/memblock.c
|
||||||
|
|
||||||
|
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
|
107
tools/testing/memblock/README
Normal file
107
tools/testing/memblock/README
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
==================
|
||||||
|
Memblock simulator
|
||||||
|
==================
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
Memblock is a boot time memory allocator[1] that manages memory regions before
|
||||||
|
the actual memory management is initialized. Its APIs allow to register physical
|
||||||
|
memory regions, mark them as available or reserved, allocate a block of memory
|
||||||
|
within the requested range and/or in specific NUMA node, and many more.
|
||||||
|
|
||||||
|
Because it is used so early in the booting process, testing and debugging it is
|
||||||
|
difficult. This test suite, usually referred as memblock simulator, is
|
||||||
|
an attempt at testing the memblock mechanism. It runs one monolithic test that
|
||||||
|
consist of a series of checks that exercise both the basic operations and
|
||||||
|
allocation functionalities of memblock. The main data structure of the boot time
|
||||||
|
memory allocator is initialized at the build time, so the checks here reuse its
|
||||||
|
instance throughout the duration of the test. To ensure that tests don't affect
|
||||||
|
each other, region arrays are reset in between.
|
||||||
|
|
||||||
|
As this project uses the actual memblock code and has to run in user space,
|
||||||
|
some of the kernel definitions were stubbed by the initial commit that
|
||||||
|
introduced memblock simulator (commit 16802e55dea9 ("memblock tests: Add
|
||||||
|
skeleton of the memblock simulator")) and a few preparation commits just
|
||||||
|
before it. Most of them don't match the kernel implementation, so one should
|
||||||
|
consult them first before making any significant changes to the project.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
To run the tests, build the main target and run it:
|
||||||
|
|
||||||
|
$ make && ./main
|
||||||
|
|
||||||
|
A successful run produces no output. It is also possible to override different
|
||||||
|
configuration parameters. For example, to simulate enabled NUMA, use:
|
||||||
|
|
||||||
|
$ make NUMA=1
|
||||||
|
|
||||||
|
For the full list of options, see `make help`.
|
||||||
|
|
||||||
|
Project structure
|
||||||
|
=================
|
||||||
|
|
||||||
|
The project has one target, main, which calls a group of checks for basic and
|
||||||
|
allocation functions. Tests for each group are defined in dedicated files, as it
|
||||||
|
can be seen here:
|
||||||
|
|
||||||
|
memblock
|
||||||
|
|-- asm ------------------,
|
||||||
|
|-- lib |-- implement function and struct stubs
|
||||||
|
|-- linux ------------------'
|
||||||
|
|-- scripts
|
||||||
|
| |-- Makefile.include -- handles `make` parameters
|
||||||
|
|-- tests
|
||||||
|
| |-- alloc_api.(c|h) -- memblock_alloc tests
|
||||||
|
| |-- alloc_helpers_api.(c|h) -- memblock_alloc_from tests
|
||||||
|
| |-- alloc_nid_api.(c|h) -- memblock_alloc_try_nid tests
|
||||||
|
| |-- basic_api.(c|h) -- memblock_add/memblock_reserve/... tests
|
||||||
|
| |-- common.(c|h) -- helper functions for resetting memblock;
|
||||||
|
|-- main.c --------------. dummy physical memory definition
|
||||||
|
|-- Makefile `- test runner
|
||||||
|
|-- README
|
||||||
|
|-- TODO
|
||||||
|
|-- .gitignore
|
||||||
|
|
||||||
|
Simulating physical memory
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Some allocation functions clear the memory in the process, so it is required for
|
||||||
|
memblock to track valid memory ranges. To achieve this, the test suite registers
|
||||||
|
with memblock memory stored by test_memory struct. It is a small wrapper that
|
||||||
|
points to a block of memory allocated via malloc. For each group of allocation
|
||||||
|
tests, dummy physical memory is allocated, added to memblock, and then released
|
||||||
|
at the end of the test run. The structure of a test runner checking allocation
|
||||||
|
functions is as follows:
|
||||||
|
|
||||||
|
int memblock_alloc_foo_checks(void)
|
||||||
|
{
|
||||||
|
reset_memblock_attributes(); /* data structure reset */
|
||||||
|
dummy_physical_memory_init(); /* allocate and register memory */
|
||||||
|
|
||||||
|
(...allocation checks...)
|
||||||
|
|
||||||
|
dummy_physical_memory_cleanup(); /* free the memory */
|
||||||
|
}
|
||||||
|
|
||||||
|
There's no need to explicitly free the dummy memory from memblock via
|
||||||
|
memblock_free() call. The entry will be erased by reset_memblock_regions(),
|
||||||
|
called at the beginning of each test.
|
||||||
|
|
||||||
|
Known issues
|
||||||
|
============
|
||||||
|
|
||||||
|
1. Requesting a specific NUMA node via memblock_alloc_node() does not work as
|
||||||
|
intended. Once the fix is in place, tests for this function can be added.
|
||||||
|
|
||||||
|
2. Tests for memblock_alloc_low() can't be easily implemented. The function uses
|
||||||
|
ARCH_LOW_ADDRESS_LIMIT marco, which can't be changed to point at the low
|
||||||
|
memory of the memory_block.
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
1. Boot time memory management documentation page:
|
||||||
|
https://www.kernel.org/doc/html/latest/core-api/boot-time-mm.html
|
28
tools/testing/memblock/TODO
Normal file
28
tools/testing/memblock/TODO
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
TODO
|
||||||
|
=====
|
||||||
|
|
||||||
|
1. Add verbose output (e.g., what is being tested and how many tests cases are
|
||||||
|
passing)
|
||||||
|
|
||||||
|
2. Add flags to Makefile:
|
||||||
|
+ verbosity level
|
||||||
|
+ enable memblock_dbg() messages (i.e. pass "-D CONFIG_DEBUG_MEMORY_INIT"
|
||||||
|
flag)
|
||||||
|
|
||||||
|
3. Add tests trying to memblock_add() or memblock_reserve() 129th region.
|
||||||
|
This will trigger memblock_double_array(), make sure it succeeds.
|
||||||
|
*Important:* These tests require valid memory ranges, use dummy physical
|
||||||
|
memory block from common.c to implement them. It is also very
|
||||||
|
likely that the current MEM_SIZE won't be enough for these
|
||||||
|
test cases. Use realloc to adjust the size accordingly.
|
||||||
|
|
||||||
|
4. Add test cases using this functions (implement them for both directions):
|
||||||
|
+ memblock_alloc_raw()
|
||||||
|
+ memblock_alloc_exact_nid_raw()
|
||||||
|
+ memblock_alloc_try_nid_raw()
|
||||||
|
|
||||||
|
5. Add tests for memblock_alloc_node() to check if the correct NUMA node is set
|
||||||
|
for the new region
|
||||||
|
|
||||||
|
6. Update comments in tests/basic_api.c to match the style used in
|
||||||
|
tests/alloc_*.c
|
5
tools/testing/memblock/asm/dma.h
Normal file
5
tools/testing/memblock/asm/dma.h
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _TOOLS_DMA_H
|
||||||
|
#define _TOOLS_DMA_H
|
||||||
|
|
||||||
|
#endif
|
12
tools/testing/memblock/internal.h
Normal file
12
tools/testing/memblock/internal.h
Normal file
|
@ -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
|
9
tools/testing/memblock/lib/slab.c
Normal file
9
tools/testing/memblock/lib/slab.c
Normal file
|
@ -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;
|
||||||
|
}
|
34
tools/testing/memblock/linux/init.h
Normal file
34
tools/testing/memblock/linux/init.h
Normal file
|
@ -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
|
||||||
|
#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
|
12
tools/testing/memblock/linux/kernel.h
Normal file
12
tools/testing/memblock/linux/kernel.h
Normal file
|
@ -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
|
18
tools/testing/memblock/linux/kmemleak.h
Normal file
18
tools/testing/memblock/linux/kmemleak.h
Normal file
|
@ -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
|
19
tools/testing/memblock/linux/memory_hotplug.h
Normal file
19
tools/testing/memblock/linux/memory_hotplug.h
Normal file
|
@ -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
|
35
tools/testing/memblock/linux/mmzone.h
Normal file
35
tools/testing/memblock/linux/mmzone.h
Normal file
|
@ -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
|
25
tools/testing/memblock/linux/printk.h
Normal file
25
tools/testing/memblock/linux/printk.h
Normal file
|
@ -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
|
15
tools/testing/memblock/main.c
Normal file
15
tools/testing/memblock/main.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#include "tests/basic_api.h"
|
||||||
|
#include "tests/alloc_api.h"
|
||||||
|
#include "tests/alloc_helpers_api.h"
|
||||||
|
#include "tests/alloc_nid_api.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
memblock_basic_checks();
|
||||||
|
memblock_alloc_checks();
|
||||||
|
memblock_alloc_helpers_checks();
|
||||||
|
memblock_alloc_nid_checks();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
20
tools/testing/memblock/mmzone.c
Normal file
20
tools/testing/memblock/mmzone.c
Normal file
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
19
tools/testing/memblock/scripts/Makefile.include
Normal file
19
tools/testing/memblock/scripts/Makefile.include
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# 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.
|
||||||
|
# Remember to install 32-bit version of dependencies.
|
||||||
|
ifeq ($(32BIT_PHYS_ADDR_T), 1)
|
||||||
|
CFLAGS += -m32 -U CONFIG_PHYS_ADDR_T_64BIT
|
||||||
|
LDFLAGS += -m32
|
||||||
|
endif
|
750
tools/testing/memblock/tests/alloc_api.c
Normal file
750
tools/testing/memblock/tests/alloc_api.c
Normal file
|
@ -0,0 +1,750 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#include "alloc_api.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simple test that tries to allocate a small memory region.
|
||||||
|
* Expect to allocate an aligned region near the end of the available memory.
|
||||||
|
*/
|
||||||
|
static int alloc_top_down_simple_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t size = SZ_2;
|
||||||
|
phys_addr_t expected_start;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
expected_start = memblock_end_of_DRAM() - SMP_CACHE_BYTES;
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == size);
|
||||||
|
assert(rgn->base == expected_start);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory next to a reserved region that starts at
|
||||||
|
* the misaligned address. Expect to create two separate entries, with the new
|
||||||
|
* entry aligned to the provided alignment:
|
||||||
|
*
|
||||||
|
* +
|
||||||
|
* | +--------+ +--------|
|
||||||
|
* | | rgn2 | | rgn1 |
|
||||||
|
* +------------+--------+---------+--------+
|
||||||
|
* ^
|
||||||
|
* |
|
||||||
|
* Aligned address boundary
|
||||||
|
*
|
||||||
|
* The allocation direction is top-down and region arrays are sorted from lower
|
||||||
|
* to higher addresses, so the new region will be the first entry in
|
||||||
|
* memory.reserved array. The previously reserved region does not get modified.
|
||||||
|
* Region counter and total size get updated.
|
||||||
|
*/
|
||||||
|
static int alloc_top_down_disjoint_check(void)
|
||||||
|
{
|
||||||
|
/* After allocation, this will point to the "old" region */
|
||||||
|
struct memblock_region *rgn1 = &memblock.reserved.regions[1];
|
||||||
|
struct memblock_region *rgn2 = &memblock.reserved.regions[0];
|
||||||
|
struct region r1;
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r2_size = SZ_16;
|
||||||
|
/* Use custom alignment */
|
||||||
|
phys_addr_t alignment = SMP_CACHE_BYTES * 2;
|
||||||
|
phys_addr_t total_size;
|
||||||
|
phys_addr_t expected_start;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
r1.base = memblock_end_of_DRAM() - SZ_2;
|
||||||
|
r1.size = SZ_2;
|
||||||
|
|
||||||
|
total_size = r1.size + r2_size;
|
||||||
|
expected_start = memblock_end_of_DRAM() - alignment;
|
||||||
|
|
||||||
|
memblock_reserve(r1.base, r1.size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r2_size, alignment);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn1->size == r1.size);
|
||||||
|
assert(rgn1->base == r1.base);
|
||||||
|
|
||||||
|
assert(rgn2->size == r2_size);
|
||||||
|
assert(rgn2->base == expected_start);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 2);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there is enough space at the end
|
||||||
|
* of the previously reserved block (i.e. first fit):
|
||||||
|
*
|
||||||
|
* | +--------+--------------|
|
||||||
|
* | | r1 | r2 |
|
||||||
|
* +--------------+--------+--------------+
|
||||||
|
*
|
||||||
|
* Expect a merge of both regions. Only the region size gets updated.
|
||||||
|
*/
|
||||||
|
static int alloc_top_down_before_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The first region ends at the aligned address to test region merging
|
||||||
|
*/
|
||||||
|
phys_addr_t r1_size = SMP_CACHE_BYTES;
|
||||||
|
phys_addr_t r2_size = SZ_512;
|
||||||
|
phys_addr_t total_size = r1_size + r2_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
memblock_reserve(memblock_end_of_DRAM() - total_size, r1_size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == total_size);
|
||||||
|
assert(rgn->base == memblock_end_of_DRAM() - total_size);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there is not enough space at the
|
||||||
|
* end of the previously reserved block (i.e. second fit):
|
||||||
|
*
|
||||||
|
* | +-----------+------+ |
|
||||||
|
* | | r2 | r1 | |
|
||||||
|
* +------------+-----------+------+-----+
|
||||||
|
*
|
||||||
|
* Expect a merge of both regions. Both the base address and size of the region
|
||||||
|
* get updated.
|
||||||
|
*/
|
||||||
|
static int alloc_top_down_after_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
struct region r1;
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r2_size = SZ_512;
|
||||||
|
phys_addr_t total_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The first region starts at the aligned address to test region merging
|
||||||
|
*/
|
||||||
|
r1.base = memblock_end_of_DRAM() - SMP_CACHE_BYTES;
|
||||||
|
r1.size = SZ_8;
|
||||||
|
|
||||||
|
total_size = r1.size + r2_size;
|
||||||
|
|
||||||
|
memblock_reserve(r1.base, r1.size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == total_size);
|
||||||
|
assert(rgn->base == r1.base - r2_size);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there are two reserved regions with
|
||||||
|
* a gap too small to fit the new region:
|
||||||
|
*
|
||||||
|
* | +--------+----------+ +------|
|
||||||
|
* | | r3 | r2 | | r1 |
|
||||||
|
* +-------+--------+----------+---+------+
|
||||||
|
*
|
||||||
|
* Expect to allocate a region before the one that starts at the lower address,
|
||||||
|
* and merge them into one. The region counter and total size fields get
|
||||||
|
* updated.
|
||||||
|
*/
|
||||||
|
static int alloc_top_down_second_fit_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
struct region r1, r2;
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r3_size = SZ_1K;
|
||||||
|
phys_addr_t total_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
r1.base = memblock_end_of_DRAM() - SZ_512;
|
||||||
|
r1.size = SZ_512;
|
||||||
|
|
||||||
|
r2.base = r1.base - SZ_512;
|
||||||
|
r2.size = SZ_256;
|
||||||
|
|
||||||
|
total_size = r1.size + r2.size + r3_size;
|
||||||
|
|
||||||
|
memblock_reserve(r1.base, r1.size);
|
||||||
|
memblock_reserve(r2.base, r2.size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == r2.size + r3_size);
|
||||||
|
assert(rgn->base == r2.base - r3_size);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 2);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there are two reserved regions with
|
||||||
|
* a gap big enough to accommodate the new region:
|
||||||
|
*
|
||||||
|
* | +--------+--------+--------+ |
|
||||||
|
* | | r2 | r3 | r1 | |
|
||||||
|
* +-----+--------+--------+--------+-----+
|
||||||
|
*
|
||||||
|
* Expect to merge all of them, creating one big entry in memblock.reserved
|
||||||
|
* array. The region counter and total size fields get updated.
|
||||||
|
*/
|
||||||
|
static int alloc_in_between_generic_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
struct region r1, r2;
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t gap_size = SMP_CACHE_BYTES;
|
||||||
|
phys_addr_t r3_size = SZ_64;
|
||||||
|
/*
|
||||||
|
* Calculate regions size so there's just enough space for the new entry
|
||||||
|
*/
|
||||||
|
phys_addr_t rgn_size = (MEM_SIZE - (2 * gap_size + r3_size)) / 2;
|
||||||
|
phys_addr_t total_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
r1.size = rgn_size;
|
||||||
|
r1.base = memblock_end_of_DRAM() - (gap_size + rgn_size);
|
||||||
|
|
||||||
|
r2.size = rgn_size;
|
||||||
|
r2.base = memblock_start_of_DRAM() + gap_size;
|
||||||
|
|
||||||
|
total_size = r1.size + r2.size + r3_size;
|
||||||
|
|
||||||
|
memblock_reserve(r1.base, r1.size);
|
||||||
|
memblock_reserve(r2.base, r2.size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == total_size);
|
||||||
|
assert(rgn->base == r1.base - r2.size - r3_size);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when the memory is filled with reserved
|
||||||
|
* regions with memory gaps too small to fit the new region:
|
||||||
|
*
|
||||||
|
* +-------+
|
||||||
|
* | new |
|
||||||
|
* +--+----+
|
||||||
|
* | +-----+ +-----+ +-----+ |
|
||||||
|
* | | res | | res | | res | |
|
||||||
|
* +----+-----+----+-----+----+-----+----+
|
||||||
|
*
|
||||||
|
* Expect no allocation to happen.
|
||||||
|
*/
|
||||||
|
static int alloc_small_gaps_generic_check(void)
|
||||||
|
{
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t region_size = SZ_1K;
|
||||||
|
phys_addr_t gap_size = SZ_256;
|
||||||
|
phys_addr_t region_end;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
region_end = memblock_start_of_DRAM();
|
||||||
|
|
||||||
|
while (region_end < memblock_end_of_DRAM()) {
|
||||||
|
memblock_reserve(region_end + gap_size, region_size);
|
||||||
|
region_end += gap_size + region_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(region_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(!allocated_ptr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when all memory is reserved.
|
||||||
|
* Expect no allocation to happen.
|
||||||
|
*/
|
||||||
|
static int alloc_all_reserved_generic_check(void)
|
||||||
|
{
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
/* Simulate full memory */
|
||||||
|
memblock_reserve(memblock_start_of_DRAM(), MEM_SIZE);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(SZ_256, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(!allocated_ptr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when the memory is almost full,
|
||||||
|
* with not enough space left for the new region:
|
||||||
|
*
|
||||||
|
* +-------+
|
||||||
|
* | new |
|
||||||
|
* +-------+
|
||||||
|
* |-----------------------------+ |
|
||||||
|
* | reserved | |
|
||||||
|
* +-----------------------------+---+
|
||||||
|
*
|
||||||
|
* Expect no allocation to happen.
|
||||||
|
*/
|
||||||
|
static int alloc_no_space_generic_check(void)
|
||||||
|
{
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
phys_addr_t available_size = SZ_256;
|
||||||
|
phys_addr_t reserved_size = MEM_SIZE - available_size;
|
||||||
|
|
||||||
|
/* Simulate almost-full memory */
|
||||||
|
memblock_reserve(memblock_start_of_DRAM(), reserved_size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(SZ_1K, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(!allocated_ptr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when the memory is almost full,
|
||||||
|
* but there is just enough space left:
|
||||||
|
*
|
||||||
|
* |---------------------------+---------|
|
||||||
|
* | reserved | new |
|
||||||
|
* +---------------------------+---------+
|
||||||
|
*
|
||||||
|
* Expect to allocate memory and merge all the regions. The total size field
|
||||||
|
* gets updated.
|
||||||
|
*/
|
||||||
|
static int alloc_limited_space_generic_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t available_size = SZ_256;
|
||||||
|
phys_addr_t reserved_size = MEM_SIZE - available_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
/* Simulate almost-full memory */
|
||||||
|
memblock_reserve(memblock_start_of_DRAM(), reserved_size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(available_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == MEM_SIZE);
|
||||||
|
assert(rgn->base == memblock_start_of_DRAM());
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == MEM_SIZE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there is no available memory
|
||||||
|
* registered (i.e. memblock.memory has only a dummy entry).
|
||||||
|
* Expect no allocation to happen.
|
||||||
|
*/
|
||||||
|
static int alloc_no_memory_generic_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
reset_memblock_regions();
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(SZ_1K, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(!allocated_ptr);
|
||||||
|
assert(rgn->size == 0);
|
||||||
|
assert(rgn->base == 0);
|
||||||
|
assert(memblock.reserved.total_size == 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simple test that tries to allocate a small memory region.
|
||||||
|
* Expect to allocate an aligned region at the beginning of the available
|
||||||
|
* memory.
|
||||||
|
*/
|
||||||
|
static int alloc_bottom_up_simple_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(SZ_2, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == SZ_2);
|
||||||
|
assert(rgn->base == memblock_start_of_DRAM());
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == SZ_2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory next to a reserved region that starts at
|
||||||
|
* the misaligned address. Expect to create two separate entries, with the new
|
||||||
|
* entry aligned to the provided alignment:
|
||||||
|
*
|
||||||
|
* +
|
||||||
|
* | +----------+ +----------+ |
|
||||||
|
* | | rgn1 | | rgn2 | |
|
||||||
|
* +----+----------+---+----------+-----+
|
||||||
|
* ^
|
||||||
|
* |
|
||||||
|
* Aligned address boundary
|
||||||
|
*
|
||||||
|
* The allocation direction is bottom-up, so the new region will be the second
|
||||||
|
* entry in memory.reserved array. The previously reserved region does not get
|
||||||
|
* modified. Region counter and total size get updated.
|
||||||
|
*/
|
||||||
|
static int alloc_bottom_up_disjoint_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn1 = &memblock.reserved.regions[0];
|
||||||
|
struct memblock_region *rgn2 = &memblock.reserved.regions[1];
|
||||||
|
struct region r1;
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r2_size = SZ_16;
|
||||||
|
/* Use custom alignment */
|
||||||
|
phys_addr_t alignment = SMP_CACHE_BYTES * 2;
|
||||||
|
phys_addr_t total_size;
|
||||||
|
phys_addr_t expected_start;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
r1.base = memblock_start_of_DRAM() + SZ_2;
|
||||||
|
r1.size = SZ_2;
|
||||||
|
|
||||||
|
total_size = r1.size + r2_size;
|
||||||
|
expected_start = memblock_start_of_DRAM() + alignment;
|
||||||
|
|
||||||
|
memblock_reserve(r1.base, r1.size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r2_size, alignment);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
|
||||||
|
assert(rgn1->size == r1.size);
|
||||||
|
assert(rgn1->base == r1.base);
|
||||||
|
|
||||||
|
assert(rgn2->size == r2_size);
|
||||||
|
assert(rgn2->base == expected_start);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 2);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there is enough space at
|
||||||
|
* the beginning of the previously reserved block (i.e. first fit):
|
||||||
|
*
|
||||||
|
* |------------------+--------+ |
|
||||||
|
* | r1 | r2 | |
|
||||||
|
* +------------------+--------+---------+
|
||||||
|
*
|
||||||
|
* Expect a merge of both regions. Only the region size gets updated.
|
||||||
|
*/
|
||||||
|
static int alloc_bottom_up_before_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r1_size = SZ_512;
|
||||||
|
phys_addr_t r2_size = SZ_128;
|
||||||
|
phys_addr_t total_size = r1_size + r2_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
memblock_reserve(memblock_start_of_DRAM() + r1_size, r2_size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r1_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == total_size);
|
||||||
|
assert(rgn->base == memblock_start_of_DRAM());
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there is not enough space at
|
||||||
|
* the beginning of the previously reserved block (i.e. second fit):
|
||||||
|
*
|
||||||
|
* | +--------+--------------+ |
|
||||||
|
* | | r1 | r2 | |
|
||||||
|
* +----+--------+--------------+---------+
|
||||||
|
*
|
||||||
|
* Expect a merge of both regions. Only the region size gets updated.
|
||||||
|
*/
|
||||||
|
static int alloc_bottom_up_after_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
struct region r1;
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r2_size = SZ_512;
|
||||||
|
phys_addr_t total_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The first region starts at the aligned address to test region merging
|
||||||
|
*/
|
||||||
|
r1.base = memblock_start_of_DRAM() + SMP_CACHE_BYTES;
|
||||||
|
r1.size = SZ_64;
|
||||||
|
|
||||||
|
total_size = r1.size + r2_size;
|
||||||
|
|
||||||
|
memblock_reserve(r1.base, r1.size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r2_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == total_size);
|
||||||
|
assert(rgn->base == r1.base);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate memory when there are two reserved regions, the
|
||||||
|
* first one starting at the beginning of the available memory, with a gap too
|
||||||
|
* small to fit the new region:
|
||||||
|
*
|
||||||
|
* |------------+ +--------+--------+ |
|
||||||
|
* | r1 | | r2 | r3 | |
|
||||||
|
* +------------+-----+--------+--------+--+
|
||||||
|
*
|
||||||
|
* Expect to allocate after the second region, which starts at the higher
|
||||||
|
* address, and merge them into one. The region counter and total size fields
|
||||||
|
* get updated.
|
||||||
|
*/
|
||||||
|
static int alloc_bottom_up_second_fit_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[1];
|
||||||
|
struct region r1, r2;
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r3_size = SZ_1K;
|
||||||
|
phys_addr_t total_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
r1.base = memblock_start_of_DRAM();
|
||||||
|
r1.size = SZ_512;
|
||||||
|
|
||||||
|
r2.base = r1.base + r1.size + SZ_512;
|
||||||
|
r2.size = SZ_256;
|
||||||
|
|
||||||
|
total_size = r1.size + r2.size + r3_size;
|
||||||
|
|
||||||
|
memblock_reserve(r1.base, r1.size);
|
||||||
|
memblock_reserve(r2.base, r2.size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc(r3_size, SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == r2.size + r3_size);
|
||||||
|
assert(rgn->base == r2.base);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 2);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test case wrappers */
|
||||||
|
static int alloc_simple_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_top_down_simple_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_bottom_up_simple_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_disjoint_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_top_down_disjoint_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_bottom_up_disjoint_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_before_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_top_down_before_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_bottom_up_before_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_after_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_top_down_after_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_bottom_up_after_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_in_between_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_in_between_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_in_between_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_second_fit_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_top_down_second_fit_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_bottom_up_second_fit_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_small_gaps_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_small_gaps_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_small_gaps_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_all_reserved_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_all_reserved_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_all_reserved_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_no_space_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_no_space_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_no_space_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_limited_space_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_limited_space_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_limited_space_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_no_memory_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_no_memory_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_no_memory_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memblock_alloc_checks(void)
|
||||||
|
{
|
||||||
|
reset_memblock_attributes();
|
||||||
|
dummy_physical_memory_init();
|
||||||
|
|
||||||
|
alloc_simple_check();
|
||||||
|
alloc_disjoint_check();
|
||||||
|
alloc_before_check();
|
||||||
|
alloc_after_check();
|
||||||
|
alloc_second_fit_check();
|
||||||
|
alloc_small_gaps_check();
|
||||||
|
alloc_in_between_check();
|
||||||
|
alloc_all_reserved_check();
|
||||||
|
alloc_no_space_check();
|
||||||
|
alloc_limited_space_check();
|
||||||
|
alloc_no_memory_check();
|
||||||
|
|
||||||
|
dummy_physical_memory_cleanup();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
9
tools/testing/memblock/tests/alloc_api.h
Normal file
9
tools/testing/memblock/tests/alloc_api.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
#ifndef _MEMBLOCK_ALLOCS_H
|
||||||
|
#define _MEMBLOCK_ALLOCS_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
int memblock_alloc_checks(void);
|
||||||
|
|
||||||
|
#endif
|
393
tools/testing/memblock/tests/alloc_helpers_api.c
Normal file
393
tools/testing/memblock/tests/alloc_helpers_api.c
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#include "alloc_helpers_api.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simple test that tries to allocate a memory region above a specified,
|
||||||
|
* aligned address:
|
||||||
|
*
|
||||||
|
* +
|
||||||
|
* | +-----------+ |
|
||||||
|
* | | rgn | |
|
||||||
|
* +----------+-----------+---------+
|
||||||
|
* ^
|
||||||
|
* |
|
||||||
|
* Aligned min_addr
|
||||||
|
*
|
||||||
|
* Expect to allocate a cleared region at the minimal memory address.
|
||||||
|
*/
|
||||||
|
static int alloc_from_simple_generic_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
char *b;
|
||||||
|
|
||||||
|
phys_addr_t size = SZ_16;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
min_addr = memblock_end_of_DRAM() - SMP_CACHE_BYTES;
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
b = (char *)allocated_ptr;
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(*b == 0);
|
||||||
|
|
||||||
|
assert(rgn->size == size);
|
||||||
|
assert(rgn->base == min_addr);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate a memory region above a certain address.
|
||||||
|
* The minimal address here is not aligned:
|
||||||
|
*
|
||||||
|
* + +
|
||||||
|
* | + +---------+ |
|
||||||
|
* | | | rgn | |
|
||||||
|
* +------+------+---------+------------+
|
||||||
|
* ^ ^------.
|
||||||
|
* | |
|
||||||
|
* min_addr Aligned address
|
||||||
|
* boundary
|
||||||
|
*
|
||||||
|
* Expect to allocate a cleared region at the closest aligned memory address.
|
||||||
|
*/
|
||||||
|
static int alloc_from_misaligned_generic_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
char *b;
|
||||||
|
|
||||||
|
phys_addr_t size = SZ_32;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
/* A misaligned address */
|
||||||
|
min_addr = memblock_end_of_DRAM() - (SMP_CACHE_BYTES * 2 - 1);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
b = (char *)allocated_ptr;
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(*b == 0);
|
||||||
|
|
||||||
|
assert(rgn->size == size);
|
||||||
|
assert(rgn->base == memblock_end_of_DRAM() - SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate a memory region above an address that is too
|
||||||
|
* close to the end of the memory:
|
||||||
|
*
|
||||||
|
* + +
|
||||||
|
* | +--------+---+ |
|
||||||
|
* | | rgn + | |
|
||||||
|
* +-----------+--------+---+------+
|
||||||
|
* ^ ^
|
||||||
|
* | |
|
||||||
|
* | min_addr
|
||||||
|
* |
|
||||||
|
* Aligned address
|
||||||
|
* boundary
|
||||||
|
*
|
||||||
|
* Expect to prioritize granting memory over satisfying the minimal address
|
||||||
|
* requirement.
|
||||||
|
*/
|
||||||
|
static int alloc_from_top_down_high_addr_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t size = SZ_32;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
/* The address is too close to the end of the memory */
|
||||||
|
min_addr = memblock_end_of_DRAM() - SZ_16;
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == size);
|
||||||
|
assert(rgn->base == memblock_end_of_DRAM() - SMP_CACHE_BYTES);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate a memory region when there is no space
|
||||||
|
* available above the minimal address above a certain address:
|
||||||
|
*
|
||||||
|
* +
|
||||||
|
* | +---------+-------------|
|
||||||
|
* | | rgn | |
|
||||||
|
* +--------+---------+-------------+
|
||||||
|
* ^
|
||||||
|
* |
|
||||||
|
* min_addr
|
||||||
|
*
|
||||||
|
* Expect to prioritize granting memory over satisfying the minimal address
|
||||||
|
* requirement and to allocate next to the previously reserved region. The
|
||||||
|
* regions get merged into one.
|
||||||
|
*/
|
||||||
|
static int alloc_from_top_down_no_space_above_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r1_size = SZ_64;
|
||||||
|
phys_addr_t r2_size = SZ_2;
|
||||||
|
phys_addr_t total_size = r1_size + r2_size;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
min_addr = memblock_end_of_DRAM() - SMP_CACHE_BYTES * 2;
|
||||||
|
|
||||||
|
/* No space above this address */
|
||||||
|
memblock_reserve(min_addr, r2_size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->base == min_addr - r1_size);
|
||||||
|
assert(rgn->size == total_size);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == total_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate a memory region with a minimal address below
|
||||||
|
* the start address of the available memory. As the allocation is top-down,
|
||||||
|
* first reserve a region that will force allocation near the start.
|
||||||
|
* Expect successful allocation and merge of both regions.
|
||||||
|
*/
|
||||||
|
static int alloc_from_top_down_min_addr_cap_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r1_size = SZ_64;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
phys_addr_t start_addr;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
start_addr = (phys_addr_t)memblock_start_of_DRAM();
|
||||||
|
min_addr = start_addr - SMP_CACHE_BYTES * 3;
|
||||||
|
|
||||||
|
memblock_reserve(start_addr + r1_size, MEM_SIZE - r1_size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->base == start_addr);
|
||||||
|
assert(rgn->size == MEM_SIZE);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == MEM_SIZE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate a memory region above an address that is too
|
||||||
|
* close to the end of the memory:
|
||||||
|
*
|
||||||
|
* +
|
||||||
|
* |-----------+ + |
|
||||||
|
* | rgn | | |
|
||||||
|
* +-----------+--------------+-----+
|
||||||
|
* ^ ^
|
||||||
|
* | |
|
||||||
|
* Aligned address min_addr
|
||||||
|
* boundary
|
||||||
|
*
|
||||||
|
* Expect to prioritize granting memory over satisfying the minimal address
|
||||||
|
* requirement. Allocation happens at beginning of the available memory.
|
||||||
|
*/
|
||||||
|
static int alloc_from_bottom_up_high_addr_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t size = SZ_32;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
/* The address is too close to the end of the memory */
|
||||||
|
min_addr = memblock_end_of_DRAM() - SZ_8;
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->size == size);
|
||||||
|
assert(rgn->base == memblock_start_of_DRAM());
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate a memory region when there is no space
|
||||||
|
* available above the minimal address above a certain address:
|
||||||
|
*
|
||||||
|
* +
|
||||||
|
* |-----------+ +-------------------|
|
||||||
|
* | rgn | | |
|
||||||
|
* +-----------+----+-------------------+
|
||||||
|
* ^
|
||||||
|
* |
|
||||||
|
* min_addr
|
||||||
|
*
|
||||||
|
* Expect to prioritize granting memory over satisfying the minimal address
|
||||||
|
* requirement and to allocate at the beginning of the available memory.
|
||||||
|
*/
|
||||||
|
static int alloc_from_bottom_up_no_space_above_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r1_size = SZ_64;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
phys_addr_t r2_size;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
min_addr = memblock_start_of_DRAM() + SZ_128;
|
||||||
|
r2_size = memblock_end_of_DRAM() - min_addr;
|
||||||
|
|
||||||
|
/* No space above this address */
|
||||||
|
memblock_reserve(min_addr - SMP_CACHE_BYTES, r2_size);
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->base == memblock_start_of_DRAM());
|
||||||
|
assert(rgn->size == r1_size);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 2);
|
||||||
|
assert(memblock.reserved.total_size == r1_size + r2_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A test that tries to allocate a memory region with a minimal address below
|
||||||
|
* the start address of the available memory. Expect to allocate a region
|
||||||
|
* at the beginning of the available memory.
|
||||||
|
*/
|
||||||
|
static int alloc_from_bottom_up_min_addr_cap_check(void)
|
||||||
|
{
|
||||||
|
struct memblock_region *rgn = &memblock.reserved.regions[0];
|
||||||
|
void *allocated_ptr = NULL;
|
||||||
|
|
||||||
|
phys_addr_t r1_size = SZ_64;
|
||||||
|
phys_addr_t min_addr;
|
||||||
|
phys_addr_t start_addr;
|
||||||
|
|
||||||
|
setup_memblock();
|
||||||
|
|
||||||
|
start_addr = (phys_addr_t)memblock_start_of_DRAM();
|
||||||
|
min_addr = start_addr - SMP_CACHE_BYTES * 3;
|
||||||
|
|
||||||
|
allocated_ptr = memblock_alloc_from(r1_size, SMP_CACHE_BYTES, min_addr);
|
||||||
|
|
||||||
|
assert(allocated_ptr);
|
||||||
|
assert(rgn->base == start_addr);
|
||||||
|
assert(rgn->size == r1_size);
|
||||||
|
|
||||||
|
assert(memblock.reserved.cnt == 1);
|
||||||
|
assert(memblock.reserved.total_size == r1_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test case wrappers */
|
||||||
|
static int alloc_from_simple_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_from_simple_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_from_simple_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_from_misaligned_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_from_misaligned_generic_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_from_misaligned_generic_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_from_high_addr_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_from_top_down_high_addr_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_from_bottom_up_high_addr_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_from_no_space_above_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_from_top_down_no_space_above_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_from_bottom_up_no_space_above_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_from_min_addr_cap_check(void)
|
||||||
|
{
|
||||||
|
memblock_set_bottom_up(false);
|
||||||
|
alloc_from_top_down_min_addr_cap_check();
|
||||||
|
memblock_set_bottom_up(true);
|
||||||
|
alloc_from_bottom_up_min_addr_cap_check();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memblock_alloc_helpers_checks(void)
|
||||||
|
{
|
||||||
|
reset_memblock_attributes();
|
||||||
|
dummy_physical_memory_init();
|
||||||
|
|
||||||
|
alloc_from_simple_check();
|
||||||
|
alloc_from_misaligned_check();
|
||||||
|
alloc_from_high_addr_check();
|
||||||
|
alloc_from_no_space_above_check();
|
||||||
|
alloc_from_min_addr_cap_check();
|
||||||
|
|
||||||
|
dummy_physical_memory_cleanup();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
9
tools/testing/memblock/tests/alloc_helpers_api.h
Normal file
9
tools/testing/memblock/tests/alloc_helpers_api.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
#ifndef _MEMBLOCK_ALLOC_HELPERS_H
|
||||||
|
#define _MEMBLOCK_ALLOC_HELPERS_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
int memblock_alloc_helpers_checks(void);
|
||||||
|
|
||||||
|
#endif
|
1174
tools/testing/memblock/tests/alloc_nid_api.c
Normal file
1174
tools/testing/memblock/tests/alloc_nid_api.c
Normal file
File diff suppressed because it is too large
Load diff
9
tools/testing/memblock/tests/alloc_nid_api.h
Normal file
9
tools/testing/memblock/tests/alloc_nid_api.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
#ifndef _MEMBLOCK_ALLOC_NID_H
|
||||||
|
#define _MEMBLOCK_ALLOC_NID_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
int memblock_alloc_nid_checks(void);
|
||||||
|
|
||||||
|
#endif
|
903
tools/testing/memblock/tests/basic_api.c
Normal file
903
tools/testing/memblock/tests/basic_api.c
Normal file
|
@ -0,0 +1,903 @@
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_regions();
|
||||||
|
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 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_regions();
|
||||||
|
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 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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
|
||||||
|
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_node_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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_regions();
|
||||||
|
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;
|
||||||
|
}
|
9
tools/testing/memblock/tests/basic_api.h
Normal file
9
tools/testing/memblock/tests/basic_api.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
#ifndef _MEMBLOCK_BASIC_H
|
||||||
|
#define _MEMBLOCK_BASIC_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
int memblock_basic_checks(void);
|
||||||
|
|
||||||
|
#endif
|
48
tools/testing/memblock/tests/common.c
Normal file
48
tools/testing/memblock/tests/common.c
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
static struct test_memory memory_block;
|
||||||
|
|
||||||
|
void reset_memblock_regions(void)
|
||||||
|
{
|
||||||
|
memset(memblock.memory.regions, 0,
|
||||||
|
memblock.memory.cnt * sizeof(struct memblock_region));
|
||||||
|
memblock.memory.cnt = 1;
|
||||||
|
memblock.memory.max = INIT_MEMBLOCK_REGIONS;
|
||||||
|
memblock.memory.total_size = 0;
|
||||||
|
|
||||||
|
memset(memblock.reserved.regions, 0,
|
||||||
|
memblock.reserved.cnt * sizeof(struct memblock_region));
|
||||||
|
memblock.reserved.cnt = 1;
|
||||||
|
memblock.reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS;
|
||||||
|
memblock.reserved.total_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_memblock_attributes(void)
|
||||||
|
{
|
||||||
|
memblock.memory.name = "memory";
|
||||||
|
memblock.reserved.name = "reserved";
|
||||||
|
memblock.bottom_up = false;
|
||||||
|
memblock.current_limit = MEMBLOCK_ALLOC_ANYWHERE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_memblock(void)
|
||||||
|
{
|
||||||
|
reset_memblock_regions();
|
||||||
|
memblock_add((phys_addr_t)memory_block.base, MEM_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dummy_physical_memory_init(void)
|
||||||
|
{
|
||||||
|
memory_block.base = malloc(MEM_SIZE);
|
||||||
|
assert(memory_block.base);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dummy_physical_memory_cleanup(void)
|
||||||
|
{
|
||||||
|
free(memory_block.base);
|
||||||
|
}
|
34
tools/testing/memblock/tests/common.h
Normal file
34
tools/testing/memblock/tests/common.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
#ifndef _MEMBLOCK_TEST_H
|
||||||
|
#define _MEMBLOCK_TEST_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/memblock.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
|
#define MEM_SIZE SZ_16K
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Available memory registered with memblock needs to be valid for allocs
|
||||||
|
* test to run. This is a convenience wrapper for memory allocated in
|
||||||
|
* dummy_physical_memory_init() that is later registered with memblock
|
||||||
|
* in setup_memblock().
|
||||||
|
*/
|
||||||
|
struct test_memory {
|
||||||
|
void *base;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct region {
|
||||||
|
phys_addr_t base;
|
||||||
|
phys_addr_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
void reset_memblock_regions(void);
|
||||||
|
void reset_memblock_attributes(void);
|
||||||
|
void setup_memblock(void);
|
||||||
|
void dummy_physical_memory_init(void);
|
||||||
|
void dummy_physical_memory_cleanup(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,7 +5,8 @@ CFLAGS += -I. -I../../include -g -Og -Wall -D_LGPL_SOURCE -fsanitize=address \
|
||||||
LDFLAGS += -fsanitize=address -fsanitize=undefined
|
LDFLAGS += -fsanitize=address -fsanitize=undefined
|
||||||
LDLIBS+= -lpthread -lurcu
|
LDLIBS+= -lpthread -lurcu
|
||||||
TARGETS = main idr-test multiorder xarray
|
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 \
|
OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \
|
||||||
regression4.o tag_check.o multiorder.o idr-test.o iteration_check.o \
|
regression4.o tag_check.o multiorder.o idr-test.o iteration_check.o \
|
||||||
iteration_check_2.o benchmark.o
|
iteration_check_2.o benchmark.o
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
int nr_allocated;
|
int nr_allocated;
|
||||||
int preempt_count;
|
int preempt_count;
|
||||||
int kmalloc_verbose;
|
|
||||||
int test_verbose;
|
int test_verbose;
|
||||||
|
|
||||||
struct kmem_cache {
|
struct kmem_cache {
|
||||||
|
@ -78,32 +77,6 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp)
|
||||||
pthread_mutex_unlock(&cachep->lock);
|
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 *
|
struct kmem_cache *
|
||||||
kmem_cache_create(const char *name, unsigned int size, unsigned int align,
|
kmem_cache_create(const char *name, unsigned int size, unsigned int align,
|
||||||
unsigned int flags, void (*ctor)(void *))
|
unsigned int flags, void (*ctor)(void *))
|
||||||
|
|
|
@ -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
|
|
Loading…
Add table
Reference in a new issue