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