From cf450c0d7cae36847df442c2d415af516f0a2b51 Mon Sep 17 00:00:00 2001 From: Ben Walker Date: Mon, 18 May 2020 11:05:43 -0700 Subject: [PATCH] env: Add spdk_mem_reserve The spdk_mem_reserve() function reserves a memory region in SPDK's memory maps. This pre-allocates all of the required data structures to hold memory address translations for that region without actually populating the region. After a region is reserved, calls to spdk_mem_register() for addresses in that range will not require any internal memory allocations. This is useful when overlaying a custom memory allocator on top of SPDK's hugepage memory, such as tcmalloc. Signed-off-by: Ben Walker Change-Id: Ia4e8a770e8b5c956814aa90e9119013356dfab46 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2511 Community-CI: Mellanox Build Bot Community-CI: Broadcom CI Tested-by: SPDK CI Jenkins Reviewed-by: Tomasz Zawadzki Reviewed-by: Jim Harris Reviewed-by: Aleksey Marchuk --- include/spdk/env.h | 14 ++++++++++++ lib/env_dpdk/memory.c | 51 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/include/spdk/env.h b/include/spdk/env.h index af7e1e0dd..f32fa2992 100644 --- a/include/spdk/env.h +++ b/include/spdk/env.h @@ -1248,6 +1248,20 @@ int spdk_mem_register(void *vaddr, size_t len); */ int spdk_mem_unregister(void *vaddr, size_t len); +/** + * Reserve the address space specified in all memory maps. + * + * This pre-allocates the necessary space in the memory maps such that + * future calls to spdk_mem_register() on that region require no + * internal memory allocations. + * + * \param vaddr Virtual address to reserve + * \param len Length in bytes of vaddr + * + * \return 0 on success, negated errno on failure. + */ +int spdk_mem_reserve(void *vaddr, size_t len); + #ifdef __cplusplus } #endif diff --git a/lib/env_dpdk/memory.c b/lib/env_dpdk/memory.c index 1688dc475..5e41014f8 100644 --- a/lib/env_dpdk/memory.c +++ b/lib/env_dpdk/memory.c @@ -509,6 +509,57 @@ spdk_mem_unregister(void *vaddr, size_t len) return 0; } +int +spdk_mem_reserve(void *vaddr, size_t len) +{ + struct spdk_mem_map *map; + void *seg_vaddr; + size_t seg_len; + uint64_t reg; + + if ((uintptr_t)vaddr & ~MASK_256TB) { + DEBUG_PRINT("invalid usermode virtual address %p\n", vaddr); + return -EINVAL; + } + + if (((uintptr_t)vaddr & MASK_2MB) || (len & MASK_2MB)) { + DEBUG_PRINT("invalid %s parameters, vaddr=%p len=%ju\n", + __func__, vaddr, len); + return -EINVAL; + } + + if (len == 0) { + return 0; + } + + pthread_mutex_lock(&g_spdk_mem_map_mutex); + + /* Check if any part of this range is already registered */ + seg_vaddr = vaddr; + seg_len = len; + while (seg_len > 0) { + reg = spdk_mem_map_translate(g_mem_reg_map, (uint64_t)seg_vaddr, NULL); + if (reg & REG_MAP_REGISTERED) { + pthread_mutex_unlock(&g_spdk_mem_map_mutex); + return -EBUSY; + } + seg_vaddr += VALUE_2MB; + seg_len -= VALUE_2MB; + } + + /* Simply set the translation to the memory map's default. This allocates the space in the + * map but does not provide a valid translation. */ + spdk_mem_map_set_translation(g_mem_reg_map, (uint64_t)vaddr, len, + g_mem_reg_map->default_translation); + + TAILQ_FOREACH(map, &g_spdk_mem_maps, tailq) { + spdk_mem_map_set_translation(map, (uint64_t)vaddr, len, map->default_translation); + } + + pthread_mutex_unlock(&g_spdk_mem_map_mutex); + return 0; +} + static struct map_1gb * spdk_mem_map_get_map_1gb(struct spdk_mem_map *map, uint64_t vfn_2mb) {