xref: /dpdk/lib/eal/common/rte_malloc.c (revision 6cc51b1293ceac4a77d4bf7ac91a8bbd59e1f78c)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2019 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
599a2dd95SBruce Richardson #include <stdint.h>
699a2dd95SBruce Richardson #include <stddef.h>
799a2dd95SBruce Richardson #include <stdio.h>
899a2dd95SBruce Richardson #include <string.h>
999a2dd95SBruce Richardson #include <sys/queue.h>
1099a2dd95SBruce Richardson 
1199a2dd95SBruce Richardson #include <rte_errno.h>
1299a2dd95SBruce Richardson #include <rte_memcpy.h>
1399a2dd95SBruce Richardson #include <rte_memory.h>
1499a2dd95SBruce Richardson #include <rte_eal.h>
1599a2dd95SBruce Richardson #include <rte_eal_memconfig.h>
1699a2dd95SBruce Richardson #include <rte_branch_prediction.h>
1799a2dd95SBruce Richardson #include <rte_debug.h>
1899a2dd95SBruce Richardson #include <rte_launch.h>
1999a2dd95SBruce Richardson #include <rte_per_lcore.h>
2099a2dd95SBruce Richardson #include <rte_lcore.h>
2199a2dd95SBruce Richardson #include <rte_common.h>
2299a2dd95SBruce Richardson #include <rte_spinlock.h>
2399a2dd95SBruce Richardson 
2499a2dd95SBruce Richardson #include <rte_eal_trace.h>
2599a2dd95SBruce Richardson 
2699a2dd95SBruce Richardson #include <rte_malloc.h>
2799a2dd95SBruce Richardson #include "malloc_elem.h"
2899a2dd95SBruce Richardson #include "malloc_heap.h"
2999a2dd95SBruce Richardson #include "eal_memalloc.h"
3099a2dd95SBruce Richardson #include "eal_memcfg.h"
3199a2dd95SBruce Richardson #include "eal_private.h"
3299a2dd95SBruce Richardson 
3399a2dd95SBruce Richardson 
3499a2dd95SBruce Richardson /* Free the memory space back to heap */
3599a2dd95SBruce Richardson static void
3699a2dd95SBruce Richardson mem_free(void *addr, const bool trace_ena)
3799a2dd95SBruce Richardson {
3899a2dd95SBruce Richardson 	if (trace_ena)
3999a2dd95SBruce Richardson 		rte_eal_trace_mem_free(addr);
4099a2dd95SBruce Richardson 
4199a2dd95SBruce Richardson 	if (addr == NULL) return;
4299a2dd95SBruce Richardson 	if (malloc_heap_free(malloc_elem_from_data(addr)) < 0)
4399a2dd95SBruce Richardson 		RTE_LOG(ERR, EAL, "Error: Invalid memory\n");
4499a2dd95SBruce Richardson }
4599a2dd95SBruce Richardson 
4699a2dd95SBruce Richardson void
4799a2dd95SBruce Richardson rte_free(void *addr)
4899a2dd95SBruce Richardson {
4999a2dd95SBruce Richardson 	return mem_free(addr, true);
5099a2dd95SBruce Richardson }
5199a2dd95SBruce Richardson 
5299a2dd95SBruce Richardson void
5399a2dd95SBruce Richardson eal_free_no_trace(void *addr)
5499a2dd95SBruce Richardson {
5599a2dd95SBruce Richardson 	return mem_free(addr, false);
5699a2dd95SBruce Richardson }
5799a2dd95SBruce Richardson 
5899a2dd95SBruce Richardson static void *
5999a2dd95SBruce Richardson malloc_socket(const char *type, size_t size, unsigned int align,
6099a2dd95SBruce Richardson 		int socket_arg, const bool trace_ena)
6199a2dd95SBruce Richardson {
6299a2dd95SBruce Richardson 	void *ptr;
6399a2dd95SBruce Richardson 
6499a2dd95SBruce Richardson 	/* return NULL if size is 0 or alignment is not power-of-2 */
6599a2dd95SBruce Richardson 	if (size == 0 || (align && !rte_is_power_of_2(align)))
6699a2dd95SBruce Richardson 		return NULL;
6799a2dd95SBruce Richardson 
6899a2dd95SBruce Richardson 	/* if there are no hugepages and if we are not allocating from an
6999a2dd95SBruce Richardson 	 * external heap, use memory from any socket available. checking for
7099a2dd95SBruce Richardson 	 * socket being external may return -1 in case of invalid socket, but
7199a2dd95SBruce Richardson 	 * that's OK - if there are no hugepages, it doesn't matter.
7299a2dd95SBruce Richardson 	 */
7399a2dd95SBruce Richardson 	if (rte_malloc_heap_socket_is_external(socket_arg) != 1 &&
7499a2dd95SBruce Richardson 				!rte_eal_has_hugepages())
7599a2dd95SBruce Richardson 		socket_arg = SOCKET_ID_ANY;
7699a2dd95SBruce Richardson 
7799a2dd95SBruce Richardson 	ptr = malloc_heap_alloc(type, size, socket_arg, 0,
7899a2dd95SBruce Richardson 			align == 0 ? 1 : align, 0, false);
7999a2dd95SBruce Richardson 
8099a2dd95SBruce Richardson 	if (trace_ena)
8199a2dd95SBruce Richardson 		rte_eal_trace_mem_malloc(type, size, align, socket_arg, ptr);
8299a2dd95SBruce Richardson 	return ptr;
8399a2dd95SBruce Richardson }
8499a2dd95SBruce Richardson 
8599a2dd95SBruce Richardson /*
8699a2dd95SBruce Richardson  * Allocate memory on specified heap.
8799a2dd95SBruce Richardson  */
8899a2dd95SBruce Richardson void *
8999a2dd95SBruce Richardson rte_malloc_socket(const char *type, size_t size, unsigned int align,
9099a2dd95SBruce Richardson 		int socket_arg)
9199a2dd95SBruce Richardson {
9299a2dd95SBruce Richardson 	return malloc_socket(type, size, align, socket_arg, true);
9399a2dd95SBruce Richardson }
9499a2dd95SBruce Richardson 
9599a2dd95SBruce Richardson void *
9699a2dd95SBruce Richardson eal_malloc_no_trace(const char *type, size_t size, unsigned int align)
9799a2dd95SBruce Richardson {
9899a2dd95SBruce Richardson 	return malloc_socket(type, size, align, SOCKET_ID_ANY, false);
9999a2dd95SBruce Richardson }
10099a2dd95SBruce Richardson 
10199a2dd95SBruce Richardson /*
10299a2dd95SBruce Richardson  * Allocate memory on default heap.
10399a2dd95SBruce Richardson  */
10499a2dd95SBruce Richardson void *
10599a2dd95SBruce Richardson rte_malloc(const char *type, size_t size, unsigned align)
10699a2dd95SBruce Richardson {
10799a2dd95SBruce Richardson 	return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
10899a2dd95SBruce Richardson }
10999a2dd95SBruce Richardson 
11099a2dd95SBruce Richardson /*
11199a2dd95SBruce Richardson  * Allocate zero'd memory on specified heap.
11299a2dd95SBruce Richardson  */
11399a2dd95SBruce Richardson void *
11499a2dd95SBruce Richardson rte_zmalloc_socket(const char *type, size_t size, unsigned align, int socket)
11599a2dd95SBruce Richardson {
11699a2dd95SBruce Richardson 	void *ptr = rte_malloc_socket(type, size, align, socket);
11799a2dd95SBruce Richardson 
11899a2dd95SBruce Richardson #ifdef RTE_MALLOC_DEBUG
11999a2dd95SBruce Richardson 	/*
12099a2dd95SBruce Richardson 	 * If DEBUG is enabled, then freed memory is marked with poison
12199a2dd95SBruce Richardson 	 * value and set to zero on allocation.
12299a2dd95SBruce Richardson 	 * If DEBUG is not enabled then  memory is already zeroed.
12399a2dd95SBruce Richardson 	 */
12499a2dd95SBruce Richardson 	if (ptr != NULL)
12599a2dd95SBruce Richardson 		memset(ptr, 0, size);
12699a2dd95SBruce Richardson #endif
12799a2dd95SBruce Richardson 
12899a2dd95SBruce Richardson 	rte_eal_trace_mem_zmalloc(type, size, align, socket, ptr);
12999a2dd95SBruce Richardson 	return ptr;
13099a2dd95SBruce Richardson }
13199a2dd95SBruce Richardson 
13299a2dd95SBruce Richardson /*
13399a2dd95SBruce Richardson  * Allocate zero'd memory on default heap.
13499a2dd95SBruce Richardson  */
13599a2dd95SBruce Richardson void *
13699a2dd95SBruce Richardson rte_zmalloc(const char *type, size_t size, unsigned align)
13799a2dd95SBruce Richardson {
13899a2dd95SBruce Richardson 	return rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
13999a2dd95SBruce Richardson }
14099a2dd95SBruce Richardson 
14199a2dd95SBruce Richardson /*
14299a2dd95SBruce Richardson  * Allocate zero'd memory on specified heap.
14399a2dd95SBruce Richardson  */
14499a2dd95SBruce Richardson void *
14599a2dd95SBruce Richardson rte_calloc_socket(const char *type, size_t num, size_t size, unsigned align, int socket)
14699a2dd95SBruce Richardson {
14799a2dd95SBruce Richardson 	return rte_zmalloc_socket(type, num * size, align, socket);
14899a2dd95SBruce Richardson }
14999a2dd95SBruce Richardson 
15099a2dd95SBruce Richardson /*
15199a2dd95SBruce Richardson  * Allocate zero'd memory on default heap.
15299a2dd95SBruce Richardson  */
15399a2dd95SBruce Richardson void *
15499a2dd95SBruce Richardson rte_calloc(const char *type, size_t num, size_t size, unsigned align)
15599a2dd95SBruce Richardson {
15699a2dd95SBruce Richardson 	return rte_zmalloc(type, num * size, align);
15799a2dd95SBruce Richardson }
15899a2dd95SBruce Richardson 
15999a2dd95SBruce Richardson /*
16099a2dd95SBruce Richardson  * Resize allocated memory on specified heap.
16199a2dd95SBruce Richardson  */
16299a2dd95SBruce Richardson void *
16399a2dd95SBruce Richardson rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket)
16499a2dd95SBruce Richardson {
165*6cc51b12SZhihong Peng 	size_t user_size;
166*6cc51b12SZhihong Peng 
16799a2dd95SBruce Richardson 	if (ptr == NULL)
16899a2dd95SBruce Richardson 		return rte_malloc_socket(NULL, size, align, socket);
16999a2dd95SBruce Richardson 
17099a2dd95SBruce Richardson 	struct malloc_elem *elem = malloc_elem_from_data(ptr);
17199a2dd95SBruce Richardson 	if (elem == NULL) {
17299a2dd95SBruce Richardson 		RTE_LOG(ERR, EAL, "Error: memory corruption detected\n");
17399a2dd95SBruce Richardson 		return NULL;
17499a2dd95SBruce Richardson 	}
17599a2dd95SBruce Richardson 
176*6cc51b12SZhihong Peng 	user_size = size;
177*6cc51b12SZhihong Peng 
17899a2dd95SBruce Richardson 	size = RTE_CACHE_LINE_ROUNDUP(size), align = RTE_CACHE_LINE_ROUNDUP(align);
17999a2dd95SBruce Richardson 
18099a2dd95SBruce Richardson 	/* check requested socket id and alignment matches first, and if ok,
18199a2dd95SBruce Richardson 	 * see if we can resize block
18299a2dd95SBruce Richardson 	 */
18399a2dd95SBruce Richardson 	if ((socket == SOCKET_ID_ANY ||
18499a2dd95SBruce Richardson 	     (unsigned int)socket == elem->heap->socket_id) &&
18599a2dd95SBruce Richardson 			RTE_PTR_ALIGN(ptr, align) == ptr &&
18699a2dd95SBruce Richardson 			malloc_heap_resize(elem, size) == 0) {
18799a2dd95SBruce Richardson 		rte_eal_trace_mem_realloc(size, align, socket, ptr);
188*6cc51b12SZhihong Peng 
189*6cc51b12SZhihong Peng 		asan_set_redzone(elem, user_size);
190*6cc51b12SZhihong Peng 
19199a2dd95SBruce Richardson 		return ptr;
19299a2dd95SBruce Richardson 	}
19399a2dd95SBruce Richardson 
19499a2dd95SBruce Richardson 	/* either requested socket id doesn't match, alignment is off
19599a2dd95SBruce Richardson 	 * or we have no room to expand,
19699a2dd95SBruce Richardson 	 * so move the data.
19799a2dd95SBruce Richardson 	 */
19899a2dd95SBruce Richardson 	void *new_ptr = rte_malloc_socket(NULL, size, align, socket);
19999a2dd95SBruce Richardson 	if (new_ptr == NULL)
20099a2dd95SBruce Richardson 		return NULL;
20199a2dd95SBruce Richardson 	/* elem: |pad|data_elem|data|trailer| */
202*6cc51b12SZhihong Peng 	const size_t old_size = old_malloc_size(elem);
20399a2dd95SBruce Richardson 	rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
20499a2dd95SBruce Richardson 	rte_free(ptr);
20599a2dd95SBruce Richardson 
20699a2dd95SBruce Richardson 	rte_eal_trace_mem_realloc(size, align, socket, new_ptr);
20799a2dd95SBruce Richardson 	return new_ptr;
20899a2dd95SBruce Richardson }
20999a2dd95SBruce Richardson 
21099a2dd95SBruce Richardson /*
21199a2dd95SBruce Richardson  * Resize allocated memory.
21299a2dd95SBruce Richardson  */
21399a2dd95SBruce Richardson void *
21499a2dd95SBruce Richardson rte_realloc(void *ptr, size_t size, unsigned int align)
21599a2dd95SBruce Richardson {
21699a2dd95SBruce Richardson 	return rte_realloc_socket(ptr, size, align, SOCKET_ID_ANY);
21799a2dd95SBruce Richardson }
21899a2dd95SBruce Richardson 
21999a2dd95SBruce Richardson int
22099a2dd95SBruce Richardson rte_malloc_validate(const void *ptr, size_t *size)
22199a2dd95SBruce Richardson {
22299a2dd95SBruce Richardson 	const struct malloc_elem *elem = malloc_elem_from_data(ptr);
22399a2dd95SBruce Richardson 	if (!malloc_elem_cookies_ok(elem))
22499a2dd95SBruce Richardson 		return -1;
22599a2dd95SBruce Richardson 	if (size != NULL)
22699a2dd95SBruce Richardson 		*size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
22799a2dd95SBruce Richardson 	return 0;
22899a2dd95SBruce Richardson }
22999a2dd95SBruce Richardson 
23099a2dd95SBruce Richardson /*
23199a2dd95SBruce Richardson  * Function to retrieve data for heap on given socket
23299a2dd95SBruce Richardson  */
23399a2dd95SBruce Richardson int
23499a2dd95SBruce Richardson rte_malloc_get_socket_stats(int socket,
23599a2dd95SBruce Richardson 		struct rte_malloc_socket_stats *socket_stats)
23699a2dd95SBruce Richardson {
23799a2dd95SBruce Richardson 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
23899a2dd95SBruce Richardson 	int heap_idx;
23999a2dd95SBruce Richardson 
24099a2dd95SBruce Richardson 	heap_idx = malloc_socket_to_heap_id(socket);
24199a2dd95SBruce Richardson 	if (heap_idx < 0)
24299a2dd95SBruce Richardson 		return -1;
24399a2dd95SBruce Richardson 
24499a2dd95SBruce Richardson 	return malloc_heap_get_stats(&mcfg->malloc_heaps[heap_idx],
24599a2dd95SBruce Richardson 			socket_stats);
24699a2dd95SBruce Richardson }
24799a2dd95SBruce Richardson 
24899a2dd95SBruce Richardson /*
24999a2dd95SBruce Richardson  * Function to dump contents of all heaps
25099a2dd95SBruce Richardson  */
25199a2dd95SBruce Richardson void
25299a2dd95SBruce Richardson rte_malloc_dump_heaps(FILE *f)
25399a2dd95SBruce Richardson {
25499a2dd95SBruce Richardson 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
25599a2dd95SBruce Richardson 	unsigned int idx;
25699a2dd95SBruce Richardson 
25799a2dd95SBruce Richardson 	for (idx = 0; idx < RTE_MAX_HEAPS; idx++) {
25899a2dd95SBruce Richardson 		fprintf(f, "Heap id: %u\n", idx);
25999a2dd95SBruce Richardson 		malloc_heap_dump(&mcfg->malloc_heaps[idx], f);
26099a2dd95SBruce Richardson 	}
26199a2dd95SBruce Richardson }
26299a2dd95SBruce Richardson 
26399a2dd95SBruce Richardson int
26499a2dd95SBruce Richardson rte_malloc_heap_get_socket(const char *name)
26599a2dd95SBruce Richardson {
26699a2dd95SBruce Richardson 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
26799a2dd95SBruce Richardson 	struct malloc_heap *heap = NULL;
26899a2dd95SBruce Richardson 	unsigned int idx;
26999a2dd95SBruce Richardson 	int ret;
27099a2dd95SBruce Richardson 
27199a2dd95SBruce Richardson 	if (name == NULL ||
27299a2dd95SBruce Richardson 			strnlen(name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
27399a2dd95SBruce Richardson 			strnlen(name, RTE_HEAP_NAME_MAX_LEN) ==
27499a2dd95SBruce Richardson 				RTE_HEAP_NAME_MAX_LEN) {
27599a2dd95SBruce Richardson 		rte_errno = EINVAL;
27699a2dd95SBruce Richardson 		return -1;
27799a2dd95SBruce Richardson 	}
27899a2dd95SBruce Richardson 	rte_mcfg_mem_read_lock();
27999a2dd95SBruce Richardson 	for (idx = 0; idx < RTE_MAX_HEAPS; idx++) {
28099a2dd95SBruce Richardson 		struct malloc_heap *tmp = &mcfg->malloc_heaps[idx];
28199a2dd95SBruce Richardson 
28299a2dd95SBruce Richardson 		if (!strncmp(name, tmp->name, RTE_HEAP_NAME_MAX_LEN)) {
28399a2dd95SBruce Richardson 			heap = tmp;
28499a2dd95SBruce Richardson 			break;
28599a2dd95SBruce Richardson 		}
28699a2dd95SBruce Richardson 	}
28799a2dd95SBruce Richardson 
28899a2dd95SBruce Richardson 	if (heap != NULL) {
28999a2dd95SBruce Richardson 		ret = heap->socket_id;
29099a2dd95SBruce Richardson 	} else {
29199a2dd95SBruce Richardson 		rte_errno = ENOENT;
29299a2dd95SBruce Richardson 		ret = -1;
29399a2dd95SBruce Richardson 	}
29499a2dd95SBruce Richardson 	rte_mcfg_mem_read_unlock();
29599a2dd95SBruce Richardson 
29699a2dd95SBruce Richardson 	return ret;
29799a2dd95SBruce Richardson }
29899a2dd95SBruce Richardson 
29999a2dd95SBruce Richardson int
30099a2dd95SBruce Richardson rte_malloc_heap_socket_is_external(int socket_id)
30199a2dd95SBruce Richardson {
30299a2dd95SBruce Richardson 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
30399a2dd95SBruce Richardson 	unsigned int idx;
30499a2dd95SBruce Richardson 	int ret = -1;
30599a2dd95SBruce Richardson 
30699a2dd95SBruce Richardson 	if (socket_id == SOCKET_ID_ANY)
30799a2dd95SBruce Richardson 		return 0;
30899a2dd95SBruce Richardson 
30999a2dd95SBruce Richardson 	rte_mcfg_mem_read_lock();
31099a2dd95SBruce Richardson 	for (idx = 0; idx < RTE_MAX_HEAPS; idx++) {
31199a2dd95SBruce Richardson 		struct malloc_heap *tmp = &mcfg->malloc_heaps[idx];
31299a2dd95SBruce Richardson 
31399a2dd95SBruce Richardson 		if ((int)tmp->socket_id == socket_id) {
31499a2dd95SBruce Richardson 			/* external memory always has large socket ID's */
31599a2dd95SBruce Richardson 			ret = tmp->socket_id >= RTE_MAX_NUMA_NODES;
31699a2dd95SBruce Richardson 			break;
31799a2dd95SBruce Richardson 		}
31899a2dd95SBruce Richardson 	}
31999a2dd95SBruce Richardson 	rte_mcfg_mem_read_unlock();
32099a2dd95SBruce Richardson 
32199a2dd95SBruce Richardson 	return ret;
32299a2dd95SBruce Richardson }
32399a2dd95SBruce Richardson 
32499a2dd95SBruce Richardson /*
32599a2dd95SBruce Richardson  * Print stats on memory type. If type is NULL, info on all types is printed
32699a2dd95SBruce Richardson  */
32799a2dd95SBruce Richardson void
32899a2dd95SBruce Richardson rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)
32999a2dd95SBruce Richardson {
33099a2dd95SBruce Richardson 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
33199a2dd95SBruce Richardson 	unsigned int heap_id;
33299a2dd95SBruce Richardson 	struct rte_malloc_socket_stats sock_stats;
33399a2dd95SBruce Richardson 
33499a2dd95SBruce Richardson 	/* Iterate through all initialised heaps */
33599a2dd95SBruce Richardson 	for (heap_id = 0; heap_id < RTE_MAX_HEAPS; heap_id++) {
33699a2dd95SBruce Richardson 		struct malloc_heap *heap = &mcfg->malloc_heaps[heap_id];
33799a2dd95SBruce Richardson 
33899a2dd95SBruce Richardson 		malloc_heap_get_stats(heap, &sock_stats);
33999a2dd95SBruce Richardson 
34099a2dd95SBruce Richardson 		fprintf(f, "Heap id:%u\n", heap_id);
34199a2dd95SBruce Richardson 		fprintf(f, "\tHeap name:%s\n", heap->name);
34299a2dd95SBruce Richardson 		fprintf(f, "\tHeap_size:%zu,\n", sock_stats.heap_totalsz_bytes);
34399a2dd95SBruce Richardson 		fprintf(f, "\tFree_size:%zu,\n", sock_stats.heap_freesz_bytes);
34499a2dd95SBruce Richardson 		fprintf(f, "\tAlloc_size:%zu,\n", sock_stats.heap_allocsz_bytes);
34599a2dd95SBruce Richardson 		fprintf(f, "\tGreatest_free_size:%zu,\n",
34699a2dd95SBruce Richardson 				sock_stats.greatest_free_size);
34799a2dd95SBruce Richardson 		fprintf(f, "\tAlloc_count:%u,\n",sock_stats.alloc_count);
34899a2dd95SBruce Richardson 		fprintf(f, "\tFree_count:%u,\n", sock_stats.free_count);
34999a2dd95SBruce Richardson 	}
35099a2dd95SBruce Richardson 	return;
35199a2dd95SBruce Richardson }
35299a2dd95SBruce Richardson 
35399a2dd95SBruce Richardson /*
35499a2dd95SBruce Richardson  * TODO: Set limit to memory that can be allocated to memory type
35599a2dd95SBruce Richardson  */
35699a2dd95SBruce Richardson int
35799a2dd95SBruce Richardson rte_malloc_set_limit(__rte_unused const char *type,
35899a2dd95SBruce Richardson 		__rte_unused size_t max)
35999a2dd95SBruce Richardson {
36099a2dd95SBruce Richardson 	return 0;
36199a2dd95SBruce Richardson }
36299a2dd95SBruce Richardson 
36399a2dd95SBruce Richardson /*
36499a2dd95SBruce Richardson  * Return the IO address of a virtual address obtained through rte_malloc
36599a2dd95SBruce Richardson  */
36699a2dd95SBruce Richardson rte_iova_t
36799a2dd95SBruce Richardson rte_malloc_virt2iova(const void *addr)
36899a2dd95SBruce Richardson {
36999a2dd95SBruce Richardson 	const struct rte_memseg *ms;
37099a2dd95SBruce Richardson 	struct malloc_elem *elem = malloc_elem_from_data(addr);
37199a2dd95SBruce Richardson 
37299a2dd95SBruce Richardson 	if (elem == NULL)
37399a2dd95SBruce Richardson 		return RTE_BAD_IOVA;
37499a2dd95SBruce Richardson 
37599a2dd95SBruce Richardson 	if (!elem->msl->external && rte_eal_iova_mode() == RTE_IOVA_VA)
37699a2dd95SBruce Richardson 		return (uintptr_t) addr;
37799a2dd95SBruce Richardson 
37899a2dd95SBruce Richardson 	ms = rte_mem_virt2memseg(addr, elem->msl);
37999a2dd95SBruce Richardson 	if (ms == NULL)
38099a2dd95SBruce Richardson 		return RTE_BAD_IOVA;
38199a2dd95SBruce Richardson 
38299a2dd95SBruce Richardson 	if (ms->iova == RTE_BAD_IOVA)
38399a2dd95SBruce Richardson 		return RTE_BAD_IOVA;
38499a2dd95SBruce Richardson 
38599a2dd95SBruce Richardson 	return ms->iova + RTE_PTR_DIFF(addr, ms->addr);
38699a2dd95SBruce Richardson }
38799a2dd95SBruce Richardson 
38899a2dd95SBruce Richardson static struct malloc_heap *
38999a2dd95SBruce Richardson find_named_heap(const char *name)
39099a2dd95SBruce Richardson {
39199a2dd95SBruce Richardson 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
39299a2dd95SBruce Richardson 	unsigned int i;
39399a2dd95SBruce Richardson 
39499a2dd95SBruce Richardson 	for (i = 0; i < RTE_MAX_HEAPS; i++) {
39599a2dd95SBruce Richardson 		struct malloc_heap *heap = &mcfg->malloc_heaps[i];
39699a2dd95SBruce Richardson 
39799a2dd95SBruce Richardson 		if (!strncmp(name, heap->name, RTE_HEAP_NAME_MAX_LEN))
39899a2dd95SBruce Richardson 			return heap;
39999a2dd95SBruce Richardson 	}
40099a2dd95SBruce Richardson 	return NULL;
40199a2dd95SBruce Richardson }
40299a2dd95SBruce Richardson 
40399a2dd95SBruce Richardson int
40499a2dd95SBruce Richardson rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,
40599a2dd95SBruce Richardson 		rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz)
40699a2dd95SBruce Richardson {
40799a2dd95SBruce Richardson 	struct malloc_heap *heap = NULL;
40899a2dd95SBruce Richardson 	struct rte_memseg_list *msl;
40999a2dd95SBruce Richardson 	unsigned int n;
41099a2dd95SBruce Richardson 	int ret;
41199a2dd95SBruce Richardson 
41299a2dd95SBruce Richardson 	if (heap_name == NULL || va_addr == NULL ||
41399a2dd95SBruce Richardson 			page_sz == 0 || !rte_is_power_of_2(page_sz) ||
41499a2dd95SBruce Richardson 			RTE_ALIGN(len, page_sz) != len ||
41599a2dd95SBruce Richardson 			!rte_is_aligned(va_addr, page_sz) ||
41699a2dd95SBruce Richardson 			((len / page_sz) != n_pages && iova_addrs != NULL) ||
41799a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
41899a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
41999a2dd95SBruce Richardson 				RTE_HEAP_NAME_MAX_LEN) {
42099a2dd95SBruce Richardson 		rte_errno = EINVAL;
42199a2dd95SBruce Richardson 		return -1;
42299a2dd95SBruce Richardson 	}
42399a2dd95SBruce Richardson 	rte_mcfg_mem_write_lock();
42499a2dd95SBruce Richardson 
42599a2dd95SBruce Richardson 	/* find our heap */
42699a2dd95SBruce Richardson 	heap = find_named_heap(heap_name);
42799a2dd95SBruce Richardson 	if (heap == NULL) {
42899a2dd95SBruce Richardson 		rte_errno = ENOENT;
42999a2dd95SBruce Richardson 		ret = -1;
43099a2dd95SBruce Richardson 		goto unlock;
43199a2dd95SBruce Richardson 	}
43299a2dd95SBruce Richardson 	if (heap->socket_id < RTE_MAX_NUMA_NODES) {
43399a2dd95SBruce Richardson 		/* cannot add memory to internal heaps */
43499a2dd95SBruce Richardson 		rte_errno = EPERM;
43599a2dd95SBruce Richardson 		ret = -1;
43699a2dd95SBruce Richardson 		goto unlock;
43799a2dd95SBruce Richardson 	}
43899a2dd95SBruce Richardson 	n = len / page_sz;
43999a2dd95SBruce Richardson 
44099a2dd95SBruce Richardson 	msl = malloc_heap_create_external_seg(va_addr, iova_addrs, n, page_sz,
44199a2dd95SBruce Richardson 			heap_name, heap->socket_id);
44299a2dd95SBruce Richardson 	if (msl == NULL) {
44399a2dd95SBruce Richardson 		ret = -1;
44499a2dd95SBruce Richardson 		goto unlock;
44599a2dd95SBruce Richardson 	}
44699a2dd95SBruce Richardson 
44799a2dd95SBruce Richardson 	rte_spinlock_lock(&heap->lock);
44899a2dd95SBruce Richardson 	ret = malloc_heap_add_external_memory(heap, msl);
44999a2dd95SBruce Richardson 	msl->heap = 1; /* mark it as heap segment */
45099a2dd95SBruce Richardson 	rte_spinlock_unlock(&heap->lock);
45199a2dd95SBruce Richardson 
45299a2dd95SBruce Richardson unlock:
45399a2dd95SBruce Richardson 	rte_mcfg_mem_write_unlock();
45499a2dd95SBruce Richardson 
45599a2dd95SBruce Richardson 	return ret;
45699a2dd95SBruce Richardson }
45799a2dd95SBruce Richardson 
45899a2dd95SBruce Richardson int
45999a2dd95SBruce Richardson rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len)
46099a2dd95SBruce Richardson {
46199a2dd95SBruce Richardson 	struct malloc_heap *heap = NULL;
46299a2dd95SBruce Richardson 	struct rte_memseg_list *msl;
46399a2dd95SBruce Richardson 	int ret;
46499a2dd95SBruce Richardson 
46599a2dd95SBruce Richardson 	if (heap_name == NULL || va_addr == NULL || len == 0 ||
46699a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
46799a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
46899a2dd95SBruce Richardson 				RTE_HEAP_NAME_MAX_LEN) {
46999a2dd95SBruce Richardson 		rte_errno = EINVAL;
47099a2dd95SBruce Richardson 		return -1;
47199a2dd95SBruce Richardson 	}
47299a2dd95SBruce Richardson 	rte_mcfg_mem_write_lock();
47399a2dd95SBruce Richardson 	/* find our heap */
47499a2dd95SBruce Richardson 	heap = find_named_heap(heap_name);
47599a2dd95SBruce Richardson 	if (heap == NULL) {
47699a2dd95SBruce Richardson 		rte_errno = ENOENT;
47799a2dd95SBruce Richardson 		ret = -1;
47899a2dd95SBruce Richardson 		goto unlock;
47999a2dd95SBruce Richardson 	}
48099a2dd95SBruce Richardson 	if (heap->socket_id < RTE_MAX_NUMA_NODES) {
48199a2dd95SBruce Richardson 		/* cannot remove memory from internal heaps */
48299a2dd95SBruce Richardson 		rte_errno = EPERM;
48399a2dd95SBruce Richardson 		ret = -1;
48499a2dd95SBruce Richardson 		goto unlock;
48599a2dd95SBruce Richardson 	}
48699a2dd95SBruce Richardson 
48799a2dd95SBruce Richardson 	msl = malloc_heap_find_external_seg(va_addr, len);
48899a2dd95SBruce Richardson 	if (msl == NULL) {
48999a2dd95SBruce Richardson 		ret = -1;
49099a2dd95SBruce Richardson 		goto unlock;
49199a2dd95SBruce Richardson 	}
49299a2dd95SBruce Richardson 
49399a2dd95SBruce Richardson 	rte_spinlock_lock(&heap->lock);
49499a2dd95SBruce Richardson 	ret = malloc_heap_remove_external_memory(heap, va_addr, len);
49599a2dd95SBruce Richardson 	rte_spinlock_unlock(&heap->lock);
49699a2dd95SBruce Richardson 	if (ret != 0)
49799a2dd95SBruce Richardson 		goto unlock;
49899a2dd95SBruce Richardson 
49999a2dd95SBruce Richardson 	ret = malloc_heap_destroy_external_seg(msl);
50099a2dd95SBruce Richardson 
50199a2dd95SBruce Richardson unlock:
50299a2dd95SBruce Richardson 	rte_mcfg_mem_write_unlock();
50399a2dd95SBruce Richardson 
50499a2dd95SBruce Richardson 	return ret;
50599a2dd95SBruce Richardson }
50699a2dd95SBruce Richardson 
50799a2dd95SBruce Richardson static int
50899a2dd95SBruce Richardson sync_memory(const char *heap_name, void *va_addr, size_t len, bool attach)
50999a2dd95SBruce Richardson {
51099a2dd95SBruce Richardson 	struct malloc_heap *heap = NULL;
51199a2dd95SBruce Richardson 	struct rte_memseg_list *msl;
51299a2dd95SBruce Richardson 	int ret;
51399a2dd95SBruce Richardson 
51499a2dd95SBruce Richardson 	if (heap_name == NULL || va_addr == NULL || len == 0 ||
51599a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
51699a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
51799a2dd95SBruce Richardson 				RTE_HEAP_NAME_MAX_LEN) {
51899a2dd95SBruce Richardson 		rte_errno = EINVAL;
51999a2dd95SBruce Richardson 		return -1;
52099a2dd95SBruce Richardson 	}
52199a2dd95SBruce Richardson 	rte_mcfg_mem_read_lock();
52299a2dd95SBruce Richardson 
52399a2dd95SBruce Richardson 	/* find our heap */
52499a2dd95SBruce Richardson 	heap = find_named_heap(heap_name);
52599a2dd95SBruce Richardson 	if (heap == NULL) {
52699a2dd95SBruce Richardson 		rte_errno = ENOENT;
52799a2dd95SBruce Richardson 		ret = -1;
52899a2dd95SBruce Richardson 		goto unlock;
52999a2dd95SBruce Richardson 	}
53099a2dd95SBruce Richardson 	/* we shouldn't be able to sync to internal heaps */
53199a2dd95SBruce Richardson 	if (heap->socket_id < RTE_MAX_NUMA_NODES) {
53299a2dd95SBruce Richardson 		rte_errno = EPERM;
53399a2dd95SBruce Richardson 		ret = -1;
53499a2dd95SBruce Richardson 		goto unlock;
53599a2dd95SBruce Richardson 	}
53699a2dd95SBruce Richardson 
53799a2dd95SBruce Richardson 	/* find corresponding memseg list to sync to */
53899a2dd95SBruce Richardson 	msl = malloc_heap_find_external_seg(va_addr, len);
53999a2dd95SBruce Richardson 	if (msl == NULL) {
54099a2dd95SBruce Richardson 		ret = -1;
54199a2dd95SBruce Richardson 		goto unlock;
54299a2dd95SBruce Richardson 	}
54399a2dd95SBruce Richardson 
54499a2dd95SBruce Richardson 	if (attach) {
54599a2dd95SBruce Richardson 		ret = rte_fbarray_attach(&msl->memseg_arr);
54699a2dd95SBruce Richardson 		if (ret == 0) {
54799a2dd95SBruce Richardson 			/* notify all subscribers that a new memory area was
54899a2dd95SBruce Richardson 			 * added.
54999a2dd95SBruce Richardson 			 */
55099a2dd95SBruce Richardson 			eal_memalloc_mem_event_notify(RTE_MEM_EVENT_ALLOC,
55199a2dd95SBruce Richardson 					va_addr, len);
55299a2dd95SBruce Richardson 		} else {
55399a2dd95SBruce Richardson 			ret = -1;
55499a2dd95SBruce Richardson 			goto unlock;
55599a2dd95SBruce Richardson 		}
55699a2dd95SBruce Richardson 	} else {
55799a2dd95SBruce Richardson 		/* notify all subscribers that a memory area is about to
55899a2dd95SBruce Richardson 		 * be removed.
55999a2dd95SBruce Richardson 		 */
56099a2dd95SBruce Richardson 		eal_memalloc_mem_event_notify(RTE_MEM_EVENT_FREE,
56199a2dd95SBruce Richardson 				msl->base_va, msl->len);
56299a2dd95SBruce Richardson 		ret = rte_fbarray_detach(&msl->memseg_arr);
56399a2dd95SBruce Richardson 		if (ret < 0) {
56499a2dd95SBruce Richardson 			ret = -1;
56599a2dd95SBruce Richardson 			goto unlock;
56699a2dd95SBruce Richardson 		}
56799a2dd95SBruce Richardson 	}
56899a2dd95SBruce Richardson unlock:
56999a2dd95SBruce Richardson 	rte_mcfg_mem_read_unlock();
57099a2dd95SBruce Richardson 	return ret;
57199a2dd95SBruce Richardson }
57299a2dd95SBruce Richardson 
57399a2dd95SBruce Richardson int
57499a2dd95SBruce Richardson rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)
57599a2dd95SBruce Richardson {
57699a2dd95SBruce Richardson 	return sync_memory(heap_name, va_addr, len, true);
57799a2dd95SBruce Richardson }
57899a2dd95SBruce Richardson 
57999a2dd95SBruce Richardson int
58099a2dd95SBruce Richardson rte_malloc_heap_memory_detach(const char *heap_name, void *va_addr, size_t len)
58199a2dd95SBruce Richardson {
58299a2dd95SBruce Richardson 	return sync_memory(heap_name, va_addr, len, false);
58399a2dd95SBruce Richardson }
58499a2dd95SBruce Richardson 
58599a2dd95SBruce Richardson int
58699a2dd95SBruce Richardson rte_malloc_heap_create(const char *heap_name)
58799a2dd95SBruce Richardson {
58899a2dd95SBruce Richardson 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
58999a2dd95SBruce Richardson 	struct malloc_heap *heap = NULL;
59099a2dd95SBruce Richardson 	int i, ret;
59199a2dd95SBruce Richardson 
59299a2dd95SBruce Richardson 	if (heap_name == NULL ||
59399a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
59499a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
59599a2dd95SBruce Richardson 				RTE_HEAP_NAME_MAX_LEN) {
59699a2dd95SBruce Richardson 		rte_errno = EINVAL;
59799a2dd95SBruce Richardson 		return -1;
59899a2dd95SBruce Richardson 	}
59999a2dd95SBruce Richardson 	/* check if there is space in the heap list, or if heap with this name
60099a2dd95SBruce Richardson 	 * already exists.
60199a2dd95SBruce Richardson 	 */
60299a2dd95SBruce Richardson 	rte_mcfg_mem_write_lock();
60399a2dd95SBruce Richardson 
60499a2dd95SBruce Richardson 	for (i = 0; i < RTE_MAX_HEAPS; i++) {
60599a2dd95SBruce Richardson 		struct malloc_heap *tmp = &mcfg->malloc_heaps[i];
60699a2dd95SBruce Richardson 		/* existing heap */
60799a2dd95SBruce Richardson 		if (strncmp(heap_name, tmp->name,
60899a2dd95SBruce Richardson 				RTE_HEAP_NAME_MAX_LEN) == 0) {
60999a2dd95SBruce Richardson 			RTE_LOG(ERR, EAL, "Heap %s already exists\n",
61099a2dd95SBruce Richardson 				heap_name);
61199a2dd95SBruce Richardson 			rte_errno = EEXIST;
61299a2dd95SBruce Richardson 			ret = -1;
61399a2dd95SBruce Richardson 			goto unlock;
61499a2dd95SBruce Richardson 		}
61599a2dd95SBruce Richardson 		/* empty heap */
61699a2dd95SBruce Richardson 		if (strnlen(tmp->name, RTE_HEAP_NAME_MAX_LEN) == 0) {
61799a2dd95SBruce Richardson 			heap = tmp;
61899a2dd95SBruce Richardson 			break;
61999a2dd95SBruce Richardson 		}
62099a2dd95SBruce Richardson 	}
62199a2dd95SBruce Richardson 	if (heap == NULL) {
62299a2dd95SBruce Richardson 		RTE_LOG(ERR, EAL, "Cannot create new heap: no space\n");
62399a2dd95SBruce Richardson 		rte_errno = ENOSPC;
62499a2dd95SBruce Richardson 		ret = -1;
62599a2dd95SBruce Richardson 		goto unlock;
62699a2dd95SBruce Richardson 	}
62799a2dd95SBruce Richardson 
62899a2dd95SBruce Richardson 	/* we're sure that we can create a new heap, so do it */
62999a2dd95SBruce Richardson 	ret = malloc_heap_create(heap, heap_name);
63099a2dd95SBruce Richardson unlock:
63199a2dd95SBruce Richardson 	rte_mcfg_mem_write_unlock();
63299a2dd95SBruce Richardson 
63399a2dd95SBruce Richardson 	return ret;
63499a2dd95SBruce Richardson }
63599a2dd95SBruce Richardson 
63699a2dd95SBruce Richardson int
63799a2dd95SBruce Richardson rte_malloc_heap_destroy(const char *heap_name)
63899a2dd95SBruce Richardson {
63999a2dd95SBruce Richardson 	struct malloc_heap *heap = NULL;
64099a2dd95SBruce Richardson 	int ret;
64199a2dd95SBruce Richardson 
64299a2dd95SBruce Richardson 	if (heap_name == NULL ||
64399a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) == 0 ||
64499a2dd95SBruce Richardson 			strnlen(heap_name, RTE_HEAP_NAME_MAX_LEN) ==
64599a2dd95SBruce Richardson 				RTE_HEAP_NAME_MAX_LEN) {
64699a2dd95SBruce Richardson 		rte_errno = EINVAL;
64799a2dd95SBruce Richardson 		return -1;
64899a2dd95SBruce Richardson 	}
64999a2dd95SBruce Richardson 	rte_mcfg_mem_write_lock();
65099a2dd95SBruce Richardson 
65199a2dd95SBruce Richardson 	/* start from non-socket heaps */
65299a2dd95SBruce Richardson 	heap = find_named_heap(heap_name);
65399a2dd95SBruce Richardson 	if (heap == NULL) {
65499a2dd95SBruce Richardson 		RTE_LOG(ERR, EAL, "Heap %s not found\n", heap_name);
65599a2dd95SBruce Richardson 		rte_errno = ENOENT;
65699a2dd95SBruce Richardson 		ret = -1;
65799a2dd95SBruce Richardson 		goto unlock;
65899a2dd95SBruce Richardson 	}
65999a2dd95SBruce Richardson 	/* we shouldn't be able to destroy internal heaps */
66099a2dd95SBruce Richardson 	if (heap->socket_id < RTE_MAX_NUMA_NODES) {
66199a2dd95SBruce Richardson 		rte_errno = EPERM;
66299a2dd95SBruce Richardson 		ret = -1;
66399a2dd95SBruce Richardson 		goto unlock;
66499a2dd95SBruce Richardson 	}
66599a2dd95SBruce Richardson 	/* sanity checks done, now we can destroy the heap */
66699a2dd95SBruce Richardson 	rte_spinlock_lock(&heap->lock);
66799a2dd95SBruce Richardson 	ret = malloc_heap_destroy(heap);
66899a2dd95SBruce Richardson 
66999a2dd95SBruce Richardson 	/* if we failed, lock is still active */
67099a2dd95SBruce Richardson 	if (ret < 0)
67199a2dd95SBruce Richardson 		rte_spinlock_unlock(&heap->lock);
67299a2dd95SBruce Richardson unlock:
67399a2dd95SBruce Richardson 	rte_mcfg_mem_write_unlock();
67499a2dd95SBruce Richardson 
67599a2dd95SBruce Richardson 	return ret;
67699a2dd95SBruce Richardson }
677