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