1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Intel Corporation 3 */ 4 5 #include "test.h" 6 7 #include <errno.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <fcntl.h> 12 13 #ifdef RTE_EXEC_ENV_WINDOWS 14 static int 15 test_external_mem(void) 16 { 17 printf("external_mem not supported on Windows, skipping test\n"); 18 return TEST_SKIPPED; 19 } 20 21 #else 22 23 #include <sys/mman.h> 24 #include <sys/wait.h> 25 26 #include <rte_common.h> 27 #include <rte_debug.h> 28 #include <rte_eal.h> 29 #include <rte_eal_paging.h> 30 #include <rte_errno.h> 31 #include <rte_malloc.h> 32 #include <rte_ring.h> 33 #include <rte_string_fns.h> 34 35 #define EXTERNAL_MEM_SZ (RTE_PGSIZE_4K << 10) /* 4M of data */ 36 37 static int 38 check_mem(void *addr, rte_iova_t *iova, size_t pgsz, int n_pages) 39 { 40 int i; 41 42 /* check that we can get this memory from EAL now */ 43 for (i = 0; i < n_pages; i++) { 44 const struct rte_memseg_list *msl; 45 const struct rte_memseg *ms; 46 void *cur = RTE_PTR_ADD(addr, pgsz * i); 47 rte_iova_t expected_iova; 48 49 msl = rte_mem_virt2memseg_list(cur); 50 if (!msl->external) { 51 printf("%s():%i: Memseg list is not marked as external\n", 52 __func__, __LINE__); 53 return -1; 54 } 55 56 ms = rte_mem_virt2memseg(cur, msl); 57 if (ms == NULL) { 58 printf("%s():%i: Failed to retrieve memseg for external mem\n", 59 __func__, __LINE__); 60 return -1; 61 } 62 if (ms->addr != cur) { 63 printf("%s():%i: VA mismatch\n", __func__, __LINE__); 64 return -1; 65 } 66 expected_iova = (iova == NULL) ? RTE_BAD_IOVA : iova[i]; 67 if (ms->iova != expected_iova) { 68 printf("%s():%i: IOVA mismatch\n", __func__, __LINE__); 69 return -1; 70 } 71 } 72 return 0; 73 } 74 75 static int 76 test_malloc_invalid_param(void *addr, size_t len, size_t pgsz, rte_iova_t *iova, 77 int n_pages) 78 { 79 static const char * const names[] = { 80 NULL, /* NULL name */ 81 "", /* empty name */ 82 "this heap name is definitely way too long to be valid" 83 }; 84 const char *valid_name = "valid heap name"; 85 unsigned int i; 86 87 /* check invalid name handling */ 88 for (i = 0; i < RTE_DIM(names); i++) { 89 const char *name = names[i]; 90 91 /* these calls may fail for other reasons, so check errno */ 92 if (rte_malloc_heap_create(name) >= 0 || rte_errno != EINVAL) { 93 printf("%s():%i: Created heap with invalid name\n", 94 __func__, __LINE__); 95 goto fail; 96 } 97 98 if (rte_malloc_heap_destroy(name) >= 0 || rte_errno != EINVAL) { 99 printf("%s():%i: Destroyed heap with invalid name\n", 100 __func__, __LINE__); 101 goto fail; 102 } 103 104 if (rte_malloc_heap_get_socket(name) >= 0 || 105 rte_errno != EINVAL) { 106 printf("%s():%i: Found socket for heap with invalid name\n", 107 __func__, __LINE__); 108 goto fail; 109 } 110 111 if (rte_malloc_heap_memory_add(name, addr, len, 112 NULL, 0, pgsz) >= 0 || rte_errno != EINVAL) { 113 printf("%s():%i: Added memory to heap with invalid name\n", 114 __func__, __LINE__); 115 goto fail; 116 } 117 if (rte_malloc_heap_memory_remove(name, addr, len) >= 0 || 118 rte_errno != EINVAL) { 119 printf("%s():%i: Removed memory from heap with invalid name\n", 120 __func__, __LINE__); 121 goto fail; 122 } 123 124 if (rte_malloc_heap_memory_attach(name, addr, len) >= 0 || 125 rte_errno != EINVAL) { 126 printf("%s():%i: Attached memory to heap with invalid name\n", 127 __func__, __LINE__); 128 goto fail; 129 } 130 if (rte_malloc_heap_memory_detach(name, addr, len) >= 0 || 131 rte_errno != EINVAL) { 132 printf("%s():%i: Detached memory from heap with invalid name\n", 133 __func__, __LINE__); 134 goto fail; 135 } 136 } 137 138 /* do same as above, but with a valid heap name */ 139 140 /* skip create call */ 141 if (rte_malloc_heap_destroy(valid_name) >= 0 || rte_errno != ENOENT) { 142 printf("%s():%i: Destroyed heap with invalid name\n", 143 __func__, __LINE__); 144 goto fail; 145 } 146 if (rte_malloc_heap_get_socket(valid_name) >= 0 || 147 rte_errno != ENOENT) { 148 printf("%s():%i: Found socket for heap with invalid name\n", 149 __func__, __LINE__); 150 goto fail; 151 } 152 153 /* these calls may fail for other reasons, so check errno */ 154 if (rte_malloc_heap_memory_add(valid_name, addr, len, 155 NULL, 0, pgsz) >= 0 || rte_errno != ENOENT) { 156 printf("%s():%i: Added memory to non-existent heap\n", 157 __func__, __LINE__); 158 goto fail; 159 } 160 if (rte_malloc_heap_memory_remove(valid_name, addr, len) >= 0 || 161 rte_errno != ENOENT) { 162 printf("%s():%i: Removed memory from non-existent heap\n", 163 __func__, __LINE__); 164 goto fail; 165 } 166 167 if (rte_malloc_heap_memory_attach(valid_name, addr, len) >= 0 || 168 rte_errno != ENOENT) { 169 printf("%s():%i: Attached memory to non-existent heap\n", 170 __func__, __LINE__); 171 goto fail; 172 } 173 if (rte_malloc_heap_memory_detach(valid_name, addr, len) >= 0 || 174 rte_errno != ENOENT) { 175 printf("%s():%i: Detached memory from non-existent heap\n", 176 __func__, __LINE__); 177 goto fail; 178 } 179 180 /* create a valid heap but test other invalid parameters */ 181 if (rte_malloc_heap_create(valid_name) != 0) { 182 printf("%s():%i: Failed to create valid heap\n", 183 __func__, __LINE__); 184 goto fail; 185 } 186 187 /* zero length */ 188 if (rte_malloc_heap_memory_add(valid_name, addr, 0, 189 NULL, 0, pgsz) >= 0 || rte_errno != EINVAL) { 190 printf("%s():%i: Added memory with invalid parameters\n", 191 __func__, __LINE__); 192 goto fail; 193 } 194 195 if (rte_malloc_heap_memory_remove(valid_name, addr, 0) >= 0 || 196 rte_errno != EINVAL) { 197 printf("%s():%i: Removed memory with invalid parameters\n", 198 __func__, __LINE__); 199 goto fail; 200 } 201 202 if (rte_malloc_heap_memory_attach(valid_name, addr, 0) >= 0 || 203 rte_errno != EINVAL) { 204 printf("%s():%i: Attached memory with invalid parameters\n", 205 __func__, __LINE__); 206 goto fail; 207 } 208 if (rte_malloc_heap_memory_detach(valid_name, addr, 0) >= 0 || 209 rte_errno != EINVAL) { 210 printf("%s():%i: Detached memory with invalid parameters\n", 211 __func__, __LINE__); 212 goto fail; 213 } 214 215 /* zero address */ 216 if (rte_malloc_heap_memory_add(valid_name, NULL, len, 217 NULL, 0, pgsz) >= 0 || rte_errno != EINVAL) { 218 printf("%s():%i: Added memory with invalid parameters\n", 219 __func__, __LINE__); 220 goto fail; 221 } 222 223 if (rte_malloc_heap_memory_remove(valid_name, NULL, len) >= 0 || 224 rte_errno != EINVAL) { 225 printf("%s():%i: Removed memory with invalid parameters\n", 226 __func__, __LINE__); 227 goto fail; 228 } 229 230 if (rte_malloc_heap_memory_attach(valid_name, NULL, len) >= 0 || 231 rte_errno != EINVAL) { 232 printf("%s():%i: Attached memory with invalid parameters\n", 233 __func__, __LINE__); 234 goto fail; 235 } 236 if (rte_malloc_heap_memory_detach(valid_name, NULL, len) >= 0 || 237 rte_errno != EINVAL) { 238 printf("%s():%i: Detached memory with invalid parameters\n", 239 __func__, __LINE__); 240 goto fail; 241 } 242 243 /* the following tests are only valid if IOVA table is not NULL */ 244 if (iova != NULL) { 245 /* wrong page count */ 246 if (rte_malloc_heap_memory_add(valid_name, addr, len, 247 iova, 0, pgsz) >= 0 || rte_errno != EINVAL) { 248 printf("%s():%i: Added memory with invalid parameters\n", 249 __func__, __LINE__); 250 goto fail; 251 } 252 if (rte_malloc_heap_memory_add(valid_name, addr, len, 253 iova, n_pages - 1, pgsz) >= 0 || 254 rte_errno != EINVAL) { 255 printf("%s():%i: Added memory with invalid parameters\n", 256 __func__, __LINE__); 257 goto fail; 258 } 259 if (rte_malloc_heap_memory_add(valid_name, addr, len, 260 iova, n_pages + 1, pgsz) >= 0 || 261 rte_errno != EINVAL) { 262 printf("%s():%i: Added memory with invalid parameters\n", 263 __func__, __LINE__); 264 goto fail; 265 } 266 } 267 268 /* tests passed, destroy heap */ 269 if (rte_malloc_heap_destroy(valid_name) != 0) { 270 printf("%s():%i: Failed to destroy valid heap\n", 271 __func__, __LINE__); 272 goto fail; 273 } 274 return 0; 275 fail: 276 rte_malloc_heap_destroy(valid_name); 277 return -1; 278 } 279 280 static int 281 test_malloc_basic(void *addr, size_t len, size_t pgsz, rte_iova_t *iova, 282 int n_pages) 283 { 284 const char *heap_name = "heap"; 285 void *ptr = NULL; 286 int socket_id; 287 const struct rte_memzone *mz = NULL, *contig_mz = NULL; 288 289 /* create heap */ 290 if (rte_malloc_heap_create(heap_name) != 0) { 291 printf("%s():%i: Failed to create malloc heap\n", 292 __func__, __LINE__); 293 goto fail; 294 } 295 296 /* get socket ID corresponding to this heap */ 297 socket_id = rte_malloc_heap_get_socket(heap_name); 298 if (socket_id < 0) { 299 printf("%s():%i: cannot find socket for external heap\n", 300 __func__, __LINE__); 301 goto fail; 302 } 303 304 /* heap is empty, so any allocation should fail */ 305 ptr = rte_malloc_socket("EXTMEM", 64, 0, socket_id); 306 if (ptr != NULL) { 307 printf("%s():%i: Allocated from empty heap\n", __func__, 308 __LINE__); 309 goto fail; 310 } 311 312 /* add memory to heap */ 313 if (rte_malloc_heap_memory_add(heap_name, addr, len, 314 iova, n_pages, pgsz) != 0) { 315 printf("%s():%i: Failed to add memory to heap\n", 316 __func__, __LINE__); 317 goto fail; 318 } 319 320 /* check if memory is accessible from EAL */ 321 if (check_mem(addr, iova, pgsz, n_pages) < 0) 322 goto fail; 323 324 /* allocate - this now should succeed */ 325 ptr = rte_malloc_socket("EXTMEM", 64, 0, socket_id); 326 if (ptr == NULL) { 327 printf("%s():%i: Failed to allocate from external heap\n", 328 __func__, __LINE__); 329 goto fail; 330 } 331 332 /* check if address is in expected range */ 333 if (ptr < addr || ptr >= RTE_PTR_ADD(addr, len)) { 334 printf("%s():%i: Allocated from unexpected address space\n", 335 __func__, __LINE__); 336 goto fail; 337 } 338 339 /* we've allocated something - removing memory should fail */ 340 if (rte_malloc_heap_memory_remove(heap_name, addr, len) >= 0 || 341 rte_errno != EBUSY) { 342 printf("%s():%i: Removing memory succeeded when memory is not free\n", 343 __func__, __LINE__); 344 goto fail; 345 } 346 if (rte_malloc_heap_destroy(heap_name) >= 0 || rte_errno != EBUSY) { 347 printf("%s():%i: Destroying heap succeeded when memory is not free\n", 348 __func__, __LINE__); 349 goto fail; 350 } 351 352 /* try allocating a memzone */ 353 mz = rte_memzone_reserve("heap_test", pgsz * 2, socket_id, 0); 354 if (mz == NULL) { 355 printf("%s():%i: Failed to reserve memzone\n", 356 __func__, __LINE__); 357 goto fail; 358 } 359 /* try allocating an IOVA-contiguous memzone - this should succeed 360 * if we've set up a contiguous IOVA table, and fail if we haven't. 361 */ 362 contig_mz = rte_memzone_reserve("heap_test_contig", pgsz * 2, socket_id, 363 RTE_MEMZONE_IOVA_CONTIG); 364 if ((iova == NULL) != (contig_mz == NULL)) { 365 printf("%s():%i: Failed to reserve memzone\n", 366 __func__, __LINE__); 367 goto fail; 368 } 369 370 rte_malloc_dump_stats(stdout, NULL); 371 rte_malloc_dump_heaps(stdout); 372 373 /* free memory - removing it should now succeed */ 374 rte_free(ptr); 375 ptr = NULL; 376 377 rte_memzone_free(mz); 378 mz = NULL; 379 rte_memzone_free(contig_mz); 380 contig_mz = NULL; 381 382 if (rte_malloc_heap_memory_remove(heap_name, addr, len) != 0) { 383 printf("%s():%i: Removing memory from heap failed\n", 384 __func__, __LINE__); 385 goto fail; 386 } 387 if (rte_malloc_heap_destroy(heap_name) != 0) { 388 printf("%s():%i: Destroying heap failed\n", 389 __func__, __LINE__); 390 goto fail; 391 } 392 393 return 0; 394 fail: 395 rte_memzone_free(contig_mz); 396 rte_memzone_free(mz); 397 rte_free(ptr); 398 /* even if something failed, attempt to clean up */ 399 rte_malloc_heap_memory_remove(heap_name, addr, len); 400 rte_malloc_heap_destroy(heap_name); 401 402 return -1; 403 } 404 405 static int 406 test_extmem_invalid_param(void *addr, size_t len, size_t pgsz, rte_iova_t *iova, 407 int n_pages) 408 { 409 /* these calls may fail for other reasons, so check errno */ 410 if (rte_extmem_unregister(addr, len) >= 0 || 411 rte_errno != ENOENT) { 412 printf("%s():%i: Unregistered non-existent memory\n", 413 __func__, __LINE__); 414 return -1; 415 } 416 417 if (rte_extmem_attach(addr, len) >= 0 || 418 rte_errno != ENOENT) { 419 printf("%s():%i: Attached to non-existent memory\n", 420 __func__, __LINE__); 421 return -1; 422 } 423 if (rte_extmem_attach(addr, len) >= 0 || 424 rte_errno != ENOENT) { 425 printf("%s():%i: Detached from non-existent memory\n", 426 __func__, __LINE__); 427 return -1; 428 } 429 430 /* zero length */ 431 if (rte_extmem_register(addr, 0, NULL, 0, pgsz) >= 0 || 432 rte_errno != EINVAL) { 433 printf("%s():%i: Registered memory with invalid parameters\n", 434 __func__, __LINE__); 435 return -1; 436 } 437 438 if (rte_extmem_unregister(addr, 0) >= 0 || 439 rte_errno != EINVAL) { 440 printf("%s():%i: Unregistered memory with invalid parameters\n", 441 __func__, __LINE__); 442 return -1; 443 } 444 445 if (rte_extmem_attach(addr, 0) >= 0 || 446 rte_errno != EINVAL) { 447 printf("%s():%i: Attached memory with invalid parameters\n", 448 __func__, __LINE__); 449 return -1; 450 } 451 if (rte_extmem_attach(addr, 0) >= 0 || 452 rte_errno != EINVAL) { 453 printf("%s():%i: Detached memory with invalid parameters\n", 454 __func__, __LINE__); 455 return -1; 456 } 457 458 /* zero address */ 459 if (rte_extmem_register(NULL, len, NULL, 0, pgsz) >= 0 || 460 rte_errno != EINVAL) { 461 printf("%s():%i: Registered memory with invalid parameters\n", 462 __func__, __LINE__); 463 return -1; 464 } 465 466 if (rte_extmem_unregister(NULL, len) >= 0 || 467 rte_errno != EINVAL) { 468 printf("%s():%i: Unregistered memory with invalid parameters\n", 469 __func__, __LINE__); 470 return -1; 471 } 472 473 if (rte_extmem_attach(NULL, len) >= 0 || 474 rte_errno != EINVAL) { 475 printf("%s():%i: Attached memory with invalid parameters\n", 476 __func__, __LINE__); 477 return -1; 478 } 479 if (rte_extmem_attach(NULL, len) >= 0 || 480 rte_errno != EINVAL) { 481 printf("%s():%i: Detached memory with invalid parameters\n", 482 __func__, __LINE__); 483 return -1; 484 } 485 486 /* the following tests are only valid if IOVA table is not NULL */ 487 if (iova != NULL) { 488 /* wrong page count */ 489 if (rte_extmem_register(addr, len, 490 iova, 0, pgsz) >= 0 || rte_errno != EINVAL) { 491 printf("%s():%i: Registered memory with invalid parameters\n", 492 __func__, __LINE__); 493 return -1; 494 } 495 if (rte_extmem_register(addr, len, 496 iova, n_pages - 1, pgsz) >= 0 || 497 rte_errno != EINVAL) { 498 printf("%s():%i: Registered memory with invalid parameters\n", 499 __func__, __LINE__); 500 return -1; 501 } 502 if (rte_extmem_register(addr, len, 503 iova, n_pages + 1, pgsz) >= 0 || 504 rte_errno != EINVAL) { 505 printf("%s():%i: Registered memory with invalid parameters\n", 506 __func__, __LINE__); 507 return -1; 508 } 509 } 510 511 return 0; 512 } 513 514 static int 515 test_extmem_basic(void *addr, size_t len, size_t pgsz, rte_iova_t *iova, 516 int n_pages) 517 { 518 /* register memory */ 519 if (rte_extmem_register(addr, len, iova, n_pages, pgsz) != 0) { 520 printf("%s():%i: Failed to register memory\n", 521 __func__, __LINE__); 522 goto fail; 523 } 524 525 /* check if memory is accessible from EAL */ 526 if (check_mem(addr, iova, pgsz, n_pages) < 0) 527 goto fail; 528 529 if (rte_extmem_unregister(addr, len) != 0) { 530 printf("%s():%i: Removing memory from heap failed\n", 531 __func__, __LINE__); 532 goto fail; 533 } 534 535 return 0; 536 fail: 537 /* even if something failed, attempt to clean up */ 538 rte_extmem_unregister(addr, len); 539 540 return -1; 541 } 542 543 /* we need to test attach/detach in secondary processes. */ 544 static int 545 test_external_mem(void) 546 { 547 size_t pgsz = rte_mem_page_size(); 548 size_t len = EXTERNAL_MEM_SZ; 549 rte_iova_t iova[len / pgsz]; 550 void *addr; 551 int ret, n_pages; 552 int i; 553 554 /* create external memory area */ 555 n_pages = RTE_DIM(iova); 556 addr = mmap(NULL, len, PROT_WRITE | PROT_READ, 557 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 558 if (addr == MAP_FAILED) { 559 printf("%s():%i: Failed to create dummy memory area\n", 560 __func__, __LINE__); 561 return -1; 562 } 563 for (i = 0; i < n_pages; i++) { 564 /* arbitrary IOVA */ 565 rte_iova_t tmp = 0x100000000 + i * pgsz; 566 iova[i] = tmp; 567 } 568 569 /* test external heap memory */ 570 ret = test_malloc_invalid_param(addr, len, pgsz, iova, n_pages); 571 ret |= test_malloc_basic(addr, len, pgsz, iova, n_pages); 572 /* when iova table is NULL, everything should still work */ 573 ret |= test_malloc_invalid_param(addr, len, pgsz, NULL, n_pages); 574 ret |= test_malloc_basic(addr, len, pgsz, NULL, n_pages); 575 576 /* test non-heap memory */ 577 ret |= test_extmem_invalid_param(addr, len, pgsz, iova, n_pages); 578 ret |= test_extmem_basic(addr, len, pgsz, iova, n_pages); 579 /* when iova table is NULL, everything should still work */ 580 ret |= test_extmem_invalid_param(addr, len, pgsz, NULL, n_pages); 581 ret |= test_extmem_basic(addr, len, pgsz, NULL, n_pages); 582 583 munmap(addr, len); 584 585 return ret; 586 } 587 588 #endif /* !RTE_EXEC_ENV_WINDOWS */ 589 590 REGISTER_TEST_COMMAND(external_mem_autotest, test_external_mem); 591