test: skip tests when missing requirements
[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 <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_memconfig.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 len = EXTERNAL_MEM_SZ;
537         size_t pgsz = RTE_PGSIZE_4K;
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);