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
test_external_mem(void)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
check_mem(void * addr,rte_iova_t * iova,size_t pgsz,int n_pages)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
test_malloc_invalid_param(void * addr,size_t len,size_t pgsz,rte_iova_t * iova,int n_pages)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
test_malloc_basic(void * addr,size_t len,size_t pgsz,rte_iova_t * iova,int n_pages)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
test_extmem_invalid_param(void * addr,size_t len,size_t pgsz,rte_iova_t * iova,int n_pages)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
test_extmem_basic(void * addr,size_t len,size_t pgsz,rte_iova_t * iova,int n_pages)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
test_external_mem(void)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