test/threads: add unit test
[dpdk.git] / app / test / test_external_mem.c
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);