malloc: enable event callbacks for external memory
[dpdk.git] / lib / librte_eal / common / rte_malloc.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <stdint.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <sys/queue.h>
10
11 #include <rte_errno.h>
12 #include <rte_memcpy.h>
13 #include <rte_memory.h>
14 #include <rte_eal.h>
15 #include <rte_eal_memconfig.h>
16 #include <rte_branch_prediction.h>
17 #include <rte_debug.h>
18 #include <rte_launch.h>
19 #include <rte_per_lcore.h>
20 #include <rte_lcore.h>
21 #include <rte_common.h>
22 #include <rte_spinlock.h>
23
24 #include <rte_malloc.h>
25 #include "malloc_elem.h"
26 #include "malloc_heap.h"
27 #include "eal_memalloc.h"
28
29
30 /* Free the memory space back to heap */
31 void rte_free(void *addr)
32 {
33         if (addr == NULL) return;
34         if (malloc_heap_free(malloc_elem_from_data(addr)) < 0)
35                 RTE_LOG(ERR, EAL, "Error: Invalid memory\n");
36 }
37
38 /*
39  * Allocate memory on specified heap.
40  */
41 void *
42 rte_malloc_socket(const char *type, size_t size, unsigned int align,
43                 int socket_arg)
44 {
45         /* return NULL if size is 0 or alignment is not power-of-2 */
46         if (size == 0 || (align && !rte_is_power_of_2(align)))
47                 return NULL;
48
49         if (!rte_eal_has_hugepages())
50                 socket_arg = SOCKET_ID_ANY;
51
52         return malloc_heap_alloc(type, size, socket_arg, 0,
53                         align == 0 ? 1 : align, 0, false);
54 }
55
56 /*
57  * Allocate memory on default heap.
58  */
59 void *
60 rte_malloc(const char *type, size_t size, unsigned align)
61 {
62         return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
63 }
64
65 /*
66  * Allocate zero'd memory on specified heap.
67  */
68 void *
69 rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
70 {
71         return rte_malloc_socket(type, size, align, socket);
72 }
73
74 /*
75  * Allocate zero'd memory on default heap.
76  */
77 void *
78 rte_zmalloc(const char *type, size_t size, unsigned align)
79 {
80         return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
81 }
82
83 /*
84  * Allocate zero'd memory on specified heap.
85  */
86 void *
87 rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
88 {
89         return rte_zmalloc_socket(type, num * size, align, socket);
90 }
91
92 /*
93  * Allocate zero'd memory on default heap.
94  */
95 void *
96 rte_calloc(const char *type, size_t num, size_t size, unsigned align)
97 {
98         return rte_zmalloc(type, num * size, align);
99 }
100
101 /*
102  * Resize allocated memory.
103  */
104 void *
105 rte_realloc(void *ptr, size_t size, unsigned align)
106 {
107         if (ptr == NULL)
108                 return rte_malloc(NULL, size, align);
109
110         struct malloc_elem *elem = malloc_elem_from_data(ptr);
111         if (elem == NULL) {
112                 RTE_LOG(ERR, EAL, "Error: memory corruption detected\n");
113                 return NULL;
114         }
115
116         size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
117         /* check alignment matches first, and if ok, see if we can resize block */
118         if (RTE_PTR_ALIGN(ptr,align) == ptr &&
119                         malloc_heap_resize(elem, size) == 0)
120                 return ptr;
121
122         /* either alignment is off, or we have no room to expand,
123          * so move data. */
124         void *new_ptr = rte_malloc(NULL, size, align);
125         if (new_ptr == NULL)
126                 return NULL;
127         const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
128         rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
129         rte_free(ptr);
130
131         return new_ptr;
132 }
133
134 int
135 rte_malloc_validate(const void *ptr, size_t *size)
136 {
137         const struct malloc_elem *elem = malloc_elem_from_data(ptr);
138         if (!malloc_elem_cookies_ok(elem))
139                 return -1;
140         if (size != NULL)
141                 *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
142         return 0;
143 }
144
145 /*
146  * Function to retrieve data for heap on given socket
147  */
148 int
149 rte_malloc_get_socket_stats(int socket,
150                 struct rte_malloc_socket_stats *socket_stats)
151 {
152         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
153         int heap_idx, ret = -1;
154
155         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
156
157         heap_idx = malloc_socket_to_heap_id(socket);
158         if (heap_idx < 0)
159                 goto unlock;
160
161         ret = malloc_heap_get_stats(&mcfg->malloc_heaps[heap_idx],
162                         socket_stats);
163 unlock:
164         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
165
166         return ret;
167 }
168
169 /*
170  * Function to dump contents of all heaps
171  */
172 void __rte_experimental
173 rte_malloc_dump_heaps(FILE *f)
174 {
175         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
176         unsigned int idx;
177
178         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
179
180         for (idx = 0; idx < RTE_MAX_HEAPS; idx++) {
181                 fprintf(f, "Heap id: %u\n", idx);
182                 malloc_heap_dump(&mcfg->malloc_heaps[idx], f);
183         }
184
185         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
186 }
187
188 int
189 rte_malloc_heap_get_socket(const char *name)
190 {
191         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
192         struct malloc_heap *heap = NULL;
193         unsigned int idx;
194         int ret;
195
196         if (name == NULL ||
197                         strnlen(name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
198                         strnlen(name, RTE_HEAP_NAME_MAX_LEN) ==
199                                 RTE_HEAP_NAME_MAX_LEN) {
200                 rte_errno = EINVAL;
201                 return -1;
202         }
203         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
204         for (idx = 0; idx < RTE_MAX_HEAPS; idx++) {
205                 struct malloc_heap *tmp = &mcfg->malloc_heaps[idx];
206
207                 if (!strncmp(name, tmp->name, RTE_HEAP_NAME_MAX_LEN)) {
208                         heap = tmp;
209                         break;
210                 }
211         }
212
213         if (heap != NULL) {
214                 ret = heap->socket_id;
215         } else {
216                 rte_errno = ENOENT;
217                 ret = -1;
218         }
219         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
220
221         return ret;
222 }
223
224 int
225 rte_malloc_heap_socket_is_external(int socket_id)
226 {
227         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
228         unsigned int idx;
229         int ret = -1;
230
231         if (socket_id == SOCKET_ID_ANY)
232                 return 0;
233
234         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
235         for (idx = 0; idx < RTE_MAX_HEAPS; idx++) {
236                 struct malloc_heap *tmp = &mcfg->malloc_heaps[idx];
237
238                 if ((int)tmp->socket_id == socket_id) {
239                         /* external memory always has large socket ID's */
240                         ret = tmp->socket_id >= RTE_MAX_NUMA_NODES;
241                         break;
242                 }
243         }
244         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
245
246         return ret;
247 }
248
249 /*
250  * Print stats on memory type. If type is NULL, info on all types is printed
251  */
252 void
253 rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
254 {
255         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
256         unsigned int heap_id;
257         struct rte_malloc_socket_stats sock_stats;
258
259         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
260
261         /* Iterate through all initialised heaps */
262         for (heap_id = 0; heap_id < RTE_MAX_HEAPS; heap_id++) {
263                 struct malloc_heap *heap = &mcfg->malloc_heaps[heap_id];
264
265                 malloc_heap_get_stats(heap, &sock_stats);
266
267                 fprintf(f, "Heap id:%u\n", heap_id);
268                 fprintf(f, "\tHeap name:%s\n", heap->name);
269                 fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
270                 fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
271                 fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
272                 fprintf(f, "\tGreatest_free_size:%zu,\n",
273                                 sock_stats.greatest_free_size);
274                 fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
275                 fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
276         }
277         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
278         return;
279 }
280
281 /*
282  * TODO: Set limit to memory that can be allocated to memory type
283  */
284 int
285 rte_malloc_set_limit(__rte_unused const char *type,
286                 __rte_unused size_t max)
287 {
288         return 0;
289 }
290
291 /*
292  * Return the IO address of a virtual address obtained through rte_malloc
293  */
294 rte_iova_t
295 rte_malloc_virt2iova(const void *addr)
296 {
297         const struct rte_memseg *ms;
298         struct malloc_elem *elem = malloc_elem_from_data(addr);
299
300         if (elem == NULL)
301                 return RTE_BAD_IOVA;
302
303         if (!elem->msl->external && rte_eal_iova_mode() == RTE_IOVA_VA)
304                 return (uintptr_t) addr;
305
306         ms = rte_mem_virt2memseg(addr, elem->msl);
307         if (ms == NULL)
308                 return RTE_BAD_IOVA;
309
310         if (ms->iova == RTE_BAD_IOVA)
311                 return RTE_BAD_IOVA;
312
313         return ms->iova + RTE_PTR_DIFF(addr, ms->addr);
314 }
315
316 static struct malloc_heap *
317 find_named_heap(const char *name)
318 {
319         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
320         unsigned int i;
321
322         for (i = 0; i < RTE_MAX_HEAPS; i++) {
323                 struct malloc_heap *heap = &mcfg->malloc_heaps[i];
324
325                 if (!strncmp(name, heap->name, RTE_HEAP_NAME_MAX_LEN))
326                         return heap;
327         }
328         return NULL;
329 }
330
331 int
332 rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,
333                 rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz)
334 {
335         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
336         struct malloc_heap *heap = NULL;
337         unsigned int n;
338         int ret;
339
340         if (heap_name == NULL || va_addr == NULL ||
341                         page_sz == 0 || !rte_is_power_of_2(page_sz) ||
342                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
343                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
344                                 RTE_HEAP_NAME_MAX_LEN) {
345                 rte_errno = EINVAL;
346                 ret = -1;
347                 goto unlock;
348         }
349         rte_rwlock_write_lock(&mcfg->memory_hotplug_lock);
350
351         /* find our heap */
352         heap = find_named_heap(heap_name);
353         if (heap == NULL) {
354                 rte_errno = ENOENT;
355                 ret = -1;
356                 goto unlock;
357         }
358         if (heap->socket_id < RTE_MAX_NUMA_NODES) {
359                 /* cannot add memory to internal heaps */
360                 rte_errno = EPERM;
361                 ret = -1;
362                 goto unlock;
363         }
364         n = len / page_sz;
365         if (n != n_pages && iova_addrs != NULL) {
366                 rte_errno = EINVAL;
367                 ret = -1;
368                 goto unlock;
369         }
370
371         rte_spinlock_lock(&heap->lock);
372         ret = malloc_heap_add_external_memory(heap, va_addr, iova_addrs, n,
373                         page_sz);
374         rte_spinlock_unlock(&heap->lock);
375
376 unlock:
377         rte_rwlock_write_unlock(&mcfg->memory_hotplug_lock);
378
379         return ret;
380 }
381
382 int
383 rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len)
384 {
385         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
386         struct malloc_heap *heap = NULL;
387         int ret;
388
389         if (heap_name == NULL || va_addr == NULL || len == 0 ||
390                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
391                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
392                                 RTE_HEAP_NAME_MAX_LEN) {
393                 rte_errno = EINVAL;
394                 return -1;
395         }
396         rte_rwlock_write_lock(&mcfg->memory_hotplug_lock);
397         /* find our heap */
398         heap = find_named_heap(heap_name);
399         if (heap == NULL) {
400                 rte_errno = ENOENT;
401                 ret = -1;
402                 goto unlock;
403         }
404         if (heap->socket_id < RTE_MAX_NUMA_NODES) {
405                 /* cannot remove memory from internal heaps */
406                 rte_errno = EPERM;
407                 ret = -1;
408                 goto unlock;
409         }
410
411         rte_spinlock_lock(&heap->lock);
412         ret = malloc_heap_remove_external_memory(heap, va_addr, len);
413         rte_spinlock_unlock(&heap->lock);
414
415 unlock:
416         rte_rwlock_write_unlock(&mcfg->memory_hotplug_lock);
417
418         return ret;
419 }
420
421 struct sync_mem_walk_arg {
422         void *va_addr;
423         size_t len;
424         int result;
425         bool attach;
426 };
427
428 static int
429 sync_mem_walk(const struct rte_memseg_list *msl, void *arg)
430 {
431         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
432         struct sync_mem_walk_arg *wa = arg;
433         size_t len = msl->page_sz * msl->memseg_arr.len;
434
435         if (msl->base_va == wa->va_addr &&
436                         len == wa->len) {
437                 struct rte_memseg_list *found_msl;
438                 int msl_idx, ret;
439
440                 /* msl is const */
441                 msl_idx = msl - mcfg->memsegs;
442                 found_msl = &mcfg->memsegs[msl_idx];
443
444                 if (wa->attach) {
445                         ret = rte_fbarray_attach(&found_msl->memseg_arr);
446                 } else {
447                         /* notify all subscribers that a memory area is about to
448                          * be removed
449                          */
450                         eal_memalloc_mem_event_notify(RTE_MEM_EVENT_FREE,
451                                         msl->base_va, msl->len);
452                         ret = rte_fbarray_detach(&found_msl->memseg_arr);
453                 }
454
455                 if (ret < 0) {
456                         wa->result = -rte_errno;
457                 } else {
458                         /* notify all subscribers that a new memory area was
459                          * added
460                          */
461                         if (wa->attach)
462                                 eal_memalloc_mem_event_notify(
463                                                 RTE_MEM_EVENT_ALLOC,
464                                                 msl->base_va, msl->len);
465                         wa->result = 0;
466                 }
467                 return 1;
468         }
469         return 0;
470 }
471
472 static int
473 sync_memory(const char *heap_name, void *va_addr, size_t len, bool attach)
474 {
475         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
476         struct malloc_heap *heap = NULL;
477         struct sync_mem_walk_arg wa;
478         int ret;
479
480         if (heap_name == NULL || va_addr == NULL || len == 0 ||
481                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
482                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
483                                 RTE_HEAP_NAME_MAX_LEN) {
484                 rte_errno = EINVAL;
485                 return -1;
486         }
487         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
488
489         /* find our heap */
490         heap = find_named_heap(heap_name);
491         if (heap == NULL) {
492                 rte_errno = ENOENT;
493                 ret = -1;
494                 goto unlock;
495         }
496         /* we shouldn't be able to sync to internal heaps */
497         if (heap->socket_id < RTE_MAX_NUMA_NODES) {
498                 rte_errno = EPERM;
499                 ret = -1;
500                 goto unlock;
501         }
502
503         /* find corresponding memseg list to sync to */
504         wa.va_addr = va_addr;
505         wa.len = len;
506         wa.result = -ENOENT; /* fail unless explicitly told to succeed */
507         wa.attach = attach;
508
509         /* we're already holding a read lock */
510         rte_memseg_list_walk_thread_unsafe(sync_mem_walk, &wa);
511
512         if (wa.result < 0) {
513                 rte_errno = -wa.result;
514                 ret = -1;
515         } else {
516                 /* notify all subscribers that a new memory area was added */
517                 if (attach)
518                         eal_memalloc_mem_event_notify(RTE_MEM_EVENT_ALLOC,
519                                         va_addr, len);
520                 ret = 0;
521         }
522 unlock:
523         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
524         return ret;
525 }
526
527 int
528 rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)
529 {
530         return sync_memory(heap_name, va_addr, len, true);
531 }
532
533 int
534 rte_malloc_heap_memory_detach(const char *heap_name, void *va_addr, size_t len)
535 {
536         return sync_memory(heap_name, va_addr, len, false);
537 }
538
539 int
540 rte_malloc_heap_create(const char *heap_name)
541 {
542         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
543         struct malloc_heap *heap = NULL;
544         int i, ret;
545
546         if (heap_name == NULL ||
547                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
548                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
549                                 RTE_HEAP_NAME_MAX_LEN) {
550                 rte_errno = EINVAL;
551                 return -1;
552         }
553         /* check if there is space in the heap list, or if heap with this name
554          * already exists.
555          */
556         rte_rwlock_write_lock(&mcfg->memory_hotplug_lock);
557
558         for (i = 0; i < RTE_MAX_HEAPS; i++) {
559                 struct malloc_heap *tmp = &mcfg->malloc_heaps[i];
560                 /* existing heap */
561                 if (strncmp(heap_name, tmp->name,
562                                 RTE_HEAP_NAME_MAX_LEN) == 0) {
563                         RTE_LOG(ERR, EAL, "Heap %s already exists\n",
564                                 heap_name);
565                         rte_errno = EEXIST;
566                         ret = -1;
567                         goto unlock;
568                 }
569                 /* empty heap */
570                 if (strnlen(tmp->name, RTE_HEAP_NAME_MAX_LEN) == 0) {
571                         heap = tmp;
572                         break;
573                 }
574         }
575         if (heap == NULL) {
576                 RTE_LOG(ERR, EAL, "Cannot create new heap: no space\n");
577                 rte_errno = ENOSPC;
578                 ret = -1;
579                 goto unlock;
580         }
581
582         /* we're sure that we can create a new heap, so do it */
583         ret = malloc_heap_create(heap, heap_name);
584 unlock:
585         rte_rwlock_write_unlock(&mcfg->memory_hotplug_lock);
586
587         return ret;
588 }
589
590 int
591 rte_malloc_heap_destroy(const char *heap_name)
592 {
593         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
594         struct malloc_heap *heap = NULL;
595         int ret;
596
597         if (heap_name == NULL ||
598                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
599                         strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
600                                 RTE_HEAP_NAME_MAX_LEN) {
601                 rte_errno = EINVAL;
602                 return -1;
603         }
604         rte_rwlock_write_lock(&mcfg->memory_hotplug_lock);
605
606         /* start from non-socket heaps */
607         heap = find_named_heap(heap_name);
608         if (heap == NULL) {
609                 RTE_LOG(ERR, EAL, "Heap %s not found\n", heap_name);
610                 rte_errno = ENOENT;
611                 ret = -1;
612                 goto unlock;
613         }
614         /* we shouldn't be able to destroy internal heaps */
615         if (heap->socket_id < RTE_MAX_NUMA_NODES) {
616                 rte_errno = EPERM;
617                 ret = -1;
618                 goto unlock;
619         }
620         /* sanity checks done, now we can destroy the heap */
621         rte_spinlock_lock(&heap->lock);
622         ret = malloc_heap_destroy(heap);
623
624         /* if we failed, lock is still active */
625         if (ret < 0)
626                 rte_spinlock_unlock(&heap->lock);
627 unlock:
628         rte_rwlock_write_unlock(&mcfg->memory_hotplug_lock);
629
630         return ret;
631 }