xref: /spdk/test/env/mem_callbacks/mem_callbacks.c (revision 4f651a5c554531bf461572b7ff23a000d690b4c6)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2018 Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/stdinc.h"
7 
8 #include "spdk/util.h"
9 #include "spdk/queue.h"
10 #include "spdk_internal/cunit.h"
11 
12 #include <rte_config.h>
13 #include <rte_version.h>
14 #include <rte_malloc.h>
15 #include <rte_eal_memconfig.h>
16 #include <rte_eal.h>
17 
18 struct mem_allocation {
19 	uintptr_t			vaddr;
20 	size_t				len;
21 	TAILQ_ENTRY(mem_allocation)	link;
22 };
23 
24 static TAILQ_HEAD(, mem_allocation) g_mem_allocations = TAILQ_HEAD_INITIALIZER(g_mem_allocations);
25 
26 static void
27 memory_hotplug_cb(enum rte_mem_event event_type, const void *addr, size_t len, void *arg)
28 {
29 	struct mem_allocation *allocation;
30 
31 	if (event_type == RTE_MEM_EVENT_ALLOC) {
32 		allocation = calloc(1, sizeof(*allocation));
33 		SPDK_CU_ASSERT_FATAL(allocation != NULL);
34 
35 		printf("register %p %ju\n", addr, len);
36 		allocation->vaddr = (uintptr_t)addr;
37 		allocation->len = len;
38 		TAILQ_INSERT_TAIL(&g_mem_allocations, allocation, link);
39 	} else if (event_type == RTE_MEM_EVENT_FREE) {
40 		TAILQ_FOREACH(allocation, &g_mem_allocations, link) {
41 			if (allocation->vaddr == (uintptr_t)addr && allocation->len == len) {
42 				break;
43 			}
44 		}
45 		printf("unregister %p %ju %s\n", addr, len, allocation == NULL ? "FAILED" : "PASSED");
46 		SPDK_CU_ASSERT_FATAL(allocation != NULL);
47 		TAILQ_REMOVE(&g_mem_allocations, allocation, link);
48 		free(allocation);
49 	}
50 }
51 
52 static int
53 memory_iter_cb(const struct rte_memseg_list *msl,
54 	       const struct rte_memseg *ms, size_t len, void *arg)
55 {
56 	struct mem_allocation *allocation;
57 
58 	allocation = calloc(1, sizeof(*allocation));
59 	SPDK_CU_ASSERT_FATAL(allocation != NULL);
60 
61 	printf("register %p %ju\n", ms->addr, len);
62 	allocation->vaddr = (uintptr_t)ms->addr;
63 	allocation->len = len;
64 	TAILQ_INSERT_TAIL(&g_mem_allocations, allocation, link);
65 
66 	return 0;
67 }
68 
69 static void
70 verify_buffer(void *_buf, size_t len)
71 {
72 	uintptr_t buf = (uintptr_t)_buf;
73 	struct mem_allocation *allocation;
74 
75 	SPDK_CU_ASSERT_FATAL(_buf != NULL);
76 	printf("buf %p len %ju ", _buf, len);
77 	TAILQ_FOREACH(allocation, &g_mem_allocations, link) {
78 		if (buf >= allocation->vaddr &&
79 		    buf + len <= allocation->vaddr + allocation->len) {
80 			break;
81 		}
82 	}
83 	printf("%s\n", allocation == NULL ? "FAILED" : "PASSED");
84 	CU_ASSERT(allocation != NULL);
85 }
86 
87 static void
88 test(void)
89 {
90 	void *buf1, *buf2, *buf3, *buf4;
91 	size_t len1, len2, len3, len4;
92 
93 	printf("\n");
94 
95 	rte_mem_event_callback_register("test", memory_hotplug_cb, NULL);
96 	rte_memseg_contig_walk(memory_iter_cb, NULL);
97 
98 	/* First allocate a 3MB buffer.  This will allocate a 4MB hugepage
99 	 *  region, with the 3MB buffer allocated from the end of it.
100 	 */
101 	len1 = 3 * 1024 * 1024;
102 	printf("malloc %ju\n", len1);
103 	buf1 = rte_malloc(NULL, len1, 0);
104 	verify_buffer(buf1, len1);
105 
106 	/* Now allocate a very small buffer.  This will get allocated from
107 	 *  the previous 4MB hugepage allocation, just before the 3MB buffer
108 	 *  allocated just above.
109 	 */
110 	len2 = 64;
111 	printf("malloc %ju\n", len2);
112 	buf2 = rte_malloc(NULL, len2, 0);
113 	verify_buffer(buf2, len2);
114 
115 	/* Allocate a 4MB buffer.  This should trigger a new hugepage allocation
116 	 * just for this 4MB buffer.
117 	 */
118 	len3 = 4 * 1024 * 1024;
119 	printf("malloc %ju\n", len3);
120 	buf3 = rte_malloc(NULL, len3, 0);
121 	verify_buffer(buf3, len3);
122 
123 	/* Free the three buffers.  Specifically free buf1 first.  buf2 was
124 	 * allocated from the same huge page allocation as buf1 - so we want
125 	 * to make sure that DPDK doesn't try to free part of the first huge
126 	 * page allocation - it needs to wait until buf2 is also freed so it
127 	 * can free all of it.
128 	 */
129 	printf("free %p %ju\n", buf1, len1);
130 	rte_free(buf1);
131 	printf("free %p %ju\n", buf2, len2);
132 	rte_free(buf2);
133 	printf("free %p %ju\n", buf3, len3);
134 	rte_free(buf3);
135 
136 	/* Do a single 8MB hugepage allocation and then free it.  This covers
137 	 * the more simple case.
138 	 */
139 	len4 = 8 * 1024 * 1024;
140 	printf("malloc %ju\n", len4);
141 	buf4 = rte_malloc(NULL, len4, 0);
142 	verify_buffer(buf4, len4);
143 
144 	printf("free %p %ju\n", buf4, len4);
145 	rte_free(buf4);
146 }
147 
148 int
149 main(int argc, char **argv)
150 {
151 	CU_pSuite		suite = NULL;
152 	unsigned int		num_failures;
153 	char			*dpdk_arg[] = {
154 		"mem_callbacks", "-c 0x1",
155 		"--base-virtaddr=0x200000000000",
156 		"--match-allocations",
157 	};
158 	int			rc;
159 
160 	rc = rte_eal_init(SPDK_COUNTOF(dpdk_arg), dpdk_arg);
161 	if (rc < 0) {
162 		printf("Err: Unable to initialize DPDK\n");
163 		return 1;
164 	}
165 
166 	if (CU_initialize_registry() != CUE_SUCCESS) {
167 		return CU_get_error();
168 	}
169 
170 	suite = CU_add_suite("memory", NULL, NULL);
171 	if (suite == NULL) {
172 		CU_cleanup_registry();
173 		return CU_get_error();
174 	}
175 
176 	if (
177 		CU_add_test(suite, "test", test) == NULL
178 	) {
179 		CU_cleanup_registry();
180 		return CU_get_error();
181 	}
182 
183 	num_failures = spdk_ut_run_tests(argc, argv, NULL);
184 	CU_cleanup_registry();
185 
186 	return num_failures;
187 }
188