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