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