xref: /dpdk/drivers/common/mlx5/mlx5_malloc.c (revision 6e0cd74a56e4dc204ba44c3b4943192c74d6e6a7)
1d38e3d52SSuanming Mou /* SPDX-License-Identifier: BSD-3-Clause
2d38e3d52SSuanming Mou  * Copyright 2020 Mellanox Technologies, Ltd
3d38e3d52SSuanming Mou  */
4d38e3d52SSuanming Mou 
5d38e3d52SSuanming Mou #include <errno.h>
6d38e3d52SSuanming Mou #include <rte_malloc.h>
7d38e3d52SSuanming Mou #include <malloc.h>
8d38e3d52SSuanming Mou #include <stdbool.h>
9d38e3d52SSuanming Mou #include <string.h>
10d38e3d52SSuanming Mou 
11d38e3d52SSuanming Mou #include "mlx5_common_utils.h"
12d38e3d52SSuanming Mou #include "mlx5_malloc.h"
13d38e3d52SSuanming Mou 
14d38e3d52SSuanming Mou struct mlx5_sys_mem {
15d38e3d52SSuanming Mou 	uint32_t init:1; /* Memory allocator initialized. */
16d38e3d52SSuanming Mou 	uint32_t enable:1; /* System memory select. */
17d38e3d52SSuanming Mou 	uint32_t reserve:30; /* Reserve. */
18d38e3d52SSuanming Mou 	struct rte_memseg_list *last_msl;
19d38e3d52SSuanming Mou 	/* last allocated rte memory memseg list. */
20d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
21e4a4d90cSAlexander Kozyrev 	uint64_t malloc_sys;
22d38e3d52SSuanming Mou 	/* Memory allocated from system count. */
23e4a4d90cSAlexander Kozyrev 	uint64_t malloc_rte;
24d38e3d52SSuanming Mou 	/* Memory allocated from hugepage count. */
25e4a4d90cSAlexander Kozyrev 	uint64_t realloc_sys;
26d38e3d52SSuanming Mou 	/* Memory reallocate from system count. */
27e4a4d90cSAlexander Kozyrev 	uint64_t realloc_rte;
28d38e3d52SSuanming Mou 	/* Memory reallocate from hugepage count. */
29e4a4d90cSAlexander Kozyrev 	uint64_t free_sys;
30d38e3d52SSuanming Mou 	/* Memory free to system count. */
31e4a4d90cSAlexander Kozyrev 	uint64_t free_rte;
32d38e3d52SSuanming Mou 	/* Memory free to hugepage count. */
33e4a4d90cSAlexander Kozyrev 	uint64_t msl_miss;
34d38e3d52SSuanming Mou 	/* MSL miss count. */
35e4a4d90cSAlexander Kozyrev 	uint64_t msl_update;
36d38e3d52SSuanming Mou 	/* MSL update count. */
37d38e3d52SSuanming Mou #endif
38d38e3d52SSuanming Mou };
39d38e3d52SSuanming Mou 
40d38e3d52SSuanming Mou /* Initialize default as not */
41d38e3d52SSuanming Mou static struct mlx5_sys_mem mlx5_sys_mem = {
42d38e3d52SSuanming Mou 	.init = 0,
43d38e3d52SSuanming Mou 	.enable = 0,
44d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
45e4a4d90cSAlexander Kozyrev 	.malloc_sys = 0,
46e4a4d90cSAlexander Kozyrev 	.malloc_rte = 0,
47e4a4d90cSAlexander Kozyrev 	.realloc_sys = 0,
48e4a4d90cSAlexander Kozyrev 	.realloc_rte = 0,
49e4a4d90cSAlexander Kozyrev 	.free_sys = 0,
50e4a4d90cSAlexander Kozyrev 	.free_rte = 0,
51e4a4d90cSAlexander Kozyrev 	.msl_miss = 0,
52e4a4d90cSAlexander Kozyrev 	.msl_update = 0,
53d38e3d52SSuanming Mou #endif
54d38e3d52SSuanming Mou };
55d38e3d52SSuanming Mou 
56d38e3d52SSuanming Mou /**
57d38e3d52SSuanming Mou  * Check if the address belongs to memory seg list.
58d38e3d52SSuanming Mou  *
59d38e3d52SSuanming Mou  * @param addr
60d38e3d52SSuanming Mou  *   Memory address to be ckeced.
61d38e3d52SSuanming Mou  * @param msl
62d38e3d52SSuanming Mou  *   Memory seg list.
63d38e3d52SSuanming Mou  *
64d38e3d52SSuanming Mou  * @return
65d38e3d52SSuanming Mou  *   True if it belongs, false otherwise.
66d38e3d52SSuanming Mou  */
67d38e3d52SSuanming Mou static bool
68d38e3d52SSuanming Mou mlx5_mem_check_msl(void *addr, struct rte_memseg_list *msl)
69d38e3d52SSuanming Mou {
70d38e3d52SSuanming Mou 	void *start, *end;
71d38e3d52SSuanming Mou 
72d38e3d52SSuanming Mou 	if (!msl)
73d38e3d52SSuanming Mou 		return false;
74d38e3d52SSuanming Mou 	start = msl->base_va;
75d38e3d52SSuanming Mou 	end = RTE_PTR_ADD(start, msl->len);
76d38e3d52SSuanming Mou 	if (addr >= start && addr < end)
77d38e3d52SSuanming Mou 		return true;
78d38e3d52SSuanming Mou 	return false;
79d38e3d52SSuanming Mou }
80d38e3d52SSuanming Mou 
81d38e3d52SSuanming Mou /**
82d38e3d52SSuanming Mou  * Update the msl if memory belongs to new msl.
83d38e3d52SSuanming Mou  *
84d38e3d52SSuanming Mou  * @param addr
85d38e3d52SSuanming Mou  *   Memory address.
86d38e3d52SSuanming Mou  */
87d38e3d52SSuanming Mou static void
88d38e3d52SSuanming Mou mlx5_mem_update_msl(void *addr)
89d38e3d52SSuanming Mou {
90d38e3d52SSuanming Mou 	/*
91d38e3d52SSuanming Mou 	 * Update the cache msl if the new addr comes from the new msl
92d38e3d52SSuanming Mou 	 * different with the cached msl.
93d38e3d52SSuanming Mou 	 */
94d38e3d52SSuanming Mou 	if (addr && !mlx5_mem_check_msl(addr,
95e4a4d90cSAlexander Kozyrev 	    (struct rte_memseg_list *)__atomic_load_n
96e4a4d90cSAlexander Kozyrev 	    (&mlx5_sys_mem.last_msl, __ATOMIC_RELAXED))) {
97e4a4d90cSAlexander Kozyrev 		__atomic_store_n(&mlx5_sys_mem.last_msl,
98e4a4d90cSAlexander Kozyrev 			rte_mem_virt2memseg_list(addr),
99e4a4d90cSAlexander Kozyrev 			__ATOMIC_RELAXED);
100d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
101e4a4d90cSAlexander Kozyrev 		__atomic_add_fetch(&mlx5_sys_mem.msl_update, 1,
102e4a4d90cSAlexander Kozyrev 				   __ATOMIC_RELAXED);
103d38e3d52SSuanming Mou #endif
104d38e3d52SSuanming Mou 	}
105d38e3d52SSuanming Mou }
106d38e3d52SSuanming Mou 
107d38e3d52SSuanming Mou /**
108d38e3d52SSuanming Mou  * Check if the address belongs to rte memory.
109d38e3d52SSuanming Mou  *
110d38e3d52SSuanming Mou  * @param addr
111d38e3d52SSuanming Mou  *   Memory address to be ckeced.
112d38e3d52SSuanming Mou  *
113d38e3d52SSuanming Mou  * @return
114d38e3d52SSuanming Mou  *   True if it belongs, false otherwise.
115d38e3d52SSuanming Mou  */
116d38e3d52SSuanming Mou static bool
117d38e3d52SSuanming Mou mlx5_mem_is_rte(void *addr)
118d38e3d52SSuanming Mou {
119d38e3d52SSuanming Mou 	/*
120d38e3d52SSuanming Mou 	 * Check if the last cache msl matches. Drop to slow path
121d38e3d52SSuanming Mou 	 * to check if the memory belongs to rte memory.
122d38e3d52SSuanming Mou 	 */
123e4a4d90cSAlexander Kozyrev 	if (!mlx5_mem_check_msl(addr, (struct rte_memseg_list *)
124e4a4d90cSAlexander Kozyrev 	    __atomic_load_n(&mlx5_sys_mem.last_msl, __ATOMIC_RELAXED))) {
125d38e3d52SSuanming Mou 		if (!rte_mem_virt2memseg_list(addr))
126d38e3d52SSuanming Mou 			return false;
127d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
128e4a4d90cSAlexander Kozyrev 		__atomic_add_fetch(&mlx5_sys_mem.msl_miss, 1, __ATOMIC_RELAXED);
129d38e3d52SSuanming Mou #endif
130d38e3d52SSuanming Mou 	}
131d38e3d52SSuanming Mou 	return true;
132d38e3d52SSuanming Mou }
133d38e3d52SSuanming Mou 
134d38e3d52SSuanming Mou /**
135d38e3d52SSuanming Mou  * Allocate memory with alignment.
136d38e3d52SSuanming Mou  *
137d38e3d52SSuanming Mou  * @param size
138d38e3d52SSuanming Mou  *   Memory size to be allocated.
139d38e3d52SSuanming Mou  * @param align
140d38e3d52SSuanming Mou  *   Memory alignment.
141d38e3d52SSuanming Mou  * @param zero
142d38e3d52SSuanming Mou  *   Clear the allocated memory or not.
143d38e3d52SSuanming Mou  *
144d38e3d52SSuanming Mou  * @return
145d38e3d52SSuanming Mou  *   Pointer of the allocated memory, NULL otherwise.
146d38e3d52SSuanming Mou  */
147d38e3d52SSuanming Mou static void *
148d38e3d52SSuanming Mou mlx5_alloc_align(size_t size, unsigned int align, unsigned int zero)
149d38e3d52SSuanming Mou {
150d38e3d52SSuanming Mou 	void *buf;
151af863644SOphir Munk 	int ret;
152af863644SOphir Munk 
153af863644SOphir Munk 	ret = posix_memalign(&buf, align, size);
154af863644SOphir Munk 	if (ret) {
155af863644SOphir Munk 		DRV_LOG(ERR,
156af863644SOphir Munk 			"Couldn't allocate buf size=%zu align=%u. Err=%d\n",
157af863644SOphir Munk 			size, align, ret);
158af863644SOphir Munk 
159d38e3d52SSuanming Mou 		return NULL;
160d38e3d52SSuanming Mou 	}
161d38e3d52SSuanming Mou 	if (zero)
162d38e3d52SSuanming Mou 		memset(buf, 0, size);
163d38e3d52SSuanming Mou 	return buf;
164d38e3d52SSuanming Mou }
165d38e3d52SSuanming Mou 
166d38e3d52SSuanming Mou void *
167d38e3d52SSuanming Mou mlx5_malloc(uint32_t flags, size_t size, unsigned int align, int socket)
168d38e3d52SSuanming Mou {
169d38e3d52SSuanming Mou 	void *addr;
170d38e3d52SSuanming Mou 	bool rte_mem;
171d38e3d52SSuanming Mou 
172d38e3d52SSuanming Mou 	/*
173d38e3d52SSuanming Mou 	 * If neither system memory nor rte memory is required, allocate
174d38e3d52SSuanming Mou 	 * memory according to mlx5_sys_mem.enable.
175d38e3d52SSuanming Mou 	 */
176d38e3d52SSuanming Mou 	if (flags & MLX5_MEM_RTE)
177d38e3d52SSuanming Mou 		rte_mem = true;
178d38e3d52SSuanming Mou 	else if (flags & MLX5_MEM_SYS)
179d38e3d52SSuanming Mou 		rte_mem = false;
180d38e3d52SSuanming Mou 	else
181d38e3d52SSuanming Mou 		rte_mem = mlx5_sys_mem.enable ? false : true;
182d38e3d52SSuanming Mou 	if (rte_mem) {
183d38e3d52SSuanming Mou 		if (flags & MLX5_MEM_ZERO)
184d38e3d52SSuanming Mou 			addr = rte_zmalloc_socket(NULL, size, align, socket);
185d38e3d52SSuanming Mou 		else
186d38e3d52SSuanming Mou 			addr = rte_malloc_socket(NULL, size, align, socket);
187d38e3d52SSuanming Mou 		mlx5_mem_update_msl(addr);
188d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
189d38e3d52SSuanming Mou 		if (addr)
190*6e0cd74aSBing Zhao 			__atomic_add_fetch(&mlx5_sys_mem.malloc_rte, 1,
191e4a4d90cSAlexander Kozyrev 					   __ATOMIC_RELAXED);
192d38e3d52SSuanming Mou #endif
193d38e3d52SSuanming Mou 		return addr;
194d38e3d52SSuanming Mou 	}
195d38e3d52SSuanming Mou 	/* The memory will be allocated from system. */
196af863644SOphir Munk 	if (align > MLX5_MALLOC_ALIGNMENT)
197d38e3d52SSuanming Mou 		addr = mlx5_alloc_align(size, align, !!(flags & MLX5_MEM_ZERO));
198d38e3d52SSuanming Mou 	else if (flags & MLX5_MEM_ZERO)
199d38e3d52SSuanming Mou 		addr = calloc(1, size);
200d38e3d52SSuanming Mou 	else
201d38e3d52SSuanming Mou 		addr = malloc(size);
202d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
203d38e3d52SSuanming Mou 	if (addr)
204*6e0cd74aSBing Zhao 		__atomic_add_fetch(&mlx5_sys_mem.malloc_sys, 1,
205e4a4d90cSAlexander Kozyrev 				   __ATOMIC_RELAXED);
206d38e3d52SSuanming Mou #endif
207d38e3d52SSuanming Mou 	return addr;
208d38e3d52SSuanming Mou }
209d38e3d52SSuanming Mou 
210d38e3d52SSuanming Mou void *
211d38e3d52SSuanming Mou mlx5_realloc(void *addr, uint32_t flags, size_t size, unsigned int align,
212d38e3d52SSuanming Mou 	     int socket)
213d38e3d52SSuanming Mou {
214d38e3d52SSuanming Mou 	void *new_addr;
215d38e3d52SSuanming Mou 	bool rte_mem;
216d38e3d52SSuanming Mou 
217d38e3d52SSuanming Mou 	/* Allocate directly if old memory address is NULL. */
218d38e3d52SSuanming Mou 	if (!addr)
219d38e3d52SSuanming Mou 		return mlx5_malloc(flags, size, align, socket);
220d38e3d52SSuanming Mou 	/* Get the memory type. */
221d38e3d52SSuanming Mou 	if (flags & MLX5_MEM_RTE)
222d38e3d52SSuanming Mou 		rte_mem = true;
223d38e3d52SSuanming Mou 	else if (flags & MLX5_MEM_SYS)
224d38e3d52SSuanming Mou 		rte_mem = false;
225d38e3d52SSuanming Mou 	else
226d38e3d52SSuanming Mou 		rte_mem = mlx5_sys_mem.enable ? false : true;
227d38e3d52SSuanming Mou 	/* Check if old memory and to be allocated memory are the same type. */
228d38e3d52SSuanming Mou 	if (rte_mem != mlx5_mem_is_rte(addr)) {
229d38e3d52SSuanming Mou 		DRV_LOG(ERR, "Couldn't reallocate to different memory type.");
230d38e3d52SSuanming Mou 		return NULL;
231d38e3d52SSuanming Mou 	}
232d38e3d52SSuanming Mou 	/* Allocate memory from rte memory. */
233d38e3d52SSuanming Mou 	if (rte_mem) {
234d38e3d52SSuanming Mou 		new_addr = rte_realloc_socket(addr, size, align, socket);
235d38e3d52SSuanming Mou 		mlx5_mem_update_msl(new_addr);
236d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
237d38e3d52SSuanming Mou 		if (new_addr)
238*6e0cd74aSBing Zhao 			__atomic_add_fetch(&mlx5_sys_mem.realloc_rte, 1,
239e4a4d90cSAlexander Kozyrev 					   __ATOMIC_RELAXED);
240d38e3d52SSuanming Mou #endif
241d38e3d52SSuanming Mou 		return new_addr;
242d38e3d52SSuanming Mou 	}
243d38e3d52SSuanming Mou 	/* Align is not supported for system memory. */
244d38e3d52SSuanming Mou 	if (align) {
245d38e3d52SSuanming Mou 		DRV_LOG(ERR, "Couldn't reallocate with alignment");
246d38e3d52SSuanming Mou 		return NULL;
247d38e3d52SSuanming Mou 	}
248d38e3d52SSuanming Mou 	new_addr = realloc(addr, size);
249d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
250d38e3d52SSuanming Mou 	if (new_addr)
251*6e0cd74aSBing Zhao 		__atomic_add_fetch(&mlx5_sys_mem.realloc_sys, 1,
252e4a4d90cSAlexander Kozyrev 				   __ATOMIC_RELAXED);
253d38e3d52SSuanming Mou #endif
254d38e3d52SSuanming Mou 	return new_addr;
255d38e3d52SSuanming Mou }
256d38e3d52SSuanming Mou 
257d38e3d52SSuanming Mou void
258d38e3d52SSuanming Mou mlx5_free(void *addr)
259d38e3d52SSuanming Mou {
260d38e3d52SSuanming Mou 	if (addr == NULL)
261d38e3d52SSuanming Mou 		return;
262d38e3d52SSuanming Mou 	if (!mlx5_mem_is_rte(addr)) {
263d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
264*6e0cd74aSBing Zhao 		__atomic_add_fetch(&mlx5_sys_mem.free_sys, 1,
265e4a4d90cSAlexander Kozyrev 				   __ATOMIC_RELAXED);
266d38e3d52SSuanming Mou #endif
267d38e3d52SSuanming Mou 		free(addr);
268d38e3d52SSuanming Mou 	} else {
269d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
270*6e0cd74aSBing Zhao 		__atomic_add_fetch(&mlx5_sys_mem.free_rte, 1,
271e4a4d90cSAlexander Kozyrev 				   __ATOMIC_RELAXED);
272d38e3d52SSuanming Mou #endif
273d38e3d52SSuanming Mou 		rte_free(addr);
274d38e3d52SSuanming Mou 	}
275d38e3d52SSuanming Mou }
276d38e3d52SSuanming Mou 
277d38e3d52SSuanming Mou void
278d38e3d52SSuanming Mou mlx5_memory_stat_dump(void)
279d38e3d52SSuanming Mou {
280d38e3d52SSuanming Mou #ifdef RTE_LIBRTE_MLX5_DEBUG
281d38e3d52SSuanming Mou 	DRV_LOG(INFO, "System memory malloc:%"PRIi64", realloc:%"PRIi64","
282d38e3d52SSuanming Mou 		" free:%"PRIi64"\nRTE memory malloc:%"PRIi64","
283d38e3d52SSuanming Mou 		" realloc:%"PRIi64", free:%"PRIi64"\nMSL miss:%"PRIi64","
284d38e3d52SSuanming Mou 		" update:%"PRIi64"",
285e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.malloc_sys, __ATOMIC_RELAXED),
286e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.realloc_sys, __ATOMIC_RELAXED),
287e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.free_sys, __ATOMIC_RELAXED),
288e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.malloc_rte, __ATOMIC_RELAXED),
289e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.realloc_rte, __ATOMIC_RELAXED),
290e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.free_rte, __ATOMIC_RELAXED),
291e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.msl_miss, __ATOMIC_RELAXED),
292e4a4d90cSAlexander Kozyrev 		__atomic_load_n(&mlx5_sys_mem.msl_update, __ATOMIC_RELAXED));
293d38e3d52SSuanming Mou #endif
294d38e3d52SSuanming Mou }
295d38e3d52SSuanming Mou 
296d38e3d52SSuanming Mou void
297d38e3d52SSuanming Mou mlx5_malloc_mem_select(uint32_t sys_mem_en)
298d38e3d52SSuanming Mou {
299d38e3d52SSuanming Mou 	/*
300d38e3d52SSuanming Mou 	 * The initialization should be called only once and all devices
301d38e3d52SSuanming Mou 	 * should use the same memory type. Otherwise, when new device is
302d38e3d52SSuanming Mou 	 * being attached with some different memory allocation configuration,
303d38e3d52SSuanming Mou 	 * the memory will get wrong behavior or a failure will be raised.
304d38e3d52SSuanming Mou 	 */
305d38e3d52SSuanming Mou 	if (!mlx5_sys_mem.init) {
306d38e3d52SSuanming Mou 		if (sys_mem_en)
307d38e3d52SSuanming Mou 			mlx5_sys_mem.enable = 1;
308d38e3d52SSuanming Mou 		mlx5_sys_mem.init = 1;
309d38e3d52SSuanming Mou 		DRV_LOG(INFO, "%s is selected.", sys_mem_en ? "SYS_MEM" : "RTE_MEM");
310d38e3d52SSuanming Mou 	} else if (mlx5_sys_mem.enable != sys_mem_en) {
311d38e3d52SSuanming Mou 		DRV_LOG(WARNING, "%s is already selected.",
312d38e3d52SSuanming Mou 			mlx5_sys_mem.enable ? "SYS_MEM" : "RTE_MEM");
313d38e3d52SSuanming Mou 	}
314d38e3d52SSuanming Mou }
315