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