1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "env_dpdk/memory.c" 35 36 #define UNIT_TEST_NO_VTOPHYS 37 #define UNIT_TEST_NO_PCI_ADDR 38 #include "common/lib/test_env.c" 39 #include "spdk_cunit.h" 40 41 #include "spdk/bit_array.h" 42 43 #define PAGE_ARRAY_SIZE (100) 44 static struct spdk_bit_array *g_page_array; 45 static void *g_vaddr_to_fail = (void *)UINT64_MAX; 46 47 static int 48 test_mem_map_notify(void *cb_ctx, struct spdk_mem_map *map, 49 enum spdk_mem_map_notify_action action, 50 void *vaddr, size_t len) 51 { 52 uint32_t i, end; 53 54 SPDK_CU_ASSERT_FATAL(((uintptr_t)vaddr & MASK_2MB) == 0); 55 SPDK_CU_ASSERT_FATAL((len & MASK_2MB) == 0); 56 57 /* 58 * This is a test requirement - the bit array we use to verify 59 * pages are valid is only so large. 60 */ 61 SPDK_CU_ASSERT_FATAL((uintptr_t)vaddr < (VALUE_2MB * PAGE_ARRAY_SIZE)); 62 63 i = (uintptr_t)vaddr >> SHIFT_2MB; 64 end = i + (len >> SHIFT_2MB); 65 for (; i < end; i++) { 66 switch (action) { 67 case SPDK_MEM_MAP_NOTIFY_REGISTER: 68 /* This page should not already be registered */ 69 SPDK_CU_ASSERT_FATAL(spdk_bit_array_get(g_page_array, i) == false); 70 SPDK_CU_ASSERT_FATAL(spdk_bit_array_set(g_page_array, i) == 0); 71 break; 72 case SPDK_MEM_MAP_NOTIFY_UNREGISTER: 73 SPDK_CU_ASSERT_FATAL(spdk_bit_array_get(g_page_array, i) == true); 74 spdk_bit_array_clear(g_page_array, i); 75 break; 76 default: 77 SPDK_UNREACHABLE(); 78 } 79 } 80 81 return 0; 82 } 83 84 static int 85 test_mem_map_notify_fail(void *cb_ctx, struct spdk_mem_map *map, 86 enum spdk_mem_map_notify_action action, void *vaddr, size_t size) 87 { 88 struct spdk_mem_map *reg_map = cb_ctx; 89 90 switch (action) { 91 case SPDK_MEM_MAP_NOTIFY_REGISTER: 92 if (vaddr == g_vaddr_to_fail) { 93 /* Test the error handling. */ 94 return -1; 95 } 96 break; 97 case SPDK_MEM_MAP_NOTIFY_UNREGISTER: 98 /* Clear the same region in the other mem_map to be able to 99 * verify that there was no memory left still registered after 100 * the mem_map creation failure. 101 */ 102 spdk_mem_map_clear_translation(reg_map, (uint64_t)vaddr, size); 103 break; 104 } 105 106 return 0; 107 } 108 109 static int 110 test_mem_map_notify_checklen(void *cb_ctx, struct spdk_mem_map *map, 111 enum spdk_mem_map_notify_action action, void *vaddr, size_t size) 112 { 113 size_t *len_arr = cb_ctx; 114 115 /* 116 * This is a test requirement - the len array we use to verify 117 * pages are valid is only so large. 118 */ 119 SPDK_CU_ASSERT_FATAL((uintptr_t)vaddr < (VALUE_2MB * PAGE_ARRAY_SIZE)); 120 121 switch (action) { 122 case SPDK_MEM_MAP_NOTIFY_REGISTER: 123 assert(size == len_arr[(uintptr_t)vaddr / VALUE_2MB]); 124 break; 125 case SPDK_MEM_MAP_NOTIFY_UNREGISTER: 126 CU_ASSERT(size == len_arr[(uintptr_t)vaddr / VALUE_2MB]); 127 break; 128 } 129 130 return 0; 131 } 132 133 static int 134 test_check_regions_contiguous(uint64_t addr1, uint64_t addr2) 135 { 136 return addr1 == addr2; 137 } 138 139 const struct spdk_mem_map_ops test_mem_map_ops = { 140 .notify_cb = test_mem_map_notify, 141 .are_contiguous = test_check_regions_contiguous 142 }; 143 144 const struct spdk_mem_map_ops test_mem_map_ops_no_contig = { 145 .notify_cb = test_mem_map_notify, 146 .are_contiguous = NULL 147 }; 148 149 struct spdk_mem_map_ops test_map_ops_notify_fail = { 150 .notify_cb = test_mem_map_notify_fail, 151 .are_contiguous = NULL 152 }; 153 154 struct spdk_mem_map_ops test_map_ops_notify_checklen = { 155 .notify_cb = test_mem_map_notify_checklen, 156 .are_contiguous = NULL 157 }; 158 159 static void 160 test_mem_map_alloc_free(void) 161 { 162 struct spdk_mem_map *map, *failed_map; 163 uint64_t default_translation = 0xDEADBEEF0BADF00D; 164 int i; 165 166 map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops, NULL); 167 SPDK_CU_ASSERT_FATAL(map != NULL); 168 spdk_mem_map_free(&map); 169 CU_ASSERT(map == NULL); 170 171 map = spdk_mem_map_alloc(default_translation, NULL, NULL); 172 SPDK_CU_ASSERT_FATAL(map != NULL); 173 174 /* Register some memory for the initial memory walk in 175 * spdk_mem_map_alloc(). We'll fail registering the last region 176 * and will check if the mem_map cleaned up all its previously 177 * initialized translations. 178 */ 179 for (i = 0; i < 5; i++) { 180 spdk_mem_register((void *)(uintptr_t)(2 * i * VALUE_2MB), VALUE_2MB); 181 } 182 183 /* The last region */ 184 g_vaddr_to_fail = (void *)(8 * VALUE_2MB); 185 failed_map = spdk_mem_map_alloc(default_translation, &test_map_ops_notify_fail, map); 186 CU_ASSERT(failed_map == NULL); 187 188 for (i = 0; i < 4; i++) { 189 uint64_t reg, size = VALUE_2MB; 190 191 reg = spdk_mem_map_translate(map, 2 * i * VALUE_2MB, &size); 192 /* check if `failed_map` didn't leave any translations behind */ 193 CU_ASSERT(reg == default_translation); 194 } 195 196 for (i = 0; i < 5; i++) { 197 spdk_mem_unregister((void *)(uintptr_t)(2 * i * VALUE_2MB), VALUE_2MB); 198 } 199 200 spdk_mem_map_free(&map); 201 CU_ASSERT(map == NULL); 202 } 203 204 static void 205 test_mem_map_translation(void) 206 { 207 struct spdk_mem_map *map; 208 uint64_t default_translation = 0xDEADBEEF0BADF00D; 209 uint64_t addr; 210 uint64_t mapping_length; 211 int rc; 212 213 map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops, NULL); 214 SPDK_CU_ASSERT_FATAL(map != NULL); 215 216 /* Try to get translation for address with no translation */ 217 addr = spdk_mem_map_translate(map, 10, NULL); 218 CU_ASSERT(addr == default_translation); 219 220 /* Set translation for region of non-2MB multiple size */ 221 rc = spdk_mem_map_set_translation(map, VALUE_2MB, 1234, VALUE_2MB); 222 CU_ASSERT(rc == -EINVAL); 223 224 /* Set translation for vaddr that isn't 2MB aligned */ 225 rc = spdk_mem_map_set_translation(map, 1234, VALUE_2MB, VALUE_2MB); 226 CU_ASSERT(rc == -EINVAL); 227 228 /* Set translation for one 2MB page */ 229 rc = spdk_mem_map_set_translation(map, VALUE_2MB, VALUE_2MB, VALUE_2MB); 230 CU_ASSERT(rc == 0); 231 232 /* Set translation for region that overlaps the previous translation */ 233 rc = spdk_mem_map_set_translation(map, 0, 3 * VALUE_2MB, 0); 234 CU_ASSERT(rc == 0); 235 236 /* Make sure we indicate that the three regions are contiguous */ 237 mapping_length = VALUE_2MB * 3; 238 addr = spdk_mem_map_translate(map, 0, &mapping_length); 239 CU_ASSERT(addr == 0); 240 CU_ASSERT(mapping_length == VALUE_2MB * 3); 241 242 /* Translate an unaligned address */ 243 mapping_length = VALUE_2MB * 3; 244 addr = spdk_mem_map_translate(map, VALUE_4KB, &mapping_length); 245 CU_ASSERT(addr == 0); 246 CU_ASSERT(mapping_length == VALUE_2MB * 3 - VALUE_4KB); 247 248 /* Clear translation for the middle page of the larger region. */ 249 rc = spdk_mem_map_clear_translation(map, VALUE_2MB, VALUE_2MB); 250 CU_ASSERT(rc == 0); 251 252 /* Get translation for first page */ 253 addr = spdk_mem_map_translate(map, 0, NULL); 254 CU_ASSERT(addr == 0); 255 256 /* Make sure we indicate that the three regions are no longer contiguous */ 257 mapping_length = VALUE_2MB * 3; 258 addr = spdk_mem_map_translate(map, 0, &mapping_length); 259 CU_ASSERT(addr == 0); 260 CU_ASSERT(mapping_length == VALUE_2MB); 261 262 /* Get translation for an unallocated block. Make sure size is 0 */ 263 mapping_length = VALUE_2MB * 3; 264 addr = spdk_mem_map_translate(map, VALUE_2MB, &mapping_length); 265 CU_ASSERT(addr == default_translation); 266 CU_ASSERT(mapping_length == VALUE_2MB); 267 268 /* Verify translation for 2nd page is the default */ 269 addr = spdk_mem_map_translate(map, VALUE_2MB, NULL); 270 CU_ASSERT(addr == default_translation); 271 272 /* Get translation for third page */ 273 addr = spdk_mem_map_translate(map, 2 * VALUE_2MB, NULL); 274 /* 275 * Note that addr should be 0, not 4MB. When we set the 276 * translation above, we said the whole 6MB region 277 * should translate to 0. 278 */ 279 CU_ASSERT(addr == 0); 280 281 /* Translate only a subset of a 2MB page */ 282 mapping_length = 543; 283 addr = spdk_mem_map_translate(map, 0, &mapping_length); 284 CU_ASSERT(addr == 0); 285 CU_ASSERT(mapping_length == 543); 286 287 /* Translate another subset of a 2MB page */ 288 mapping_length = 543; 289 addr = spdk_mem_map_translate(map, VALUE_4KB, &mapping_length); 290 CU_ASSERT(addr == 0); 291 CU_ASSERT(mapping_length == 543); 292 293 /* Try to translate an unaligned region that is only partially registered */ 294 mapping_length = 543; 295 addr = spdk_mem_map_translate(map, 3 * VALUE_2MB - 196, &mapping_length); 296 CU_ASSERT(addr == 0); 297 CU_ASSERT(mapping_length == 196); 298 299 /* Clear translation for the first page */ 300 rc = spdk_mem_map_clear_translation(map, 0, VALUE_2MB); 301 CU_ASSERT(rc == 0); 302 303 /* Get translation for the first page */ 304 addr = spdk_mem_map_translate(map, 0, NULL); 305 CU_ASSERT(addr == default_translation); 306 307 /* Clear translation for the third page */ 308 rc = spdk_mem_map_clear_translation(map, 2 * VALUE_2MB, VALUE_2MB); 309 CU_ASSERT(rc == 0); 310 311 /* Get translation for the third page */ 312 addr = spdk_mem_map_translate(map, 2 * VALUE_2MB, NULL); 313 CU_ASSERT(addr == default_translation); 314 315 /* Set translation for the last valid 2MB region */ 316 rc = spdk_mem_map_set_translation(map, 0xffffffe00000ULL, VALUE_2MB, 0x1234); 317 CU_ASSERT(rc == 0); 318 319 /* Verify translation for last valid 2MB region */ 320 addr = spdk_mem_map_translate(map, 0xffffffe00000ULL, NULL); 321 CU_ASSERT(addr == 0x1234); 322 323 /* Attempt to set translation for the first invalid address */ 324 rc = spdk_mem_map_set_translation(map, 0x1000000000000ULL, VALUE_2MB, 0x5678); 325 CU_ASSERT(rc == -EINVAL); 326 327 /* Attempt to set translation starting at a valid address but exceeding the valid range */ 328 rc = spdk_mem_map_set_translation(map, 0xffffffe00000ULL, VALUE_2MB * 2, 0x123123); 329 CU_ASSERT(rc != 0); 330 331 spdk_mem_map_free(&map); 332 CU_ASSERT(map == NULL); 333 334 /* Allocate a map without a contiguous region checker */ 335 map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops_no_contig, NULL); 336 SPDK_CU_ASSERT_FATAL(map != NULL); 337 338 /* map three contiguous regions */ 339 rc = spdk_mem_map_set_translation(map, 0, 3 * VALUE_2MB, 0); 340 CU_ASSERT(rc == 0); 341 342 /* Since we can't check their contiguity, make sure we only return the size of one page */ 343 mapping_length = VALUE_2MB * 3; 344 addr = spdk_mem_map_translate(map, 0, &mapping_length); 345 CU_ASSERT(addr == 0); 346 CU_ASSERT(mapping_length == VALUE_2MB); 347 348 /* Translate only a subset of a 2MB page */ 349 mapping_length = 543; 350 addr = spdk_mem_map_translate(map, 0, &mapping_length); 351 CU_ASSERT(addr == 0); 352 CU_ASSERT(mapping_length == 543); 353 354 /* Clear the translation */ 355 rc = spdk_mem_map_clear_translation(map, 0, VALUE_2MB * 3); 356 CU_ASSERT(rc == 0); 357 358 spdk_mem_map_free(&map); 359 CU_ASSERT(map == NULL); 360 } 361 362 static void 363 test_mem_map_registration(void) 364 { 365 int rc; 366 struct spdk_mem_map *map; 367 uint64_t default_translation = 0xDEADBEEF0BADF00D; 368 369 map = spdk_mem_map_alloc(default_translation, &test_mem_map_ops, NULL); 370 SPDK_CU_ASSERT_FATAL(map != NULL); 371 372 /* Unregister memory region that wasn't previously registered */ 373 rc = spdk_mem_unregister((void *)VALUE_2MB, VALUE_2MB); 374 CU_ASSERT(rc == -EINVAL); 375 376 /* Register non-2MB multiple size */ 377 rc = spdk_mem_register((void *)VALUE_2MB, 1234); 378 CU_ASSERT(rc == -EINVAL); 379 380 /* Register region that isn't 2MB aligned */ 381 rc = spdk_mem_register((void *)1234, VALUE_2MB); 382 CU_ASSERT(rc == -EINVAL); 383 384 /* Register one 2MB page */ 385 rc = spdk_mem_register((void *)VALUE_2MB, VALUE_2MB); 386 CU_ASSERT(rc == 0); 387 388 /* Register an overlapping address range */ 389 rc = spdk_mem_register((void *)0, 3 * VALUE_2MB); 390 CU_ASSERT(rc == -EBUSY); 391 392 /* Unregister a 2MB page */ 393 rc = spdk_mem_unregister((void *)VALUE_2MB, VALUE_2MB); 394 CU_ASSERT(rc == 0); 395 396 /* Register non overlapping address range */ 397 rc = spdk_mem_register((void *)0, 3 * VALUE_2MB); 398 CU_ASSERT(rc == 0); 399 400 /* Unregister the middle page of the larger region. */ 401 rc = spdk_mem_unregister((void *)VALUE_2MB, VALUE_2MB); 402 CU_ASSERT(rc == -ERANGE); 403 404 /* Unregister the first page */ 405 rc = spdk_mem_unregister((void *)0, VALUE_2MB); 406 CU_ASSERT(rc == -ERANGE); 407 408 /* Unregister the third page */ 409 rc = spdk_mem_unregister((void *)(2 * VALUE_2MB), VALUE_2MB); 410 CU_ASSERT(rc == -ERANGE); 411 412 /* Unregister the entire address range */ 413 rc = spdk_mem_unregister((void *)0, 3 * VALUE_2MB); 414 CU_ASSERT(rc == 0); 415 416 spdk_mem_map_free(&map); 417 CU_ASSERT(map == NULL); 418 } 419 420 static void 421 test_mem_map_registration_adjacent(void) 422 { 423 struct spdk_mem_map *map, *newmap; 424 uint64_t default_translation = 0xDEADBEEF0BADF00D; 425 uintptr_t vaddr; 426 unsigned i; 427 size_t notify_len[PAGE_ARRAY_SIZE] = {0}; 428 size_t chunk_len[] = { 2, 1, 3, 2, 1, 1 }; 429 430 map = spdk_mem_map_alloc(default_translation, 431 &test_map_ops_notify_checklen, notify_len); 432 SPDK_CU_ASSERT_FATAL(map != NULL); 433 434 vaddr = 0; 435 for (i = 0; i < SPDK_COUNTOF(chunk_len); i++) { 436 notify_len[vaddr / VALUE_2MB] = chunk_len[i] * VALUE_2MB; 437 spdk_mem_register((void *)vaddr, notify_len[vaddr / VALUE_2MB]); 438 vaddr += notify_len[vaddr / VALUE_2MB]; 439 } 440 441 /* Verify the memory is translated in the same chunks it was registered */ 442 newmap = spdk_mem_map_alloc(default_translation, 443 &test_map_ops_notify_checklen, notify_len); 444 SPDK_CU_ASSERT_FATAL(newmap != NULL); 445 spdk_mem_map_free(&newmap); 446 CU_ASSERT(newmap == NULL); 447 448 vaddr = 0; 449 for (i = 0; i < SPDK_COUNTOF(chunk_len); i++) { 450 notify_len[vaddr / VALUE_2MB] = chunk_len[i] * VALUE_2MB; 451 spdk_mem_unregister((void *)vaddr, notify_len[vaddr / VALUE_2MB]); 452 vaddr += notify_len[vaddr / VALUE_2MB]; 453 } 454 455 /* Register all chunks again just to unregister them again, but this 456 * time with only a single unregister() call. 457 */ 458 vaddr = 0; 459 for (i = 0; i < SPDK_COUNTOF(chunk_len); i++) { 460 notify_len[vaddr / VALUE_2MB] = chunk_len[i] * VALUE_2MB; 461 spdk_mem_register((void *)vaddr, notify_len[vaddr / VALUE_2MB]); 462 vaddr += notify_len[vaddr / VALUE_2MB]; 463 } 464 spdk_mem_unregister(0, vaddr); 465 466 spdk_mem_map_free(&map); 467 CU_ASSERT(map == NULL); 468 } 469 470 int 471 main(int argc, char **argv) 472 { 473 CU_pSuite suite = NULL; 474 unsigned int num_failures; 475 476 /* 477 * These tests can use PAGE_ARRAY_SIZE 2MB pages of memory. 478 * Note that the tests just verify addresses - this memory 479 * is not actually allocated. 480 */ 481 g_page_array = spdk_bit_array_create(PAGE_ARRAY_SIZE); 482 483 /* Initialize the memory map */ 484 if (spdk_mem_map_init() < 0) { 485 return CUE_NOMEMORY; 486 } 487 488 if (CU_initialize_registry() != CUE_SUCCESS) { 489 return CU_get_error(); 490 } 491 492 suite = CU_add_suite("memory", NULL, NULL); 493 if (suite == NULL) { 494 CU_cleanup_registry(); 495 return CU_get_error(); 496 } 497 498 if ( 499 CU_add_test(suite, "alloc and free memory map", test_mem_map_alloc_free) == NULL || 500 CU_add_test(suite, "mem map translation", test_mem_map_translation) == NULL || 501 CU_add_test(suite, "mem map registration", test_mem_map_registration) == NULL || 502 CU_add_test(suite, "mem map adjacent registrations", test_mem_map_registration_adjacent) == NULL 503 ) { 504 CU_cleanup_registry(); 505 return CU_get_error(); 506 } 507 508 CU_basic_set_mode(CU_BRM_VERBOSE); 509 CU_basic_run_tests(); 510 num_failures = CU_get_number_of_failures(); 511 CU_cleanup_registry(); 512 513 spdk_bit_array_free(&g_page_array); 514 515 return num_failures; 516 } 517