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
memory_hotplug_cb(enum rte_mem_event event_type,const void * addr,size_t len,void * arg)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
memory_iter_cb(const struct rte_memseg_list * msl,const struct rte_memseg * ms,size_t len,void * arg)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
verify_buffer(void * _buf,size_t len)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
test(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
main(int argc,char ** argv)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