Support externally allocated memory in DPDK
Subsystems (2)
cover Support externally allocated memory in DPDK
This is a proposal to enable using externally allocated memory in DPDK.
In a nutshell, here is what is being done here:
- Index internal malloc heaps by NUMA node index, rather than NUMA node itself (external heaps will have ID's in order of creation)
- Add identifier string to malloc heap, to uniquely identify it
- Each new heap will receive a unique socket ID that will be used by allocator to decide from which heap (internal or external) to allocate requested amount of memory
- Allow creating named heaps and add/remove memory to/from those heaps
- Allocate memseg lists at runtime, to keep track of IOVA addresses of externally allocated memory
- If IOVA addresses aren't provided, use RTE_BAD_IOVA
- Allow malloc and memzones to allocate from external heaps
- Allow other data structures to allocate from externall heaps
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
The general approach is to create heap and add memory into it. For any other process wishing to use the same memory, said memory must first be attached (otherwise some things will not work).
A design decision was made to make multiprocess synchronization a manual process. Due to underlying issues with attaching to fbarrays in secondary processes, this design was deemed to be better because we don't want to fail to create external heap in the primary because something in the secondary has failed when in fact we may not eve have wanted this memory to be accessible in the secondary in the first place.
Using external memory in multiprocess is *hard*, because not only memory space needs to be preallocated, but it also needs to be attached in each process to allow other processes to access the page table. The attach API call may or may not succeed, depending on memory layout, for reasons similar to other multiprocess failures. This is treated as a "known issue" for this release.
RFC -> v1 changes:
- Removed the "named heaps" API, allocate using fake socket ID instead
- Added multiprocess support
- Everything is now thread-safe
- Numerous bugfixes and API improvements
Anatoly Burakov (16):
mem: add length to memseg list
mem: allow memseg lists to be marked as external
malloc: index heaps using heap ID rather than NUMA node
mem: do not check for invalid socket ID
flow_classify: do not check for invalid socket ID
pipeline: do not check for invalid socket ID
sched: do not check for invalid socket ID
malloc: add name to malloc heaps
malloc: add function to query socket ID of named heap
malloc: allow creating malloc heaps
malloc: allow destroying heaps
malloc: allow adding memory to named heaps
malloc: allow removing memory from named heaps
malloc: allow attaching to external memory chunks
malloc: allow detaching from external memory
test: add unit tests for external memory support
config/common_base | 1 + config/rte_config.h | 1 + drivers/bus/fslmc/fslmc_vfio.c | 7 +- drivers/bus/pci/linux/pci.c | 2 +- drivers/net/mlx4/mlx4_mr.c | 3 + drivers/net/mlx5/mlx5.c | 5 +- drivers/net/mlx5/mlx5_mr.c | 3 + drivers/net/virtio/virtio_user/vhost_kernel.c | 5 +- lib/librte_eal/bsdapp/eal/eal.c | 3 + lib/librte_eal/bsdapp/eal/eal_memory.c | 9 +- lib/librte_eal/common/eal_common_memory.c | 9 +- lib/librte_eal/common/eal_common_memzone.c | 8 +- .../common/include/rte_eal_memconfig.h | 6 +- lib/librte_eal/common/include/rte_malloc.h | 181 +++++++++ .../common/include/rte_malloc_heap.h | 3 + lib/librte_eal/common/include/rte_memory.h | 9 + lib/librte_eal/common/malloc_heap.c | 287 +++++++++++-- lib/librte_eal/common/malloc_heap.h | 17 + lib/librte_eal/common/rte_malloc.c | 383 ++++++++++++++++- lib/librte_eal/linuxapp/eal/eal.c | 3 + lib/librte_eal/linuxapp/eal/eal_memalloc.c | 12 +- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +- lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 +- lib/librte_eal/rte_eal_version.map | 7 + lib/librte_flow_classify/rte_flow_classify.c | 3 +- lib/librte_mempool/rte_mempool.c | 31 +- lib/librte_pipeline/rte_pipeline.c | 3 +- lib/librte_sched/rte_sched.c | 2 +- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 384 ++++++++++++++++++ test/test/test_malloc.c | 3 + test/test/test_memzone.c | 3 + 34 files changed, 1346 insertions(+), 84 deletions(-) create mode 100644 test/test/test_external_mem.c
Signature · 1 line
2.17.1
PATCH 1/16 mem: add length to memseg list
Previously, to calculate length of memory area covered by a memseg list, we would've needed to multiply page size by length of fbarray backing that memseg list. This is not obvious and unnecessarily low level, so store length in the memseg list itself.
--- drivers/bus/pci/linux/pci.c | 2 +- lib/librte_eal/bsdapp/eal/eal_memory.c | 2 ++ lib/librte_eal/common/eal_common_memory.c | 5 ++--- lib/librte_eal/common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/linuxapp/eal/eal_memalloc.c | 3 ++- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +++- 6 files changed, 11 insertions(+), 6 deletions(-)
drivers/bus/pci/linux/pci.c +1 -1
@@ -119,7 +119,7 @@ rte_pci_unmap_device(struct rte_pci_device *dev)static int find_max_end_va (const struct rte_memseg_list *msl,void *arg) { -size_t sz = msl->memseg_arr.len * msl->page_sz; +size_t sz = msl->len;void *end_va =RTE_PTR_ADD (msl->base_va, sz);void **max_va = arg;
lib/librte_eal/bsdapp/eal/eal_memory.c +2 -0
@@ -79,6 +79,7 @@ rte_eal_hugepage_init(void) } msl->base_va = addr; msl->page_sz = page_sz; + msl->len = internal_config.memory; msl->socket_id =0 ;/* populate memsegs. each memseg is 1 page long */
@@ -370,6 +371,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
lib/librte_eal/common/eal_common_memory.c +2 -3
@@ -171,7 +171,7 @@ virt2memseg(const void *addr, const struct rte_memseg_list *msl)/* a memseg list was specified, check if it's the right one */ start = msl->base_va; - end =RTE_PTR_ADD (start, (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr < start || addr >= end)return NULL ;
@@ -194,8 +194,7 @@ virt2memseg_list(const void *addr) msl = &mcfg->memsegs[msl_idx]; start = msl->base_va; - end =RTE_PTR_ADD (start, - (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr >= start && addr < end)break ; }
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -30,6 +30,7 @@ struct rte_memseg_list {uint64_t addr_64;/**< Makes sure addr is always 64-bits */ }; +size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ volatile uint32_t version;/**< version number for multiprocess sync. */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +2 -1
@@ -828,7 +828,7 @@ free_seg_walk(const struct rte_memseg_list *msl, void *arg)int msl_idx, seg_idx, ret, dir_fd =-1 ; start_addr = (uintptr_t ) msl->base_va; - end_addr = start_addr + msl->memseg_arr.len * (size_t )msl->page_sz; + end_addr = start_addr + msl->len;if ((uintptr_t )wa->ms->addr < start_addr || (uintptr_t )wa->ms->addr >= end_addr)
@@ -1314,6 +1314,7 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,return -1 ; } local_msl->base_va = primary_msl->base_va; + local_msl->len = primary_msl->len;return 0 ; }
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -1
@@ -857,6 +857,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
@@ -1365,6 +1366,7 @@ eal_legacy_hugepage_init(void) msl->base_va = addr; msl->page_sz = page_sz; msl->socket_id =0 ; + msl->len = internal_config.memory;/* populate memsegs. each memseg is one page long */ for (cur_seg =0 ; cur_seg < n_segs; cur_seg++) {
@@ -1611,7 +1613,7 @@ eal_legacy_hugepage_init(void)if (msl->memseg_arr.count >0 )continue ;/* this is an unused list, deallocate it */ - mem_sz = (size_t )msl->page_sz * msl->memseg_arr.len; + mem_sz = msl->len;munmap (msl->base_va, mem_sz); msl->base_va =NULL ;
Signature · 1 line
2.17.1
PATCH 2/16 mem: allow memseg lists to be marked as external
In reply to Anatoly BurakovWhen we allocate and use DPDK memory, we need to be able to differentiate between DPDK hugepage segments and segments that were made part of DPDK but are externally allocated. Add such a property to memseg lists.
All current calls for memseg walk functions were adjusted to ignore external segments where it made sense. Mempools is a special case, because we may be asked to allocate a mempool on a specific socket, and we need to ignore all page sizes on other heaps or other sockets. Previously, this assumption of knowing all page sizes was not a problem, but it will be now, so we have to match socket ID with page size when calculating minimum page size for a mempool.
---
Notes:
v1:
- Adjust all calls to memseg walk functions to ignore external segments where it made sense to do so
drivers/bus/fslmc/fslmc_vfio.c | 7 +++-- drivers/net/mlx4/mlx4_mr.c | 3 ++ drivers/net/mlx5/mlx5.c | 5 ++- drivers/net/mlx5/mlx5_mr.c | 3 ++ drivers/net/virtio/virtio_user/vhost_kernel.c | 5 ++- lib/librte_eal/bsdapp/eal/eal.c | 3 ++ lib/librte_eal/bsdapp/eal/eal_memory.c | 7 +++-- lib/librte_eal/common/eal_common_memory.c | 4 +++ .../common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/common/include/rte_memory.h | 9 ++++++ lib/librte_eal/common/malloc_heap.c | 9 ++++-- lib/librte_eal/linuxapp/eal/eal.c | 3 ++ lib/librte_eal/linuxapp/eal/eal_memalloc.c | 9 ++++++ lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 +++++++--- lib/librte_mempool/rte_mempool.c | 31 ++++++++++++++----- test/test/test_malloc.c | 3 ++ test/test/test_memzone.c | 3 ++ 17 files changed, 102 insertions(+), 20 deletions(-)
drivers/bus/fslmc/fslmc_vfio.c +5 -2
@@ -317,12 +317,15 @@ fslmc_unmap_dma(uint64_t vaddr, uint64_t iovaddr __rte_unused, size_t len) }static int -fslmc_dmamap_seg(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +fslmc_dmamap_seg (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *n_segs = arg;int ret; +if (msl->external) +return 0 ; + ret =fslmc_map_dma (ms->addr_64, ms->iova, ms->len);if (ret) DPAA2_BUS_ERR("Unable to VFIO map (addr=%p, len=%zu)",
drivers/net/mlx4/mlx4_mr.c +3 -0
@@ -496,6 +496,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/mlx5/mlx5.c +4 -1
@@ -568,11 +568,14 @@ static struct rte_pci_driver mlx5_driver;static void *uar_base;static int -find_lower_va_bound(const struct rte_memseg_list *msl __rte_unused, +find_lower_va_bound (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {void **addr = arg; +if (msl->external) +return 0 ; +if (*addr ==NULL ) *addr = ms->addr;else
drivers/net/mlx5/mlx5_mr.c +3 -0
@@ -486,6 +486,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/virtio/virtio_user/vhost_kernel.c +4 -1
@@ -75,13 +75,16 @@ struct walk_arg {uint32_t region_nr; };static int -add_memory_region(const struct rte_memseg_list *msl __rte_unused, +add_memory_region (const struct rte_memseg_list *msl,const struct rte_memseg *ms,size_t len,void *arg) {struct walk_arg *wa = arg;struct vhost_memory_region *mr;void *start_addr; +if (msl->external) +return 0 ; +if (wa->region_nr >= max_regions)return -1 ;
lib/librte_eal/bsdapp/eal/eal.c +3 -0
@@ -502,6 +502,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +if (msl->socket_id == *socket_id && msl->memseg_arr.count !=0 )return 1 ;
lib/librte_eal/bsdapp/eal/eal_memory.c +5 -2
@@ -236,12 +236,15 @@ struct attach_walk_args {int seg_idx; };static int -attach_segment(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +attach_segment (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {struct attach_walk_args *wa = arg;void *addr; +if (msl->external) +return 0 ; + addr =mmap (ms->addr, ms->len,PROT_READ |PROT_WRITE ,MAP_SHARED |MAP_FIXED , wa->fd_hugepage, wa->seg_idx *EAL_PAGE_SIZE );
lib/librte_eal/common/eal_common_memory.c +4 -0
@@ -272,6 +272,9 @@ physmem_size(const struct rte_memseg_list *msl, void *arg) {uint64_t *total_len = arg; +if (msl->external) +return 0 ; + *total_len += msl->memseg_arr.count * msl->page_sz;return 0 ;
@@ -547,6 +550,7 @@ rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)return ret; } +/* init memory subsystem */ int rte_eal_memory_init (void )
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -33,6 +33,7 @@ struct rte_memseg_list {size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ +bool external;/**< true if this list points to external memory */ volatile uint32_t version;/**< version number for multiprocess sync. */ struct rte_fbarray memseg_arr; };
lib/librte_eal/common/include/rte_memory.h +9 -0
@@ -215,6 +215,9 @@ typedef int (*rte_memseg_list_walk_t)(const struct rte_memseg_list *msl,* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -233,6 +236,9 @@ rte_memseg_walk(rte_memseg_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -251,6 +257,9 @@ rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
lib/librte_eal/common/malloc_heap.c +7 -2
@@ -95,6 +95,9 @@ malloc_add_seg(const struct rte_memseg_list *msl,struct malloc_heap *heap;int msl_idx; +if (msl->external) +return 0 ; + heap = &mcfg->malloc_heaps[msl->socket_id];/* msl is const, so find it */
@@ -756,8 +759,10 @@ malloc_heap_free(struct malloc_elem *elem)/* anything after this is a bonus */ ret =0 ; -/* ...of which we can't avail if we are in legacy mode */ -if (internal_config.legacy_mem) +/* ...of which we can't avail if we are in legacy mode, or if this is an +* externally allocated segment. +*/ +if (internal_config.legacy_mem || msl->external) goto free_unlock;/* check if we can free any memory back to the system */
lib/librte_eal/linuxapp/eal/eal.c +3 -0
@@ -725,6 +725,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +return *socket_id == msl->socket_id; }
lib/librte_eal/linuxapp/eal/eal_memalloc.c +9 -0
@@ -1250,6 +1250,9 @@ sync_walk(const struct rte_memseg_list *msl, void *arg __rte_unused)unsigned int i;int msl_idx; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1298,6 +1301,9 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,char name[PATH_MAX ];int msl_idx, ret; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1328,6 +1334,9 @@ secondary_lock_list_create_walk(const struct rte_memseg_list *msl,int msl_idx;int *data; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; len = msl->memseg_arr.len;
lib/librte_eal/linuxapp/eal/eal_vfio.c +13 -4
@@ -1082,11 +1082,14 @@ rte_vfio_get_group_num(const char *sysfs_base, }static int -type1_map(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +type1_map (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_type1_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1196,11 +1199,14 @@ vfio_spapr_dma_do_map(int vfio_container_fd, uint64_t vaddr, uint64_t iova, }static int -vfio_spapr_map_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_map_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_spapr_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1210,12 +1216,15 @@ struct spapr_walk_param {uint64_t hugepage_sz; };static int -vfio_spapr_window_size_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_window_size_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {struct spapr_walk_param *param = arg;uint64_t max = ms->iova + ms->len; +if (msl->external) +return 0 ; +if (max > param->window_size) { param->hugepage_sz = ms->hugepage_sz; param->window_size = max;
lib/librte_mempool/rte_mempool.c +23 -8
@@ -99,25 +99,40 @@ static unsigned optimize_object_size(unsigned obj_size)return new_obj_size *RTE_MEMPOOL_ALIGN ; } +struct pagesz_walk_arg { +int socket_id; +size_t min; +}; +static int find_min_pagesz (const struct rte_memseg_list *msl,void *arg) { -size_t *min = arg; +struct pagesz_walk_arg *wa = arg; +bool valid; -if (msl->page_sz < *min) - *min = msl->page_sz; + valid = msl->socket_id == wa->socket_id; + valid |= wa->socket_id ==SOCKET_ID_ANY && !msl->external; + +if (!valid) +return 0 ; + +if (msl->page_sz < wa->min) + wa->min = msl->page_sz;return 0 ; }static size_t -get_min_page_size (void ) +get_min_page_size (int socket_id) { -size_t min_pagesz =SIZE_MAX ; +struct pagesz_walk_arg wa; -rte_memseg_list_walk (find_min_pagesz, &min_pagesz); + wa.min =SIZE_MAX ; + wa.socket_id = socket_id; -return min_pagesz ==SIZE_MAX ? (size_t )getpagesize () : min_pagesz; +rte_memseg_list_walk (find_min_pagesz, &wa); + +return wa.min ==SIZE_MAX ? (size_t )getpagesize () : wa.min; }
@@ -470,7 +485,7 @@ rte_mempool_populate_default(struct rte_mempool *mp) pg_sz =0 ; pg_shift =0 ; }else if (try_contig) { - pg_sz =get_min_page_size (); + pg_sz =get_min_page_size (mp->socket_id); pg_shift =rte_bsf32 (pg_sz); }else { pg_sz =getpagesize ();
test/test/test_malloc.c +3 -0
@@ -711,6 +711,9 @@ check_socket_mem(const struct rte_memseg_list *msl, void *arg) {int32_t *socket = arg; +if (msl->external) +return 0 ; +return *socket == msl->socket_id; }
test/test/test_memzone.c +3 -0
@@ -115,6 +115,9 @@ find_available_pagesz(const struct rte_memseg_list *msl, void *arg) {struct walk_arg *wa = arg; +if (msl->external) +return 0 ; +if (msl->page_sz ==RTE_PGSIZE_2M ) wa->hugepage_2MB_avail =1 ;if (msl->page_sz ==RTE_PGSIZE_1G )
Signature · 1 line
2.17.1
PATCH 6/16 pipeline: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_pipeline/rte_pipeline.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_pipeline/rte_pipeline.c +1 -2
@@ -178,8 +178,7 @@ rte_pipeline_check_params(struct rte_pipeline_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_LOG (ERR ,PIPELINE ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH 10/16 malloc: allow creating malloc heaps
In reply to Anatoly BurakovAdd API to allow creating new malloc heaps. They will be created with socket ID's going above RTE_MAX_NUMA_NODES, to avoid clashing with internal heaps.
--- lib/librte_eal/common/include/rte_malloc.h | 19 ++++++++ lib/librte_eal/common/malloc_heap.c | 30 +++++++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 52 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 105 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +19 -0
@@ -263,6 +263,25 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Creates a new empty malloc heap with a specified name. +* +* @note Heaps created via this call will automatically get assigned a unique +* socket ID, which can be found using ``rte_malloc_heap_get_socket()`` +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on successful creation +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +*/ +int __rte_experimental +rte_malloc_heap_create (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +30 -0
@@ -29,6 +29,10 @@#include "malloc_heap.h" #include "malloc_mp.h" +/* start external socket ID's at a very high number */ +#define CONST_MAX (a, b) (a > b ? a : b)/* RTE_MAX is not a constant */ +#define EXTERNAL_HEAP_MIN_SOCKET_ID (CONST_MAX((1 << 8), RTE_MAX_NUMA_NODES)) +static unsigned check_hugepage_sz (unsigned flags,uint64_t hugepage_sz) {
@@ -1006,6 +1010,32 @@ malloc_heap_dump(struct malloc_heap *heap, FILE *f)rte_spinlock_unlock (&heap->lock); } +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name) +{ +static uint32_t next_socket_id =EXTERNAL_HEAP_MIN_SOCKET_ID ; + +/* prevent overflow. did you really create 2 billion heaps??? */ +if (next_socket_id >INT32_MAX ) { +RTE_LOG (ERR ,EAL ,"Cannot assign new socket ID's\n" ); + rte_errno =ENOSPC ; +return -1 ; + } + +/* initialize empty heap */ + heap->alloc_count =0 ; + heap->first =NULL ; + heap->last =NULL ; +LIST_INIT (heap->free_head); +rte_spinlock_init (&heap->lock); + heap->total_size =0 ; + heap->socket_id = next_socket_id++; + +/* set up name */ +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -33,6 +33,9 @@ void * malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags, size_t align, bool contig); +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +52 -0
61 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -311,6 +311,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_create; rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH 16/16 test: add unit tests for external memory support
In reply to Anatoly BurakovAdd simple unit tests to test external memory support. The tests are pretty basic and mostly consist of checking if invalid API calls are handled correctly, plus a simple allocation/deallocation test for malloc and memzone.
--- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 384 ++++++++++++++++++++++++++++++++++ 4 files changed, 396 insertions(+), 4 deletions(-) create mode 100644 test/test/test_external_mem.c
test/test/Makefile +1 -0
@@ -71,6 +71,7 @@ SRCS-y += test_bitmap.c SRCS-y += test_reciprocal_division.c SRCS-y += test_reciprocal_division_perf.c SRCS-y += test_fbarray.c +SRCS-y += test_external_mem.c SRCS-y += test_ring.c SRCS-y += test_ring_perf.c
test/test/autotest_data.py +10 -4
@@ -477,10 +477,16 @@ "Report": None, }, { - "Name": "Fbarray autotest", - "Command": "fbarray_autotest", - "Func": default_autotest, - "Report": None, + "Name": "Fbarray autotest", + "Command": "fbarray_autotest", + "Func": default_autotest, + "Report": None, + }, + { + "Name": "External memory autotest", + "Command": "external_mem_autotest", + "Func": default_autotest, + "Report": None, }, # #Please always keep all dump tests at the end and together!
test/test/meson.build +1 -0
@@ -155,6 +155,7 @@ test_names = [ 'eventdev_common_autotest', 'eventdev_octeontx_autotest', 'eventdev_sw_autotest', + 'external_mem_autotest', 'func_reentrancy_autotest', 'flow_classify_autotest', 'hash_scaling_autotest',
test/test/test_external_mem.c new file +384 -0
384 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH 12/16 malloc: allow adding memory to named heaps
In reply to Anatoly BurakovAdd an API to add externally allocated memory to malloc heap. The memory will be stored in memseg lists like regular DPDK memory. Multiple segments are allowed within a heap. If IOVA table is not provided, IOVA addresses are filled in with RTE_BAD_IOVA.
--- lib/librte_eal/common/include/rte_malloc.h | 39 ++++++++++++ lib/librte_eal/common/malloc_heap.c | 74 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 51 +++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 169 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +39 -0
@@ -263,6 +263,45 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Add memory chunk to a heap with specified name. +* +* @note Multiple memory chunks can be added to the same heap +* +* @note Memory must be previously allocated for DPDK to be able to use it as a +* malloc heap. Failing to do so will result in undefined behavior, up to and +* including segmentation faults. +* +* @note Calling this function will erase any contents already present at the +* supplied memory address. +* +* @param heap_name +* Name of the heap to add memory chunk to +* @param va_addr +* Start of virtual area to add to the heap +* @param len +* Length of virtual area to add to the heap +* @param iova_addrs +* Array of page IOVA addresses corresponding to each page in this memory +* area. Can be NULL, in which case page IOVA addresses will be set to +* RTE_BAD_IOVA. +* @param n_pages +* Number of elements in the iova_addrs array. Ignored if ``iova_addrs`` +* is NULL. +* @param page_sz +* Page size of the underlying memory +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to add memory to a reserved heap +* ENOSPC - no more space in internal config to store a new memory chunk +*/ +int __rte_experimental +rte_malloc_heap_memory_add (const char *heap_name,void *va_addr,size_t len, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +74 -0
80 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -39,6 +39,10 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name);int malloc_heap_destroy (struct malloc_heap *heap); +int +malloc_heap_add_external_memory (struct malloc_heap *heap,void *va_addr, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +51 -0
57 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -314,6 +314,7 @@ EXPERIMENTAL { rte_malloc_heap_create; rte_malloc_heap_destroy; rte_malloc_heap_get_socket; + rte_malloc_heap_memory_add; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH 7/16 sched: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_sched/rte_sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/librte_sched/rte_sched.c +1 -1
@@ -329,7 +329,7 @@ rte_sched_port_check_params(struct rte_sched_port_params *params)return -1 ;/* socket */ -if ((params->socket <0 ) || (params->socket >=RTE_MAX_NUMA_NODES )) +if (params->socket <0 )return -3 ;/* rate */
Signature · 1 line
2.17.1
PATCH 11/16 malloc: allow destroying heaps
In reply to Anatoly BurakovAdd an API to destroy specified heap.
--- lib/librte_eal/common/include/rte_malloc.h | 23 +++++++++ lib/librte_eal/common/malloc_heap.c | 22 ++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 58 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 107 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +23 -0
@@ -282,6 +282,29 @@ rte_malloc_get_socket_stats(int socket, int __rte_experimentalrte_malloc_heap_create (const char *heap_name); +/** +* Destroys a previously created malloc heap with specified name. +* +* @note This function will return a failure result if not all memory allocated +* from the heap has been freed back to the heap +* +* @note This function will return a failure result if not all memory segments +* were removed from the heap prior to its destruction +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* ENOENT - heap by the name of ``heap_name`` was not found +* EPERM - attempting to destroy reserved heap +* EBUSY - heap still contains data +*/ +int __rte_experimental +rte_malloc_heap_destroy (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +22 -0
@@ -1036,6 +1036,28 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name)return 0 ; } +int +malloc_heap_destroy (struct malloc_heap *heap) +{ +if (heap->alloc_count !=0 ) { +RTE_LOG (ERR ,EAL ,"Heap is still in use\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->first !=NULL || heap->last !=NULL ) { +RTE_LOG (ERR ,EAL ,"Heap still contains memory segments\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->total_size !=0 ) +RTE_LOG (ERR ,EAL ,"Total size not zero, heap is likely corrupt\n" ); + +/* after this, the lock will be dropped */ +memset (heap,0 ,sizeof (*heap)); + +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -36,6 +36,9 @@ malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags,int malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int +malloc_heap_destroy (struct malloc_heap *heap); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +58 -0
67 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -312,6 +312,7 @@ EXPERIMENTAL { rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; rte_malloc_heap_create; + rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH 14/16 malloc: allow attaching to external memory chunks
In reply to Anatoly BurakovIn order to use external memory in multiple processes, we need to attach to primary process's memseg lists, so add a new API to do that. It is the responsibility of the user to ensure that memory is accessible and that it has been previously added to the malloc heap by another process.
--- lib/librte_eal/common/include/rte_malloc.h | 32 +++++++++ lib/librte_eal/common/rte_malloc.c | 83 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 116 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +32 -0
@@ -268,6 +268,10 @@ rte_malloc_get_socket_stats(int socket,* * @note Multiple memory chunks can be added to the same heap * +* @note Before accessing this memory in other processes, it needs to be +* attached in each of those processes by calling +* ``rte_malloc_heap_memory_attach`` in each other process. +* * @note Memory must be previously allocated for DPDK to be able to use it as a * malloc heap. Failing to do so will result in undefined behavior, up to and * including segmentation faults.
@@ -329,12 +333,38 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, int __rte_experimentalrte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** +* Attach to an already existing chunk of external memory in another process. +* +* @note This function must be called before any attempt is made to use an +* already existing external memory chunk. This function does *not* need to +* be called if a call to ``rte_malloc_heap_memory_add`` was made in the +* current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful attach +* -1 on unsuccessful attach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to attach memory to a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. * * @note Heaps created via this call will automatically get assigned a unique * socket ID, which can be found using ``rte_malloc_heap_get_socket()`` * +* @note This function has to only be called in one process. +* * @param heap_name * Name of the heap to create. *
@@ -357,6 +387,8 @@ rte_malloc_heap_create(const char *heap_name);* @note This function will return a failure result if not all memory segments * were removed from the heap prior to its destruction * +* @note This function has to only be called in one process. +* * @param heap_name * Name of the heap to create. *
lib/librte_eal/common/rte_malloc.c +83 -0
89 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -315,6 +315,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_attach; rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH 9/16 malloc: add function to query socket ID of named heap
In reply to Anatoly BurakovWhen we will be creating external heaps, they will have their own "fake" socket ID, so add a function that will map the heap name to its socket ID.
--- lib/librte_eal/common/include/rte_malloc.h | 14 ++++++++ lib/librte_eal/common/rte_malloc.c | 37 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 52 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +14 -0
@@ -263,6 +263,20 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Find socket ID corresponding to a named heap. +* +* @param name +* Heap name to find socket ID for +* @return +* Socket ID in case of success (a non-negative number) +* -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``name`` was NULL +* ENOENT - heap identified by the name ``name`` was not found +*/ +int __rte_experimental +rte_malloc_heap_get_socket (const char *name); +/** * Dump statistics. *
lib/librte_eal/common/rte_malloc.c +37 -0
@@ -8,6 +8,7 @@#include <string.h> #include <sys/queue.h> +#include <rte_errno.h> #include <rte_memcpy.h> #include <rte_memory.h> #include <rte_eal.h>
@@ -183,6 +184,42 @@ rte_malloc_dump_heaps(FILE *f)rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); } +int +rte_malloc_heap_get_socket (const char *name) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +unsigned int idx; +int ret; + +if (name ==NULL || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_read_lock (&mcfg->memory_hotplug_lock); +for (idx =0 ; idx <RTE_MAX_HEAPS ; idx++) { +struct malloc_heap *tmp = &mcfg->malloc_heaps[idx]; + +if (!strncmp (name, heap->name,RTE_HEAP_NAME_MAX_LEN )) { + heap = tmp; +break ; + } + } + +if (heap !=NULL ) { + ret = heap->socket_id; + }else { + rte_errno =ENOENT ; + ret =-1 ; + } +rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +/* * Print stats on memory type. If type is NULL, info on all types is printed */
lib/librte_eal/rte_eal_version.map +1 -0
@@ -311,6 +311,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH 13/16 malloc: allow removing memory from named heaps
In reply to Anatoly BurakovAdd an API to remove memory from specified heaps. This will first check if all elements within the region are free, and that the region is the original region that was added to the heap (by comparing its length to length of memory addressed by the underlying memseg list).
--- lib/librte_eal/common/include/rte_malloc.h | 27 +++++++++++ lib/librte_eal/common/malloc_heap.c | 54 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 39 ++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 125 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -302,6 +302,33 @@ int __rte_experimental rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +/** +* Remove memory chunk from heap with specified name. +* +* @note Memory chunk being removed must be the same as one that was added; +* partially removing memory chunks is not supported +* +* @note Memory area must not contain any allocated elements to allow its +* removal from the heap +* +* @param heap_name +* Name of the heap to remove memory from +* @param va_addr +* Virtual address to remove from the heap +* @param len +* Length of virtual area to remove from the heap +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to remove memory from a reserved heap +* ENOENT - heap or memory chunk was not found +* EBUSY - memory chunk still contains data +*/ +int __rte_experimental +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +54 -0
66 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -43,6 +43,10 @@ int malloc_heap_add_external_memory(struct malloc_heap *heap, void *va_addr, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +int +malloc_heap_remove_external_memory (struct malloc_heap *heap,void *va_addr, +size_t len); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +39 -0
@@ -354,6 +354,45 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,return ret; } +int +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +int ret; + +if (heap_name ==NULL || va_addr ==NULL || len ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_write_lock (&mcfg->memory_hotplug_lock); +/* find our heap */ + heap =find_named_heap (heap_name); +if (heap ==NULL ) { + rte_errno =ENOENT ; + ret =-1 ; + goto unlock; + } +if (heap->socket_id <RTE_MAX_NUMA_NODES ) { +/* cannot remove memory from internal heaps */ + rte_errno =EPERM ; + ret =-1 ; + goto unlock; + } + +rte_spinlock_lock (&heap->lock); + ret =malloc_heap_remove_external_memory (heap, va_addr, len); +rte_spinlock_unlock (&heap->lock); + +unlock: +rte_rwlock_write_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -315,6 +315,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH 3/16 malloc: index heaps using heap ID rather than NUMA node
In reply to Anatoly BurakovSwitch over all parts of EAL to use heap ID instead of NUMA node ID to identify heaps. Heap ID for DPDK-internal heaps is NUMA node's index within the detected NUMA node list. Heap ID for external heaps will be order of their creation.
--- config/common_base | 1 + config/rte_config.h | 1 + .../common/include/rte_eal_memconfig.h | 4 +- .../common/include/rte_malloc_heap.h | 1 + lib/librte_eal/common/malloc_heap.c | 85 +++++++++++++------ lib/librte_eal/common/malloc_heap.h | 3 + lib/librte_eal/common/rte_malloc.c | 41 ++++++--- 7 files changed, 94 insertions(+), 42 deletions(-)
config/common_base +1 -0
@@ -61,6 +61,7 @@ CONFIG_RTE_CACHE_LINE_SIZE=64 CONFIG_RTE_LIBRTE_EAL=y CONFIG_RTE_MAX_LCORE=128 CONFIG_RTE_MAX_NUMA_NODES=8 +CONFIG_RTE_MAX_HEAPS=32 CONFIG_RTE_MAX_MEMSEG_LISTS=64 # each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages # or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
config/rte_config.h +1 -0
@@ -21,6 +21,7 @@/****** library defines ********/ /* EAL defines */ +#define RTE_MAX_HEAPS 32#define RTE_MAX_MEMSEG_LISTS 128#define RTE_MAX_MEMSEG_PER_LIST 8192#define RTE_MAX_MEM_MB_PER_LIST 32768
lib/librte_eal/common/include/rte_eal_memconfig.h +2 -2
@@ -72,8 +72,8 @@ struct rte_mem_config {struct rte_tailq_head tailq_head[RTE_MAX_TAILQ ];/**< Tailqs for objects */ -/* Heaps of Malloc per socket */ -struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES ]; +/* Heaps of Malloc */ +struct malloc_heap malloc_heaps[RTE_MAX_HEAPS ];/* address of mem_config in primary process. used to map shared config into * exact same address the primary process maps it.
lib/librte_eal/common/include/rte_malloc_heap.h +1 -0
@@ -27,6 +27,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size; + unsigned int socket_id; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +57 -28
181 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -46,6 +46,9 @@ malloc_heap_get_stats(struct malloc_heap *heap,void malloc_heap_dump (struct malloc_heap *heap,FILE *f); +int +malloc_socket_to_heap_id (unsigned int socket_id); +int rte_eal_malloc_heap_init (void );
lib/librte_eal/common/rte_malloc.c +29 -12
72 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH 5/16 flow_classify: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_flow_classify/rte_flow_classify.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_flow_classify/rte_flow_classify.c +1 -2
@@ -247,8 +247,7 @@ rte_flow_classifier_check_params(struct rte_flow_classifier_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_FLOW_CLASSIFY_LOG (ERR ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH 8/16 malloc: add name to malloc heaps
In reply to Anatoly BurakovWe will need to refer to external heaps in some way. While we use heap ID's internally, for external API use it has to be something more user-friendly. So, we will be using a string to uniquely identify a heap.
--- lib/librte_eal/common/include/rte_malloc_heap.h | 2 ++ lib/librte_eal/common/malloc_heap.c | 15 ++++++++++++++- lib/librte_eal/common/rte_malloc.c | 1 + 3 files changed, 17 insertions(+), 1 deletion(-)
lib/librte_eal/common/include/rte_malloc_heap.h +2 -0
@@ -12,6 +12,7 @@/* Number of free lists per heap, grouped by size. */ #define RTE_HEAP_NUM_FREELISTS 13 +#define RTE_HEAP_NAME_MAX_LEN 32/* dummy definition, for pointers */ struct malloc_elem ;
@@ -28,6 +29,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size;unsigned int socket_id; + char name[RTE_HEAP_NAME_MAX_LEN]; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +14 -1
@@ -127,7 +127,6 @@ malloc_add_seg(const struct rte_memseg_list *msl,malloc_heap_add_memory (heap, found_msl, ms->addr, len); heap->total_size += len; - heap->socket_id = msl->socket_id;RTE_LOG (DEBUG ,EAL ,"Added %zuM to heap on socket %i\n" , len >>20 , msl->socket_id);
@@ -1011,6 +1010,20 @@ intrte_eal_malloc_heap_init (void) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +unsigned int i; + +/* assign names to default DPDK heaps */ +for (i =0 ; i <rte_socket_count (); i++) { +struct malloc_heap *heap = &mcfg->malloc_heaps[i]; +char heap_name[RTE_HEAP_NAME_MAX_LEN ]; +int socket_id =rte_socket_id_by_idx (i); + +snprintf (heap_name,sizeof (heap_name) -1 , +"socket_%i" , socket_id); +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); + heap->socket_id = socket_id; + } +if (register_mp_requests ()) {RTE_LOG (ERR ,EAL ,"Couldn't register malloc multiprocess actions\n" );
lib/librte_eal/common/rte_malloc.c +1 -0
@@ -202,6 +202,7 @@ rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)malloc_heap_get_stats (heap, &sock_stats);fprintf (f,"Heap id:%u\n" , heap_id); +fprintf (f,"\tHeap name:%s\n" , heap->name);fprintf (f,"\tHeap_size:%zu,\n" , sock_stats.heap_totalsz_bytes);fprintf (f,"\tFree_size:%zu,\n" , sock_stats.heap_freesz_bytes);fprintf (f,"\tAlloc_size:%zu,\n" , sock_stats.heap_allocsz_bytes);
Signature · 1 line
2.17.1
PATCH 15/16 malloc: allow detaching from external memory
In reply to Anatoly BurakovAdd API to detach from existing chunk of external memory in a process.
--- lib/librte_eal/common/include/rte_malloc.h | 27 ++++++++++++++++++++++ lib/librte_eal/common/rte_malloc.c | 27 ++++++++++++++++++---- lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 50 insertions(+), 5 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -315,6 +315,9 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,* @note Memory area must not contain any allocated elements to allow its * removal from the heap * +* @note All other processes must detach from the memory chunk prior to it being +* removed from the heap. +* * @param heap_name * Name of the heap to remove memory from * @param va_addr
@@ -357,6 +360,30 @@ rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len); int __rte_experimentalrte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** +* Detach from a chunk of external memory in secondary process. +* +* @note This function must be called in before any attempt is made to remove +* external memory from the heap in another process. This function does *not* +* need to be called if a call to ``rte_malloc_heap_memory_remove`` will be +* called in current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful detach +* -1 on unsuccessful detach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to detach memory from a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/rte_malloc.c +22 -5
@@ -397,10 +397,11 @@ struct sync_mem_walk_arg {void *va_addr;size_t len;int result; +bool attach; };static int -attach_mem_walk (const struct rte_memseg_list *msl,void *arg) +sync_mem_walk (const struct rte_memseg_list *msl,void *arg) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct sync_mem_walk_arg *wa = arg;
@@ -415,7 +416,10 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg) msl_idx = msl - mcfg->memsegs; found_msl = &mcfg->memsegs[msl_idx]; - ret =rte_fbarray_attach (&found_msl->memseg_arr); +if (wa->attach) + ret =rte_fbarray_attach (&found_msl->memseg_arr); +else + ret =rte_fbarray_detach (&found_msl->memseg_arr);if (ret <0 ) wa->result = -rte_errno;
@@ -426,8 +430,8 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg)return 0 ; } -int -rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +static int +sync_memory (const char *heap_name,void *va_addr,size_t len,bool attach) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct malloc_heap *heap =NULL ;
@@ -461,9 +465,10 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len) wa.va_addr = va_addr; wa.len = len; wa.result = -ENOENT ;/* fail unless explicitly told to succeed */ + wa.attach = attach;/* we're already holding a read lock */ -rte_memseg_list_walk_thread_unsafe (attach_mem_walk, &wa); +rte_memseg_list_walk_thread_unsafe (sync_mem_walk, &wa);if (wa.result <0 ) { rte_errno = -wa.result;
@@ -476,6 +481,18 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)return ret; } +int +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, true); +} + +int +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, false); +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -316,6 +316,7 @@ EXPERIMENTAL { rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; rte_malloc_heap_memory_attach; + rte_malloc_heap_memory_detach; rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH 4/16 mem: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_eal/common/eal_common_memzone.c | 8 +++++--- lib/librte_eal/common/rte_malloc.c | 4 ---- 2 files changed, 5 insertions(+), 7 deletions(-)
lib/librte_eal/common/eal_common_memzone.c +5 -3
@@ -120,13 +120,15 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,return NULL ; } -if ((socket_id !=SOCKET_ID_ANY ) && - (socket_id >=RTE_MAX_NUMA_NODES || socket_id <0 )) { +if ((socket_id !=SOCKET_ID_ANY ) && socket_id <0 ) { rte_errno =EINVAL ;return NULL ; } -if (!rte_eal_has_hugepages ()) +/* only set socket to SOCKET_ID_ANY if we aren't allocating for an +* external heap. +*/ +if (!rte_eal_has_hugepages () && socket_id <RTE_MAX_NUMA_NODES ) socket_id =SOCKET_ID_ANY ; contig = (flags &RTE_MEMZONE_IOVA_CONTIG ) !=0 ;
lib/librte_eal/common/rte_malloc.c +0 -4
@@ -47,10 +47,6 @@ rte_malloc_socket(const char *type, size_t size, unsigned int align,if (!rte_eal_has_hugepages ()) socket_arg =SOCKET_ID_ANY ; -/* Check socket parameter */ -if (socket_arg >=RTE_MAX_NUMA_NODES ) -return NULL ; -return malloc_heap_alloc (type, size, socket_arg,0 , align ==0 ?1 : align,0 , false); }
Signature · 1 line
2.17.1
Hi Anatoly,
First thanks for the patchset, it is a great enhancement.
See question below.
Tuesday, September 4, 2018 4:12 PM, Anatoly Burakov:
Quoted 21 lines
Subject: [dpdk-dev] [PATCH 00/16] Support externally allocated memory in DPDK
This is a proposal to enable using externally allocated memory in DPDK.
In a nutshell, here is what is being done here:
- Index internal malloc heaps by NUMA node index, rather than NUMA node itself (external heaps will have ID's in order of creation)
- Add identifier string to malloc heap, to uniquely identify it
- Each new heap will receive a unique socket ID that will be used by allocator to decide from which heap (internal or external) to allocate requested amount of memory
- Allow creating named heaps and add/remove memory to/from those heaps
- Allocate memseg lists at runtime, to keep track of IOVA addresses of externally allocated memory
- If IOVA addresses aren't provided, useRTE_BAD_IOVA
- Allow malloc and memzones to allocate from external heaps
- Allow other data structures to allocate from externall heaps
Quoted 3 lines
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
That makes sense. However who should be in-charge of mapping this memory for dma access? The user or internally be the PMD when encounter the first packet or while traversing the existing mempools?
Quoted 81 lines
The general approach is to create heap and add memory into it. For any other process wishing to use the same memory, said memory must first be attached (otherwise some things will not work).
A design decision was made to make multiprocess synchronization a manual process. Due to underlying issues with attaching to fbarrays in secondary processes, this design was deemed to be better because we don't want to fail to create external heap in the primary because something in the secondary has failed when in fact we may not eve have wanted this memory to be accessible in the secondary in the first place.
Using external memory in multiprocess is *hard*, because not only memory space needs to be preallocated, but it also needs to be attached in each process to allow other processes to access the page table. The attach API call may or may not succeed, depending on memory layout, for reasons similar to other multiprocess failures. This is treated as a "known issue" for this release.
RFC -> v1 changes:
- Removed the "named heaps" API, allocate using fake socket ID instead
- Added multiprocess support
- Everything is now thread-safe
- Numerous bugfixes and API improvementsAnatoly Burakov (16):
mem: add length to memseg list
mem: allow memseg lists to be marked as external
malloc: index heaps using heap ID rather than NUMA node
mem: do not check for invalid socket ID
flow_classify: do not check for invalid socket ID
pipeline: do not check for invalid socket ID
sched: do not check for invalid socket ID
malloc: add name to malloc heaps
malloc: add function to query socket ID of named heap
malloc: allow creating malloc heaps
malloc: allow destroying heaps
malloc: allow adding memory to named heaps
malloc: allow removing memory from named heaps
malloc: allow attaching to external memory chunks
malloc: allow detaching from external memory
test: add unit tests for external memory supportconfig/common_base | 1 + config/rte_config.h | 1 + drivers/bus/fslmc/fslmc_vfio.c | 7 +- drivers/bus/pci/linux/pci.c | 2 +- drivers/net/mlx4/mlx4_mr.c | 3 + drivers/net/mlx5/mlx5.c | 5 +- drivers/net/mlx5/mlx5_mr.c | 3 + drivers/net/virtio/virtio_user/vhost_kernel.c | 5 +- lib/librte_eal/bsdapp/eal/eal.c | 3 + lib/librte_eal/bsdapp/eal/eal_memory.c | 9 +- lib/librte_eal/common/eal_common_memory.c | 9 +- lib/librte_eal/common/eal_common_memzone.c | 8 +- .../common/include/rte_eal_memconfig.h | 6 +- lib/librte_eal/common/include/rte_malloc.h | 181 +++++++++ .../common/include/rte_malloc_heap.h | 3 + lib/librte_eal/common/include/rte_memory.h | 9 + lib/librte_eal/common/malloc_heap.c | 287 +++++++++++-- lib/librte_eal/common/malloc_heap.h | 17 + lib/librte_eal/common/rte_malloc.c | 383 ++++++++++++++++- lib/librte_eal/linuxapp/eal/eal.c | 3 + lib/librte_eal/linuxapp/eal/eal_memalloc.c | 12 +- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +- lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 +- lib/librte_eal/rte_eal_version.map | 7 + lib/librte_flow_classify/rte_flow_classify.c | 3 +- lib/librte_mempool/rte_mempool.c | 31 +- lib/librte_pipeline/rte_pipeline.c | 3 +- lib/librte_sched/rte_sched.c | 2 +- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 384 ++++++++++++++++++ test/test/test_malloc.c | 3 + test/test/test_memzone.c | 3 + 34 files changed, 1346 insertions(+), 84 deletions(-) create mode 100644
test/test/test_external_mem.c--
2.17.1
Quoted 28 lines · Shahaf Shuler
Hi Anatoly,
First thanks for the patchset, it is a great enhancement.
See question below.
Tuesday, September 4, 2018 4:12 PM, Anatoly Burakov:
Quoted 21 lines
Subject: [dpdk-dev] [PATCH 00/16] Support externally allocated memory in DPDK
This is a proposal to enable using externally allocated memory in DPDK.
In a nutshell, here is what is being done here:
- Index internal malloc heaps by NUMA node index, rather than NUMA node itself (external heaps will have ID's in order of creation)
- Add identifier string to malloc heap, to uniquely identify it
- Each new heap will receive a unique socket ID that will be used by allocator to decide from which heap (internal or external) to allocate requested amount of memory
- Allow creating named heaps and add/remove memory to/from those heaps
- Allocate memseg lists at runtime, to keep track of IOVA addresses of externally allocated memory
- If IOVA addresses aren't provided, useRTE_BAD_IOVA
- Allow malloc and memzones to allocate from external heaps
- Allow other data structures to allocate from externall heaps
Quoted 7 lines
Quoted 3 lines
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
That makes sense. However who should be in-charge of mapping this memory for dma access? The user or internally be the PMD when encounter the first packet or while traversing the existing mempools?
Hi Shahaf,
There are two ways this can be solved. The first way is to perform VFIO mapping automatically on adding/attaching memory. The second is to force user to do it manually. For now, the latter is chosen because user knows best if they intend to do DMA on that memory, but i'm open to suggestions.
There is an issue with some devices and buses (i.e. bus/fslmc) bypassing EAL VFIO infrastructure and performing their own VFIO/DMA mapping magic, but solving that problem is outside the scope of this patchset. Those devices/buses should fix themselves :)
When not using VFIO, it's out of our hands anyway.
Signature · 2 lines
Thanks, Anatoly
Monday, September 17, 2018 1:07 PM, Burakov, Anatoly:
Quoted 4 lines
Subject: Re: [dpdk-dev] [PATCH 00/16] Support externally allocated memory in DPDK
On 13-Sep-18 8:44 AM, Shahaf Shuler wrote:
[...]
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
That makes sense. However who should be in-charge of mapping this
memory for dma access?
Quoted 1 line
The user or internally be the PMD when encounter the first packet or while
Quoted 8 lines
traversing the existing mempools?
Quoted 1 line
Hi Shahaf,
There are two ways this can be solved. The first way is to perform VFIO mapping automatically on adding/attaching memory. The second is to force user to do it manually. For now, the latter is chosen because user knows best if they intend to do DMA on that memory, but i'm open to suggestions.
I agree with that approach, and will add not only if the mempool is for dma or not but also which ports will use this mempool (this can effect on the mapping). However I don't think this is generic enough to use only VFIO. As you said, there are some devices not using VFIO for mapping rather some proprietary driver utility. IMO DPDK should introduce generic and device agnostic APIs to the user.
My suggestion is instead of doing vfio_dma_map that or vfio_dma_unmap that have a generic dma_map(uint8_t port, address, len). Each driver will register with its own mapping callback (can be vfio_dma_map). It can be outside of this series, just wondering the people opinion on such approach.
Quoted 7 lines
There is an issue with some devices and buses (i.e. bus/fslmc) bypassing EAL VFIO infrastructure and performing their own VFIO/DMA mapping magic, but solving that problem is outside the scope of this patchset. Those devices/buses should fix themselves :)
When not using VFIO, it's out of our hands anyway.
Why?
VFIO is not a must requirement for devices in DPDK.
Quoted 4 lines
--
Thanks,
Anatoly
Quoted 17 lines · Shahaf Shuler
Monday, September 17, 2018 1:07 PM, Burakov, Anatoly:
Quoted 4 lines
Subject: Re: [dpdk-dev] [PATCH 00/16] Support externally allocated memory in DPDK
On 13-Sep-18 8:44 AM, Shahaf Shuler wrote:
[...]
Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
That makes sense. However who should be in-charge of mapping this
memory for dma access?
Quoted 1 line
The user or internally be the PMD when encounter the first packet or while
traversing the existing mempools?
Quoted 1 line
Quoted 8 lines
Quoted 6 lines
Hi Shahaf,
There are two ways this can be solved. The first way is to perform VFIO mapping automatically on adding/attaching memory. The second is to force user to do it manually. For now, the latter is chosen because user knows best if they intend to do DMA on that memory, but i'm open to suggestions.
I agree with that approach, and will add not only if the mempool is for dma or not but also which ports will use this mempool (this can effect on the mapping).
That is perhaps too hardware-specific - this should probably be handled inside the driver callbacks.
Quoted 5 lines
However I don't think this is generic enough to use only VFIO. As you said, there are some devices not using VFIO for mapping rather some proprietary driver utility. IMO DPDK should introduce generic and device agnostic APIs to the user.
My suggestion is instead of doing
vfio_dma_mapthat orvfio_dma_unmapthat have a genericdma_map(uint8_t port, address, len). Each driver will register with its own mapping callback (can bevfio_dma_map). It can be outside of this series, just wondering the people opinion on such approach.
I don't disagree. I don't like bus/net/etc drivers doing their own thing with regards to mapping, and i would by far prefer generic way to set up DMA maps, to which VFIO will be a subscriber.
Quoted 11 lines
Quoted 8 lines
There is an issue with some devices and buses (i.e. bus/fslmc) bypassing EAL VFIO infrastructure and performing their own VFIO/DMA mapping magic, but solving that problem is outside the scope of this patchset. Those devices/buses should fix themselves :)
When not using VFIO, it's out of our hands anyway.
Why?
VFIO is not a must requirement for devices in DPDK.
When i say "out of our hands", what i mean to say is, currently as far as EAL API is concerned, there is no DMA mapping outside of VFIO.
Quoted 5 lines
Quoted 5 lines
--
Thanks,
Anatoly
Signature · 2 lines
Thanks, Anatoly
Quoted 29 lines · Anatoly
Quoted 17 lines · Shahaf Shuler
Monday, September 17, 2018 1:07 PM, Burakov, Anatoly:
Quoted 4 lines
Subject: Re: [dpdk-dev] [PATCH 00/16] Support externally allocated memory in DPDK
On 13-Sep-18 8:44 AM, Shahaf Shuler wrote:
[...]
Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
That makes sense. However who should be in-charge of mapping this
memory for dma access?
Quoted 1 line
The user or internally be the PMD when encounter the first packet or while
traversing the existing mempools?
Quoted 1 line
Quoted 8 lines
Quoted 6 lines
Hi Shahaf,
There are two ways this can be solved. The first way is to perform VFIO mapping automatically on adding/attaching memory. The second is to force user to do it manually. For now, the latter is chosen because user knows best if they intend to do DMA on that memory, but i'm open to suggestions.
I agree with that approach, and will add not only if the mempool is for dma or not but also which ports will use this mempool (this can effect on the mapping).
That is perhaps too hardware-specific - this should probably be handled inside the driver callbacks.
Quoted 14 lines
Quoted 5 lines
However I don't think this is generic enough to use only VFIO. As you said, there are some devices not using VFIO for mapping rather some proprietary driver utility. IMO DPDK should introduce generic and device agnostic APIs to the user.
My suggestion is instead of doing
vfio_dma_mapthat orvfio_dma_unmapthat have a genericdma_map(uint8_t port, address, len). Each driver will register with its own mapping callback (can bevfio_dma_map). It can be outside of this series, just wondering the people opinion on such approach.I don't disagree. I don't like bus/net/etc drivers doing their own thing with regards to mapping, and i would by far prefer generic way to set up DMA maps, to which VFIO will be a subscriber.
Quoted 6 lines
Quoted 6 lines
There is an issue with some devices and buses (i.e. bus/fslmc) bypassing EAL VFIO infrastructure and performing their own VFIO/DMA mapping magic, but solving that problem is outside the scope of this patchset. Those devices/buses should fix themselves :)
DMA mapping is a very common principle and can be easily be a candidate for lets-make-generic-movement, but, being close to hardware (or hardware specific), it does require the driver to have some flexibility in terms of its eventual implementation.
I maintain one of those drivers (bus/fslmc) in DPDK which needs to have special VFIO layer - and from that experience, I can say that VFIO mapping does require some flexibility. SoC semantics are sometimes too complex to pin to general-universally-agreed-standard concept. (or, one can easily call it a 'bug', while it is a 'feature' for others :D)
In fact, NXP has another driver (bus/dpaa) which doesn't even work with VFIO - loves to work directly with Phys_addr. And, it is not at a lower priority than one with VFIO.
Thus, I really don't think a strongly controlled VFIO mapping should be EAL's responsibility. Failure because of lack of mapping is a driver's problem.
Quoted 15 lines
Quoted 5 lines
Quoted 2 lines
When not using VFIO, it's out of our hands anyway.
Why?
VFIO is not a must requirement for devices in DPDK.When i say "out of our hands", what i mean to say is, currently as far as EAL API is concerned, there is no DMA mapping outside of VFIO.
Quoted 5 lines
Quoted 5 lines
Signature · 3 lines
-- Thanks, Anatoly
Quoted 40 lines · Shreyansh Jain
Quoted 39 lines · Anatoly
Quoted 17 lines · Shahaf Shuler
Monday, September 17, 2018 1:07 PM, Burakov, Anatoly:
Quoted 4 lines
Subject: Re: [dpdk-dev] [PATCH 00/16] Support externally allocated memory in DPDK
On 13-Sep-18 8:44 AM, Shahaf Shuler wrote:
[...]
Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
That makes sense. However who should be in-charge of mapping this
memory for dma access?
Quoted 1 line
The user or internally be the PMD when encounter the first packet or while
traversing the existing mempools?
Quoted 1 line
Quoted 8 lines
Quoted 6 lines
Hi Shahaf,
There are two ways this can be solved. The first way is to perform VFIO mapping automatically on adding/attaching memory. The second is to force user to do it manually. For now, the latter is chosen because user knows best if they intend to do DMA on that memory, but i'm open to suggestions.
I agree with that approach, and will add not only if the mempool is for dma or not but also which ports will use this mempool (this can effect on the mapping).
That is perhaps too hardware-specific - this should probably be handled inside the driver callbacks.
Quoted 5 lines
However I don't think this is generic enough to use only VFIO. As you said, there are some devices not using VFIO for mapping rather some proprietary driver utility. IMO DPDK should introduce generic and device agnostic APIs to the user.
My suggestion is instead of doing
vfio_dma_mapthat orvfio_dma_unmapthat have a genericdma_map(uint8_t port, address, len). Each driver will register with its own mapping callback (can bevfio_dma_map). It can be outside of this series, just wondering the people opinion on such approach.I don't disagree. I don't like bus/net/etc drivers doing their own thing with regards to mapping, and i would by far prefer generic way to set up DMA maps, to which VFIO will be a subscriber.
Quoted 2 lines
Quoted 2 lines
Quoted 6 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
There is an issue with some devices and buses (i.e. bus/fslmc) bypassing EAL VFIO infrastructure and performing their own VFIO/DMA mapping magic, but solving that problem is outside the scope of this patchset. Those devices/buses should fix themselves :)
DMA mapping is a very common principle and can be easily be a candidate for lets-make-generic-movement, but, being close to hardware (or hardware specific), it does require the driver to have some flexibility in terms of its eventual implementation.
Perhaps i didn't word my response clearly enough. I didn't mean to say (or imply) that EAL must handle all DMA mappings itself. Rather, EAL should provide a generic infrastructure of maintaining current mappings etc., and provide a subscription mechanism for other users (e.g. drivers) so that the details of implementation of exactly how to map things for DMA is up to the drivers.
In other words, we agree :)
Quoted 7 lines
I maintain one of those drivers (bus/fslmc) in DPDK which needs to have special VFIO layer - and from that experience, I can say that VFIO mapping does require some flexibility. SoC semantics are sometimes too complex to pin to general-universally-agreed-standard concept. (or, one can easily call it a 'bug', while it is a 'feature' for others :D)
In fact, NXP has another driver (bus/dpaa) which doesn't even work with VFIO - loves to work directly with
Phys_addr. And, it is not at a lower priority than one with VFIO.Thus, I really don't think a strongly controlled VFIO mapping should be EAL's responsibility. Failure because of lack of mapping is a driver's problem.
While EAL doesn't necessarily need to be involved with mapping things for VFIO, i believe it does need to be the authority on what gets mapped. The user needs a way to make arbitrary memory available for DMA - this is where EAL comes in. VFIO itself can be factored out into a separate subsystem (DMA drivers, anyone? :D ), but given that memory cometh and goeth (external memory included), and given that some things tend to be a bit complicated [*], EAL needs to know when something is supposed to be mapped or unmapped, and when to notify subscribers that they may have to refresh their DMA maps.
[*] for example, VFIO can only do mappings whenever there are devices actually attached to a VFIO container, so we have to maintain all maps between hotplug events to ensure that memory set up for DMA doesn't silently get unmapped on device detach and subsequent attach.
Signature · 2 lines
Thanks, Anatoly
v2 cover Support externally allocated memory in DPDK
In reply to Anatoly BurakovThis is a proposal to enable using externally allocated memory in DPDK.
In a nutshell, here is what is being done here:
- Index internal malloc heaps by NUMA node index, rather than NUMA node itself (external heaps will have ID's in order of creation)
- Add identifier string to malloc heap, to uniquely identify it
- Each new heap will receive a unique socket ID that will be used by allocator to decide from which heap (internal or external) to allocate requested amount of memory
- Allow creating named heaps and add/remove memory to/from those heaps
- Allocate memseg lists at runtime, to keep track of IOVA addresses of externally allocated memory
- If IOVA addresses aren't provided, use RTE_BAD_IOVA
- Allow malloc and memzones to allocate from external heaps
- Allow other data structures to allocate from externall heaps
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
The general approach is to create heap and add memory into it. For any other process wishing to use the same memory, said memory must first be attached (otherwise some things will not work).
A design decision was made to make multiprocess synchronization a manual process. Due to underlying issues with attaching to fbarrays in secondary processes, this design was deemed to be better because we don't want to fail to create external heap in the primary because something in the secondary has failed when in fact we may not eve have wanted this memory to be accessible in the secondary in the first place.
Using external memory in multiprocess is *hard*, because not only memory space needs to be preallocated, but it also needs to be attached in each process to allow other processes to access the page table. The attach API call may or may not succeed, depending on memory layout, for reasons similar to other multiprocess failures. This is treated as a "known issue" for this release.
Creating and destroying heaps is currently restricted to primary processes, because we need to keep track of all socket ID's we've ever used to prevent their reuse, and obviously different processes would have kept different socket ID counters, and it isn't important enough to put into shared memory. This means that secondary processes will not be able to create new heaps. If this use case is important enough, we can put the max socket ID into shared memory, or allow socket ID reuse (which i do not think is a good idea because it has the potential to make things harder to debug).
v2 -> v1 changes:
- Fixed NULL dereference on heap socket ID lookup
- Fixed memseg offset calculation on adding memory to heap
- Improved unit test to test for above bugfixes
- Restricted heap creation to primary processes only
- Added sample application
- Added documentation
RFC -> v1 changes:
- Removed the "named heaps" API, allocate using fake socket ID instead
- Added multiprocess support
- Everything is now thread-safe
- Numerous bugfixes and API improvements
Anatoly Burakov (20):
mem: add length to memseg list
mem: allow memseg lists to be marked as external
malloc: index heaps using heap ID rather than NUMA node
mem: do not check for invalid socket ID
flow_classify: do not check for invalid socket ID
pipeline: do not check for invalid socket ID
sched: do not check for invalid socket ID
malloc: add name to malloc heaps
malloc: add function to query socket ID of named heap
malloc: allow creating malloc heaps
malloc: allow destroying heaps
malloc: allow adding memory to named heaps
malloc: allow removing memory from named heaps
malloc: allow attaching to external memory chunks
malloc: allow detaching from external memory
test: add unit tests for external memory support
examples: add external memory example app
doc: add external memory feature to the release notes
doc: add external memory feature to programmer's guide
doc: add external memory sample application guide
config/common_base | 1 + config/rte_config.h | 1 + .../prog_guide/env_abstraction_layer.rst | 38 ++ doc/guides/rel_notes/deprecation.rst | 15 - doc/guides/rel_notes/release_18_11.rst | 24 +- doc/guides/sample_app_ug/external_mem.rst | 115 +++++ doc/guides/sample_app_ug/index.rst | 1 + drivers/bus/fslmc/fslmc_vfio.c | 7 +- drivers/bus/pci/linux/pci.c | 2 +- drivers/net/mlx4/mlx4_mr.c | 3 + drivers/net/mlx5/mlx5.c | 5 +- drivers/net/mlx5/mlx5_mr.c | 3 + drivers/net/virtio/virtio_user/vhost_kernel.c | 5 +- examples/external_mem/Makefile | 62 +++ examples/external_mem/extmem.c | 461 ++++++++++++++++++ examples/external_mem/meson.build | 12 + lib/librte_eal/bsdapp/eal/Makefile | 2 +- lib/librte_eal/bsdapp/eal/eal.c | 3 + lib/librte_eal/bsdapp/eal/eal_memory.c | 9 +- lib/librte_eal/common/eal_common_memory.c | 9 +- lib/librte_eal/common/eal_common_memzone.c | 8 +- .../common/include/rte_eal_memconfig.h | 6 +- lib/librte_eal/common/include/rte_malloc.h | 183 +++++++ .../common/include/rte_malloc_heap.h | 3 + lib/librte_eal/common/include/rte_memory.h | 9 + lib/librte_eal/common/malloc_heap.c | 300 ++++++++++-- lib/librte_eal/common/malloc_heap.h | 17 + lib/librte_eal/common/rte_malloc.c | 393 ++++++++++++++- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 10 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 12 +- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +- lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 +- lib/librte_eal/meson.build | 2 +- lib/librte_eal/rte_eal_version.map | 7 + lib/librte_flow_classify/rte_flow_classify.c | 3 +- lib/librte_mempool/rte_mempool.c | 31 +- lib/librte_pipeline/rte_pipeline.c | 3 +- lib/librte_sched/rte_sched.c | 2 +- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 +++++++++++++++ test/test/test_malloc.c | 3 + test/test/test_memzone.c | 3 + 45 files changed, 2096 insertions(+), 105 deletions(-) create mode 100644 doc/guides/sample_app_ug/external_mem.rst create mode 100644 examples/external_mem/Makefile create mode 100644 examples/external_mem/extmem.c create mode 100644 examples/external_mem/meson.build create mode 100644 test/test/test_external_mem.c
Signature · 1 line
2.17.1
PATCH v2 1/20 mem: add length to memseg list
Previously, to calculate length of memory area covered by a memseg list, we would've needed to multiply page size by length of fbarray backing that memseg list. This is not obvious and unnecessarily low level, so store length in the memseg list itself.
--- drivers/bus/pci/linux/pci.c | 2 +- lib/librte_eal/bsdapp/eal/eal_memory.c | 2 ++ lib/librte_eal/common/eal_common_memory.c | 5 ++--- lib/librte_eal/common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/linuxapp/eal/eal_memalloc.c | 3 ++- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +++- 6 files changed, 11 insertions(+), 6 deletions(-)
drivers/bus/pci/linux/pci.c +1 -1
@@ -119,7 +119,7 @@ rte_pci_unmap_device(struct rte_pci_device *dev)static int find_max_end_va (const struct rte_memseg_list *msl,void *arg) { -size_t sz = msl->memseg_arr.len * msl->page_sz; +size_t sz = msl->len;void *end_va =RTE_PTR_ADD (msl->base_va, sz);void **max_va = arg;
lib/librte_eal/bsdapp/eal/eal_memory.c +2 -0
@@ -79,6 +79,7 @@ rte_eal_hugepage_init(void) } msl->base_va = addr; msl->page_sz = page_sz; + msl->len = internal_config.memory; msl->socket_id =0 ;/* populate memsegs. each memseg is 1 page long */
@@ -370,6 +371,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
lib/librte_eal/common/eal_common_memory.c +2 -3
@@ -171,7 +171,7 @@ virt2memseg(const void *addr, const struct rte_memseg_list *msl)/* a memseg list was specified, check if it's the right one */ start = msl->base_va; - end =RTE_PTR_ADD (start, (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr < start || addr >= end)return NULL ;
@@ -194,8 +194,7 @@ virt2memseg_list(const void *addr) msl = &mcfg->memsegs[msl_idx]; start = msl->base_va; - end =RTE_PTR_ADD (start, - (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr >= start && addr < end)break ; }
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -30,6 +30,7 @@ struct rte_memseg_list {uint64_t addr_64;/**< Makes sure addr is always 64-bits */ }; +size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ volatile uint32_t version;/**< version number for multiprocess sync. */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +2 -1
@@ -828,7 +828,7 @@ free_seg_walk(const struct rte_memseg_list *msl, void *arg)int msl_idx, seg_idx, ret, dir_fd =-1 ; start_addr = (uintptr_t ) msl->base_va; - end_addr = start_addr + msl->memseg_arr.len * (size_t )msl->page_sz; + end_addr = start_addr + msl->len;if ((uintptr_t )wa->ms->addr < start_addr || (uintptr_t )wa->ms->addr >= end_addr)
@@ -1314,6 +1314,7 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,return -1 ; } local_msl->base_va = primary_msl->base_va; + local_msl->len = primary_msl->len;return 0 ; }
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -1
@@ -857,6 +857,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
@@ -1365,6 +1366,7 @@ eal_legacy_hugepage_init(void) msl->base_va = addr; msl->page_sz = page_sz; msl->socket_id =0 ; + msl->len = internal_config.memory;/* populate memsegs. each memseg is one page long */ for (cur_seg =0 ; cur_seg < n_segs; cur_seg++) {
@@ -1611,7 +1613,7 @@ eal_legacy_hugepage_init(void)if (msl->memseg_arr.count >0 )continue ;/* this is an unused list, deallocate it */ - mem_sz = (size_t )msl->page_sz * msl->memseg_arr.len; + mem_sz = msl->len;munmap (msl->base_va, mem_sz); msl->base_va =NULL ;
Signature · 1 line
2.17.1
PATCH v2 3/20 malloc: index heaps using heap ID rather than NUMA node
In reply to Anatoly BurakovSwitch over all parts of EAL to use heap ID instead of NUMA node ID to identify heaps. Heap ID for DPDK-internal heaps is NUMA node's index within the detected NUMA node list. Heap ID for external heaps will be order of their creation.
--- config/common_base | 1 + config/rte_config.h | 1 + .../common/include/rte_eal_memconfig.h | 4 +- .../common/include/rte_malloc_heap.h | 1 + lib/librte_eal/common/malloc_heap.c | 98 +++++++++++++------ lib/librte_eal/common/malloc_heap.h | 3 + lib/librte_eal/common/rte_malloc.c | 41 +++++--- 7 files changed, 106 insertions(+), 43 deletions(-)
config/common_base +1 -0
@@ -61,6 +61,7 @@ CONFIG_RTE_CACHE_LINE_SIZE=64 CONFIG_RTE_LIBRTE_EAL=y CONFIG_RTE_MAX_LCORE=128 CONFIG_RTE_MAX_NUMA_NODES=8 +CONFIG_RTE_MAX_HEAPS=32 CONFIG_RTE_MAX_MEMSEG_LISTS=64 # each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages # or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
config/rte_config.h +1 -0
@@ -21,6 +21,7 @@/****** library defines ********/ /* EAL defines */ +#define RTE_MAX_HEAPS 32#define RTE_MAX_MEMSEG_LISTS 128#define RTE_MAX_MEMSEG_PER_LIST 8192#define RTE_MAX_MEM_MB_PER_LIST 32768
lib/librte_eal/common/include/rte_eal_memconfig.h +2 -2
@@ -72,8 +72,8 @@ struct rte_mem_config {struct rte_tailq_head tailq_head[RTE_MAX_TAILQ ];/**< Tailqs for objects */ -/* Heaps of Malloc per socket */ -struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES ]; +/* Heaps of Malloc */ +struct malloc_heap malloc_heaps[RTE_MAX_HEAPS ];/* address of mem_config in primary process. used to map shared config into * exact same address the primary process maps it.
lib/librte_eal/common/include/rte_malloc_heap.h +1 -0
@@ -27,6 +27,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size; + unsigned int socket_id; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +69 -29
204 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -46,6 +46,9 @@ malloc_heap_get_stats(struct malloc_heap *heap,void malloc_heap_dump (struct malloc_heap *heap,FILE *f); +int +malloc_socket_to_heap_id (unsigned int socket_id); +int rte_eal_malloc_heap_init (void );
lib/librte_eal/common/rte_malloc.c +29 -12
72 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v2 5/20 flow_classify: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_flow_classify/rte_flow_classify.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_flow_classify/rte_flow_classify.c +1 -2
@@ -247,8 +247,7 @@ rte_flow_classifier_check_params(struct rte_flow_classifier_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_FLOW_CLASSIFY_LOG (ERR ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH v2 4/20 mem: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
This changes the semantics of what we understand by "socket ID", so document the change in the release notes.
--- doc/guides/rel_notes/release_18_11.rst | 7 +++++++ lib/librte_eal/common/eal_common_memzone.c | 8 +++++--- lib/librte_eal/common/malloc_heap.c | 2 +- lib/librte_eal/common/rte_malloc.c | 4 ---- 4 files changed, 13 insertions(+), 8 deletions(-)
doc/guides/rel_notes/release_18_11.rst +7 -0
@@ -75,6 +75,13 @@ API Changes users of memseg-walk-related functions, as they will now have to skip externally allocated segments in most cases if the intent is to only iterate over internal DPDK memory. + - ``socket_id`` parameter across the entire DPDK has gained additional + meaning, as some socket ID's will now be representing externally allocated + memory. No changes will be required for existing code as backwards + compatibility will be kept, and those who do not use this feature will not + see these extra socket ID's. Any new API's must not check socket ID + parameters themselves, and must instead leave it to the memory subsystem to + decide whether socket ID is a valid one. ABI Changes -----------
lib/librte_eal/common/eal_common_memzone.c +5 -3
@@ -120,13 +120,15 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,return NULL ; } -if ((socket_id !=SOCKET_ID_ANY ) && - (socket_id >=RTE_MAX_NUMA_NODES || socket_id <0 )) { +if ((socket_id !=SOCKET_ID_ANY ) && socket_id <0 ) { rte_errno =EINVAL ;return NULL ; } -if (!rte_eal_has_hugepages ()) +/* only set socket to SOCKET_ID_ANY if we aren't allocating for an +* external heap. +*/ +if (!rte_eal_has_hugepages () && socket_id <RTE_MAX_NUMA_NODES ) socket_id =SOCKET_ID_ANY ; contig = (flags &RTE_MEMZONE_IOVA_CONTIG ) !=0 ;
lib/librte_eal/common/malloc_heap.c +1 -1
@@ -649,7 +649,7 @@ malloc_heap_alloc(const char *type, size_t size, int socket_arg,if (size ==0 || (align && !rte_is_power_of_2 (align)))return NULL ; -if (!rte_eal_has_hugepages ()) +if (!rte_eal_has_hugepages () && socket_arg <RTE_MAX_NUMA_NODES ) socket_arg =SOCKET_ID_ANY ;if (socket_arg ==SOCKET_ID_ANY )
lib/librte_eal/common/rte_malloc.c +0 -4
@@ -47,10 +47,6 @@ rte_malloc_socket(const char *type, size_t size, unsigned int align,if (!rte_eal_has_hugepages ()) socket_arg =SOCKET_ID_ANY ; -/* Check socket parameter */ -if (socket_arg >=RTE_MAX_NUMA_NODES ) -return NULL ; -return malloc_heap_alloc (type, size, socket_arg,0 , align ==0 ?1 : align,0 , false); }
Signature · 1 line
2.17.1
PATCH v2 9/20 malloc: add function to query socket ID of named heap
In reply to Anatoly BurakovWhen we will be creating external heaps, they will have their own "fake" socket ID, so add a function that will map the heap name to its socket ID.
--- lib/librte_eal/common/include/rte_malloc.h | 14 ++++++++ lib/librte_eal/common/rte_malloc.c | 37 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 52 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +14 -0
@@ -263,6 +263,20 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Find socket ID corresponding to a named heap. +* +* @param name +* Heap name to find socket ID for +* @return +* Socket ID in case of success (a non-negative number) +* -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``name`` was NULL +* ENOENT - heap identified by the name ``name`` was not found +*/ +int __rte_experimental +rte_malloc_heap_get_socket (const char *name); +/** * Dump statistics. *
lib/librte_eal/common/rte_malloc.c +37 -0
@@ -8,6 +8,7 @@#include <string.h> #include <sys/queue.h> +#include <rte_errno.h> #include <rte_memcpy.h> #include <rte_memory.h> #include <rte_eal.h>
@@ -183,6 +184,42 @@ rte_malloc_dump_heaps(FILE *f)rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); } +int +rte_malloc_heap_get_socket (const char *name) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +unsigned int idx; +int ret; + +if (name ==NULL || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_read_lock (&mcfg->memory_hotplug_lock); +for (idx =0 ; idx <RTE_MAX_HEAPS ; idx++) { +struct malloc_heap *tmp = &mcfg->malloc_heaps[idx]; + +if (!strncmp (name, tmp->name,RTE_HEAP_NAME_MAX_LEN )) { + heap = tmp; +break ; + } + } + +if (heap !=NULL ) { + ret = heap->socket_id; + }else { + rte_errno =ENOENT ; + ret =-1 ; + } +rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +/* * Print stats on memory type. If type is NULL, info on all types is printed */
lib/librte_eal/rte_eal_version.map +1 -0
@@ -311,6 +311,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v2 11/20 malloc: allow destroying heaps
In reply to Anatoly BurakovAdd an API to destroy specified heap.
--- lib/librte_eal/common/include/rte_malloc.h | 23 +++++++++ lib/librte_eal/common/malloc_heap.c | 22 ++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 58 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 107 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +23 -0
@@ -282,6 +282,29 @@ rte_malloc_get_socket_stats(int socket, int __rte_experimentalrte_malloc_heap_create (const char *heap_name); +/** +* Destroys a previously created malloc heap with specified name. +* +* @note This function will return a failure result if not all memory allocated +* from the heap has been freed back to the heap +* +* @note This function will return a failure result if not all memory segments +* were removed from the heap prior to its destruction +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* ENOENT - heap by the name of ``heap_name`` was not found +* EPERM - attempting to destroy reserved heap +* EBUSY - heap still contains data +*/ +int __rte_experimental +rte_malloc_heap_destroy (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +22 -0
@@ -1047,6 +1047,28 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name)return 0 ; } +int +malloc_heap_destroy (struct malloc_heap *heap) +{ +if (heap->alloc_count !=0 ) { +RTE_LOG (ERR ,EAL ,"Heap is still in use\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->first !=NULL || heap->last !=NULL ) { +RTE_LOG (ERR ,EAL ,"Heap still contains memory segments\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->total_size !=0 ) +RTE_LOG (ERR ,EAL ,"Total size not zero, heap is likely corrupt\n" ); + +/* after this, the lock will be dropped */ +memset (heap,0 ,sizeof (*heap)); + +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -36,6 +36,9 @@ malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags,int malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int +malloc_heap_destroy (struct malloc_heap *heap); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +58 -0
67 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -312,6 +312,7 @@ EXPERIMENTAL { rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; rte_malloc_heap_create; + rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v2 13/20 malloc: allow removing memory from named heaps
In reply to Anatoly BurakovAdd an API to remove memory from specified heaps. This will first check if all elements within the region are free, and that the region is the original region that was added to the heap (by comparing its length to length of memory addressed by the underlying memseg list).
--- lib/librte_eal/common/include/rte_malloc.h | 27 +++++++++++ lib/librte_eal/common/malloc_heap.c | 54 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 39 ++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 125 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -302,6 +302,33 @@ int __rte_experimental rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +/** +* Remove memory chunk from heap with specified name. +* +* @note Memory chunk being removed must be the same as one that was added; +* partially removing memory chunks is not supported +* +* @note Memory area must not contain any allocated elements to allow its +* removal from the heap +* +* @param heap_name +* Name of the heap to remove memory from +* @param va_addr +* Virtual address to remove from the heap +* @param len +* Length of virtual area to remove from the heap +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to remove memory from a reserved heap +* ENOENT - heap or memory chunk was not found +* EBUSY - memory chunk still contains data +*/ +int __rte_experimental +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +54 -0
66 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -43,6 +43,10 @@ int malloc_heap_add_external_memory(struct malloc_heap *heap, void *va_addr, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +int +malloc_heap_remove_external_memory (struct malloc_heap *heap,void *va_addr, +size_t len); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +39 -0
@@ -354,6 +354,45 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,return ret; } +int +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +int ret; + +if (heap_name ==NULL || va_addr ==NULL || len ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_write_lock (&mcfg->memory_hotplug_lock); +/* find our heap */ + heap =find_named_heap (heap_name); +if (heap ==NULL ) { + rte_errno =ENOENT ; + ret =-1 ; + goto unlock; + } +if (heap->socket_id <RTE_MAX_NUMA_NODES ) { +/* cannot remove memory from internal heaps */ + rte_errno =EPERM ; + ret =-1 ; + goto unlock; + } + +rte_spinlock_lock (&heap->lock); + ret =malloc_heap_remove_external_memory (heap, va_addr, len); +rte_spinlock_unlock (&heap->lock); + +unlock: +rte_rwlock_write_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -315,6 +315,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v2 2/20 mem: allow memseg lists to be marked as external
In reply to Anatoly BurakovWhen we allocate and use DPDK memory, we need to be able to differentiate between DPDK hugepage segments and segments that were made part of DPDK but are externally allocated. Add such a property to memseg lists.
This breaks the ABI, so bump the EAL library ABI version and document the change in release notes.
All current calls for memseg walk functions were adjusted to ignore external segments where it made sense.
Mempools is a special case, because we may be asked to allocate a mempool on a specific socket, and we need to ignore all page sizes on other heaps or other sockets. Previously, this assumption of knowing all page sizes was not a problem, but it will be now, so we have to match socket ID with page size when calculating minimum page size for a mempool.
---
Notes:
v1:
- Adjust all calls to memseg walk functions to ignore external segments where it made sense to do so
doc/guides/rel_notes/deprecation.rst | 15 --------- doc/guides/rel_notes/release_18_11.rst | 12 ++++++- drivers/bus/fslmc/fslmc_vfio.c | 7 +++-- drivers/net/mlx4/mlx4_mr.c | 3 ++ drivers/net/mlx5/mlx5.c | 5 ++- drivers/net/mlx5/mlx5_mr.c | 3 ++ drivers/net/virtio/virtio_user/vhost_kernel.c | 5 ++- lib/librte_eal/bsdapp/eal/Makefile | 2 +- lib/librte_eal/bsdapp/eal/eal.c | 3 ++ lib/librte_eal/bsdapp/eal/eal_memory.c | 7 +++-- lib/librte_eal/common/eal_common_memory.c | 4 +++ .../common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/common/include/rte_memory.h | 9 ++++++ lib/librte_eal/common/malloc_heap.c | 9 ++++-- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 10 +++++- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 9 ++++++ lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 +++++++--- lib/librte_eal/meson.build | 2 +- lib/librte_mempool/rte_mempool.c | 31 ++++++++++++++----- test/test/test_malloc.c | 3 ++ test/test/test_memzone.c | 3 ++ 22 files changed, 122 insertions(+), 40 deletions(-)
doc/guides/rel_notes/deprecation.rst +0 -15
@@ -11,21 +11,6 @@ API and ABI deprecation notices are to be posted here. Deprecation Notices ------------------- -* eal: certain structures will change in EAL on account of upcoming external - memory support. Aside from internal changes leading to an ABI break, the - following externally visible changes will also be implemented: - - - ``rte_memseg_list`` will change to include a boolean flag indicating - whether a particular memseg list is externally allocated. This will have - implications for any users of memseg-walk-related functions, as they will - now have to skip externally allocated segments in most cases if the intent - is to only iterate over internal DPDK memory. - - ``socket_id`` parameter across the entire DPDK will gain additional meaning, - as some socket ID's will now be representing externally allocated memory. No - changes will be required for existing code as backwards compatibility will - be kept, and those who do not use this feature will not see these extra - socket ID's. - * eal: both declaring and identifying devices will be streamlined in v18.11. New functions will appear to query a specific port from buses, classes of device and device drivers. Device declaration will be made coherent with the
doc/guides/rel_notes/release_18_11.rst +11 -1
@@ -68,6 +68,13 @@ API Changes Also, make sure to start the actual text at the margin. ========================================================= +* eal: The following API changes were made in 18.11: + + - ``rte_memseg_list`` structure now has an additional flag indicating whether + the memseg list is externally allocated. This will have implications for any + users of memseg-walk-related functions, as they will now have to skip + externally allocated segments in most cases if the intent is to only iterate + over internal DPDK memory. ABI Changes -----------
@@ -84,6 +91,9 @@ ABI Changes ========================================================= +* eal: EAL library ABI version was changed due to previously announced work on + supporting external memory in DPDK. + Removed Items -------------
@@ -129,7 +139,7 @@ The libraries prepended with a plus sign were incremented in this version. librte_compressdev.so.1 librte_cryptodev.so.5 librte_distributor.so.1 - librte_eal.so.8 + + librte_eal.so.9 librte_ethdev.so.10 librte_eventdev.so.4 librte_flow_classify.so.1
drivers/bus/fslmc/fslmc_vfio.c +5 -2
@@ -317,12 +317,15 @@ fslmc_unmap_dma(uint64_t vaddr, uint64_t iovaddr __rte_unused, size_t len) }static int -fslmc_dmamap_seg(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +fslmc_dmamap_seg (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *n_segs = arg;int ret; +if (msl->external) +return 0 ; + ret =fslmc_map_dma (ms->addr_64, ms->iova, ms->len);if (ret) DPAA2_BUS_ERR("Unable to VFIO map (addr=%p, len=%zu)",
drivers/net/mlx4/mlx4_mr.c +3 -0
@@ -496,6 +496,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/mlx5/mlx5.c +4 -1
@@ -568,11 +568,14 @@ static struct rte_pci_driver mlx5_driver;static void *uar_base;static int -find_lower_va_bound(const struct rte_memseg_list *msl __rte_unused, +find_lower_va_bound (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {void **addr = arg; +if (msl->external) +return 0 ; +if (*addr ==NULL ) *addr = ms->addr;else
drivers/net/mlx5/mlx5_mr.c +3 -0
@@ -486,6 +486,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/virtio/virtio_user/vhost_kernel.c +4 -1
@@ -75,13 +75,16 @@ struct walk_arg {uint32_t region_nr; };static int -add_memory_region(const struct rte_memseg_list *msl __rte_unused, +add_memory_region (const struct rte_memseg_list *msl,const struct rte_memseg *ms,size_t len,void *arg) {struct walk_arg *wa = arg;struct vhost_memory_region *mr;void *start_addr; +if (msl->external) +return 0 ; +if (wa->region_nr >= max_regions)return -1 ;
lib/librte_eal/bsdapp/eal/Makefile +1 -1
@@ -22,7 +22,7 @@ LDLIBS += -lrte_kvargs EXPORT_MAP := ../../rte_eal_version.map -LIBABIVER := 8 +LIBABIVER := 9 # specific to bsdapp exec-env SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
lib/librte_eal/bsdapp/eal/eal.c +3 -0
@@ -502,6 +502,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +if (msl->socket_id == *socket_id && msl->memseg_arr.count !=0 )return 1 ;
lib/librte_eal/bsdapp/eal/eal_memory.c +5 -2
@@ -236,12 +236,15 @@ struct attach_walk_args {int seg_idx; };static int -attach_segment(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +attach_segment (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {struct attach_walk_args *wa = arg;void *addr; +if (msl->external) +return 0 ; + addr =mmap (ms->addr, ms->len,PROT_READ |PROT_WRITE ,MAP_SHARED |MAP_FIXED , wa->fd_hugepage, wa->seg_idx *EAL_PAGE_SIZE );
lib/librte_eal/common/eal_common_memory.c +4 -0
@@ -272,6 +272,9 @@ physmem_size(const struct rte_memseg_list *msl, void *arg) {uint64_t *total_len = arg; +if (msl->external) +return 0 ; + *total_len += msl->memseg_arr.count * msl->page_sz;return 0 ;
@@ -547,6 +550,7 @@ rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)return ret; } +/* init memory subsystem */ int rte_eal_memory_init (void )
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -33,6 +33,7 @@ struct rte_memseg_list {size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ +unsigned int external;/**< 1 if this list points to external memory */ volatile uint32_t version;/**< version number for multiprocess sync. */ struct rte_fbarray memseg_arr; };
lib/librte_eal/common/include/rte_memory.h +9 -0
@@ -215,6 +215,9 @@ typedef int (*rte_memseg_list_walk_t)(const struct rte_memseg_list *msl,* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -233,6 +236,9 @@ rte_memseg_walk(rte_memseg_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -251,6 +257,9 @@ rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
lib/librte_eal/common/malloc_heap.c +7 -2
@@ -95,6 +95,9 @@ malloc_add_seg(const struct rte_memseg_list *msl,struct malloc_heap *heap;int msl_idx; +if (msl->external) +return 0 ; + heap = &mcfg->malloc_heaps[msl->socket_id];/* msl is const, so find it */
@@ -756,8 +759,10 @@ malloc_heap_free(struct malloc_elem *elem)/* anything after this is a bonus */ ret =0 ; -/* ...of which we can't avail if we are in legacy mode */ -if (internal_config.legacy_mem) +/* ...of which we can't avail if we are in legacy mode, or if this is an +* externally allocated segment. +*/ +if (internal_config.legacy_mem || msl->external) goto free_unlock;/* check if we can free any memory back to the system */
lib/librte_eal/linuxapp/eal/Makefile +1 -1
@@ -10,7 +10,7 @@ ARCH_DIR ?= $(RTE_ARCH) EXPORT_MAP := ../../rte_eal_version.map VPATH += $(RTE_SDK)/lib/librte_eal/common/arch/$(ARCH_DIR) -LIBABIVER := 8 +LIBABIVER := 9 VPATH += $(RTE_SDK)/lib/librte_eal/common
lib/librte_eal/linuxapp/eal/eal.c +9 -1
@@ -725,6 +725,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +return *socket_id == msl->socket_id; }
@@ -1059,7 +1062,12 @@ mark_freeable(const struct rte_memseg_list *msl, const struct rte_memseg *ms, void *arg __rte_unused) {/* ms is const, so find this memseg */ -struct rte_memseg *found =rte_mem_virt2memseg (ms->addr, msl); +struct rte_memseg *found; + +if (msl->external) +return 0 ; + + found =rte_mem_virt2memseg (ms->addr, msl); found->flags &= ~RTE_MEMSEG_FLAG_DO_NOT_FREE ;
lib/librte_eal/linuxapp/eal/eal_memalloc.c +9 -0
@@ -1250,6 +1250,9 @@ sync_walk(const struct rte_memseg_list *msl, void *arg __rte_unused)unsigned int i;int msl_idx; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1298,6 +1301,9 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,char name[PATH_MAX ];int msl_idx, ret; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1328,6 +1334,9 @@ secondary_lock_list_create_walk(const struct rte_memseg_list *msl,int msl_idx;int *data; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; len = msl->memseg_arr.len;
lib/librte_eal/linuxapp/eal/eal_vfio.c +13 -4
@@ -1082,11 +1082,14 @@ rte_vfio_get_group_num(const char *sysfs_base, }static int -type1_map(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +type1_map (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_type1_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1196,11 +1199,14 @@ vfio_spapr_dma_do_map(int vfio_container_fd, uint64_t vaddr, uint64_t iova, }static int -vfio_spapr_map_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_map_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_spapr_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1210,12 +1216,15 @@ struct spapr_walk_param {uint64_t hugepage_sz; };static int -vfio_spapr_window_size_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_window_size_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {struct spapr_walk_param *param = arg;uint64_t max = ms->iova + ms->len; +if (msl->external) +return 0 ; +if (max > param->window_size) { param->hugepage_sz = ms->hugepage_sz; param->window_size = max;
lib/librte_eal/meson.build +1 -1
@@ -21,7 +21,7 @@ else error('unsupported system type "@0@"'.format(host_machine.system())) endif -version = 8 # the version of the EAL API +version = 9 # the version of the EAL API allow_experimental_apis = true deps += 'compat' deps += 'kvargs'
lib/librte_mempool/rte_mempool.c +23 -8
@@ -99,25 +99,40 @@ static unsigned optimize_object_size(unsigned obj_size)return new_obj_size *RTE_MEMPOOL_ALIGN ; } +struct pagesz_walk_arg { +int socket_id; +size_t min; +}; +static int find_min_pagesz (const struct rte_memseg_list *msl,void *arg) { -size_t *min = arg; +struct pagesz_walk_arg *wa = arg; +bool valid; -if (msl->page_sz < *min) - *min = msl->page_sz; + valid = msl->socket_id == wa->socket_id; + valid |= wa->socket_id ==SOCKET_ID_ANY && msl->external ==0 ; + +if (!valid) +return 0 ; + +if (msl->page_sz < wa->min) + wa->min = msl->page_sz;return 0 ; }static size_t -get_min_page_size (void ) +get_min_page_size (int socket_id) { -size_t min_pagesz =SIZE_MAX ; +struct pagesz_walk_arg wa; -rte_memseg_list_walk (find_min_pagesz, &min_pagesz); + wa.min =SIZE_MAX ; + wa.socket_id = socket_id; -return min_pagesz ==SIZE_MAX ? (size_t )getpagesize () : min_pagesz; +rte_memseg_list_walk (find_min_pagesz, &wa); + +return wa.min ==SIZE_MAX ? (size_t )getpagesize () : wa.min; }
@@ -470,7 +485,7 @@ rte_mempool_populate_default(struct rte_mempool *mp) pg_sz =0 ; pg_shift =0 ; }else if (try_contig) { - pg_sz =get_min_page_size (); + pg_sz =get_min_page_size (mp->socket_id); pg_shift =rte_bsf32 (pg_sz); }else { pg_sz =getpagesize ();
test/test/test_malloc.c +3 -0
@@ -711,6 +711,9 @@ check_socket_mem(const struct rte_memseg_list *msl, void *arg) {int32_t *socket = arg; +if (msl->external) +return 0 ; +return *socket == msl->socket_id; }
test/test/test_memzone.c +3 -0
@@ -115,6 +115,9 @@ find_available_pagesz(const struct rte_memseg_list *msl, void *arg) {struct walk_arg *wa = arg; +if (msl->external) +return 0 ; +if (msl->page_sz ==RTE_PGSIZE_2M ) wa->hugepage_2MB_avail =1 ;if (msl->page_sz ==RTE_PGSIZE_1G )
Signature · 1 line
2.17.1
PATCH v2 7/20 sched: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_sched/rte_sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/librte_sched/rte_sched.c +1 -1
@@ -329,7 +329,7 @@ rte_sched_port_check_params(struct rte_sched_port_params *params)return -1 ;/* socket */ -if ((params->socket <0 ) || (params->socket >=RTE_MAX_NUMA_NODES )) +if (params->socket <0 )return -3 ;/* rate */
Signature · 1 line
2.17.1
PATCH v2 12/20 malloc: allow adding memory to named heaps
In reply to Anatoly BurakovAdd an API to add externally allocated memory to malloc heap. The memory will be stored in memseg lists like regular DPDK memory. Multiple segments are allowed within a heap. If IOVA table is not provided, IOVA addresses are filled in with RTE_BAD_IOVA.
--- lib/librte_eal/common/include/rte_malloc.h | 39 ++++++++++++ lib/librte_eal/common/malloc_heap.c | 74 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 51 +++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 169 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +39 -0
@@ -263,6 +263,45 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Add memory chunk to a heap with specified name. +* +* @note Multiple memory chunks can be added to the same heap +* +* @note Memory must be previously allocated for DPDK to be able to use it as a +* malloc heap. Failing to do so will result in undefined behavior, up to and +* including segmentation faults. +* +* @note Calling this function will erase any contents already present at the +* supplied memory address. +* +* @param heap_name +* Name of the heap to add memory chunk to +* @param va_addr +* Start of virtual area to add to the heap +* @param len +* Length of virtual area to add to the heap +* @param iova_addrs +* Array of page IOVA addresses corresponding to each page in this memory +* area. Can be NULL, in which case page IOVA addresses will be set to +* RTE_BAD_IOVA. +* @param n_pages +* Number of elements in the iova_addrs array. Ignored if ``iova_addrs`` +* is NULL. +* @param page_sz +* Page size of the underlying memory +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to add memory to a reserved heap +* ENOSPC - no more space in internal config to store a new memory chunk +*/ +int __rte_experimental +rte_malloc_heap_memory_add (const char *heap_name,void *va_addr,size_t len, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +74 -0
80 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -39,6 +39,10 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name);int malloc_heap_destroy (struct malloc_heap *heap); +int +malloc_heap_add_external_memory (struct malloc_heap *heap,void *va_addr, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +51 -0
57 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -314,6 +314,7 @@ EXPERIMENTAL { rte_malloc_heap_create; rte_malloc_heap_destroy; rte_malloc_heap_get_socket; + rte_malloc_heap_memory_add; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v2 14/20 malloc: allow attaching to external memory chunks
In reply to Anatoly BurakovIn order to use external memory in multiple processes, we need to attach to primary process's memseg lists, so add a new API to do that. It is the responsibility of the user to ensure that memory is accessible and that it has been previously added to the malloc heap by another process.
--- lib/librte_eal/common/include/rte_malloc.h | 48 +++++++++-- lib/librte_eal/common/rte_malloc.c | 93 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 135 insertions(+), 7 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +41 -7
@@ -268,6 +268,10 @@ rte_malloc_get_socket_stats(int socket,* * @note Multiple memory chunks can be added to the same heap * +* @note Before accessing this memory in other processes, it needs to be +* attached in each of those processes by calling +* ``rte_malloc_heap_memory_attach`` in each other process. +* * @note Memory must be previously allocated for DPDK to be able to use it as a * malloc heap. Failing to do so will result in undefined behavior, up to and * including segmentation faults.
@@ -329,21 +333,48 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, int __rte_experimentalrte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** +* Attach to an already existing chunk of external memory in another process. +* +* @note This function must be called before any attempt is made to use an +* already existing external memory chunk. This function does *not* need to +* be called if a call to ``rte_malloc_heap_memory_add`` was made in the +* current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful attach +* -1 on unsuccessful attach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to attach memory to a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. * * @note Heaps created via this call will automatically get assigned a unique * socket ID, which can be found using ``rte_malloc_heap_get_socket()`` * +* @note This function can only be called in primary process. +* * @param heap_name * Name of the heap to create. * * @return * - 0 on successful creation * - -1 in case of error, with rte_errno set to one of the following: -* EINVAL - ``heap_name`` was NULL, empty or too long -* EEXIST - heap by name of ``heap_name`` already exists -* ENOSPC - no more space in internal config to store a new heap +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +* E_RTE_SECONDARY - attempted to create a heap in secondary process */ int __rte_experimentalrte_malloc_heap_create (const char *heap_name);
@@ -357,16 +388,19 @@ rte_malloc_heap_create(const char *heap_name);* @note This function will return a failure result if not all memory segments * were removed from the heap prior to its destruction * +* @note This function can only be called in primary process. +* * @param heap_name * Name of the heap to create. * * @return * - 0 on success * - -1 in case of error, with rte_errno set to one of the following: -* EINVAL - ``heap_name`` was NULL, empty or too long -* ENOENT - heap by the name of ``heap_name`` was not found -* EPERM - attempting to destroy reserved heap -* EBUSY - heap still contains data +* EINVAL - ``heap_name`` was NULL, empty or too long +* ENOENT - heap by the name of ``heap_name`` was not found +* EPERM - attempting to destroy reserved heap +* EBUSY - heap still contains data +* E_RTE_SECONDARY - attempted to destroy a heap in secondary process */ int __rte_experimentalrte_malloc_heap_destroy (const char *heap_name);
lib/librte_eal/common/rte_malloc.c +93 -0
111 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -315,6 +315,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_attach; rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v2 18/20 doc: add external memory feature to the release notes
In reply to Anatoly BurakovDocument the addition of external memory support to DPDK.
--- doc/guides/rel_notes/release_18_11.rst | 5 +++++ 1 file changed, 5 insertions(+)
doc/guides/rel_notes/release_18_11.rst +5 -0
@@ -54,6 +54,11 @@ New Features Also, make sure to start the actual text at the margin. ========================================================= +* **Added support for using externally allocated memory in DPDK.** + + DPDK has gained support for creating new ``rte_malloc`` heaps referencing + memory that was created outside of DPDK's own page allocator, and using that + memory natively with any other DPDK library or data structure. API Changes -----------
Signature · 1 line
2.17.1
PATCH v2 19/20 doc: add external memory feature to programmer's guide
In reply to Anatoly BurakovAdd a short chapter on usage of external memory in DPDK to the Programmer's Guide.
--- .../prog_guide/env_abstraction_layer.rst | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+)
doc/guides/prog_guide/env_abstraction_layer.rst +38 -0
@@ -213,6 +213,44 @@ Normally, these options do not need to be changed. can later be mapped into that preallocated VA space (if dynamic memory mode is enabled), and can optionally be mapped into it at startup. +Support for Externally Allocated Memory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to use externally allocated memory in DPDK, using a set of malloc +heap API's. Support for externally allocated memory is implemented through +overloading the socket ID - externally allocated heaps will have socket ID's +that would be considered invalid under normal circumstances. Requesting an +allocation to take place from a specified externally allocated memory is a +matter of supplying the correct socket ID to DPDK allocator, either directly +(e.g. through a call to ``rte_malloc``) or indirectly (through data +structure-specific allocation API's such as ``rte_ring_create``). + +Since there is no way DPDK can verify whether memory are is available or valid, +this responsibility falls on the shoulders of the user. All multiprocess +synchronization is also user's responsibility, as well as ensuring that all +calls to add/attach/detach/remove memory are done in the correct order. It is +not required to attach to a memory area in all processes - only attach to memory +areas as needed. + +The expected workflow is as follows: + +* Get a pointer to memory area +* Create a named heap +* Add memory area(s) to the heap + * If IOVA table is not specified, IOVA addresses will be assumed to be + unavailable + * Any DMA mappings for the external area are responsibility of the user + * Other processes must attach to the memory area before they can use it +* Get socket ID used for the heap +* Use normal DPDK allocation procedures, using supplied socket ID +* If memory area is no longer needed, it can be removed from the heap + * Other processes must detach from this memory area before it can be removed +* If heap is no longer needed, remove it + * Socket ID will become invalid and will not be reused + +For more information, please refer to ``rte_malloc`` API documentation, +specifically the ``rte_malloc_heap_*`` family of function calls. + PCI Access ~~~~~~~~~~
Signature · 1 line
2.17.1
PATCH v2 10/20 malloc: allow creating malloc heaps
In reply to Anatoly BurakovAdd API to allow creating new malloc heaps. They will be created with socket ID's going above RTE_MAX_NUMA_NODES, to avoid clashing with internal heaps.
--- lib/librte_eal/common/include/rte_malloc.h | 19 ++++++++ lib/librte_eal/common/malloc_heap.c | 30 +++++++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 52 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 105 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +19 -0
@@ -263,6 +263,25 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Creates a new empty malloc heap with a specified name. +* +* @note Heaps created via this call will automatically get assigned a unique +* socket ID, which can be found using ``rte_malloc_heap_get_socket()`` +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on successful creation +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +*/ +int __rte_experimental +rte_malloc_heap_create (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +30 -0
@@ -29,6 +29,10 @@#include "malloc_heap.h" #include "malloc_mp.h" +/* start external socket ID's at a very high number */ +#define CONST_MAX (a, b) (a > b ? a : b)/* RTE_MAX is not a constant */ +#define EXTERNAL_HEAP_MIN_SOCKET_ID (CONST_MAX((1 << 8), RTE_MAX_NUMA_NODES)) +static unsigned check_hugepage_sz (unsigned flags,uint64_t hugepage_sz) {
@@ -1017,6 +1021,32 @@ malloc_heap_dump(struct malloc_heap *heap, FILE *f)rte_spinlock_unlock (&heap->lock); } +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name) +{ +static uint32_t next_socket_id =EXTERNAL_HEAP_MIN_SOCKET_ID ; + +/* prevent overflow. did you really create 2 billion heaps??? */ +if (next_socket_id >INT32_MAX ) { +RTE_LOG (ERR ,EAL ,"Cannot assign new socket ID's\n" ); + rte_errno =ENOSPC ; +return -1 ; + } + +/* initialize empty heap */ + heap->alloc_count =0 ; + heap->first =NULL ; + heap->last =NULL ; +LIST_INIT (heap->free_head); +rte_spinlock_init (&heap->lock); + heap->total_size =0 ; + heap->socket_id = next_socket_id++; + +/* set up name */ +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -33,6 +33,9 @@ void * malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags, size_t align, bool contig); +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +52 -0
61 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -311,6 +311,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_create; rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v2 17/20 examples: add external memory example app
In reply to Anatoly BurakovIntroduce an example application demonstrating the use of external memory support. This is a simple application based on skeleton app, but instead of using internal DPDK memory, it is using externally allocated memory.
The RX/TX and init path is a carbon-copy of skeleton app, with no modifications whatseoever. The only difference is an additional init stage to allocate memory and create a heap for it, and the socket ID supplied to the mempool initialization function. The memory used by this app is hugepage memory allocated anonymously.
Anonymous hugepage memory will not be allocated in a NUMA-aware fashion, so there is a chance of performance degradation when using this app, but given that kernel usually gives hugepages on local socket first, this should not be a problem in most cases.
--- examples/external_mem/Makefile | 62 ++++ examples/external_mem/extmem.c | 461 ++++++++++++++++++++++++++++++ examples/external_mem/meson.build | 12 + 3 files changed, 535 insertions(+) create mode 100644 examples/external_mem/Makefile create mode 100644 examples/external_mem/extmem.c create mode 100644 examples/external_mem/meson.build
examples/external_mem/Makefile new file +62 -0
@@ -0,0 +1,62 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2018 Intel Corporation + +# binary name +APP = extmem + +# all source are stored in SRCS-y +SRCS-y := extmem.c + +# Build using pkg-config variables if possible +$(shell pkg-config --exists libdpdk) +ifeq ($(.SHELLSTATUS),0) + +all: shared +.PHONY: shared static +shared: build/$(APP)-shared + ln -sf $(APP)-shared build/$(APP) +static: build/$(APP)-static + ln -sf $(APP)-static build/$(APP) + +PC_FILE := $(shell pkg-config --path libdpdk) +CFLAGS += -O3 $(shell pkg-config --cflags libdpdk) +CFLAGS += -DALLOW_EXPERIMENTAL_API +LDFLAGS_SHARED = $(shell pkg-config --libs libdpdk) +LDFLAGS_STATIC = -Wl,-Bstatic $(shell pkg-config --static --libs libdpdk) + +build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) + +build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) + +build: + @mkdir -p $@ + +.PHONY: clean +clean: + rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared + rmdir --ignore-fail-on-non-empty build + +else # Build using legacy build system + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +CFLAGS += $(WERROR_FLAGS) +CFLAGS += -DALLOW_EXPERIMENTAL_API + +# workaround for a gcc bug with noreturn attribute +# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603 +ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y) +CFLAGS_main.o += -Wno-return-type +endif + +include $(RTE_SDK)/mk/rte.extapp.mk +endif
examples/external_mem/extmem.c new file +461 -0
461 lines not on this page · raw message
examples/external_mem/meson.build new file +12 -0
@@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +# meson file, for building this example as part of a main DPDK build. +# +# To build this example as a standalone application with an already-installed +# DPDK instance, use 'make' + +allow_experimental_apis = true +sources = files( + 'extmem.c' +)
Signature · 1 line
2.17.1
PATCH v2 6/20 pipeline: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_pipeline/rte_pipeline.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_pipeline/rte_pipeline.c +1 -2
@@ -178,8 +178,7 @@ rte_pipeline_check_params(struct rte_pipeline_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_LOG (ERR ,PIPELINE ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH v2 16/20 test: add unit tests for external memory support
In reply to Anatoly BurakovAdd simple unit tests to test external memory support. The tests are pretty basic and mostly consist of checking if invalid API calls are handled correctly, plus a simple allocation/deallocation test for malloc and memzone.
--- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 ++++++++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 test/test/test_external_mem.c
test/test/Makefile +1 -0
@@ -71,6 +71,7 @@ SRCS-y += test_bitmap.c SRCS-y += test_reciprocal_division.c SRCS-y += test_reciprocal_division_perf.c SRCS-y += test_fbarray.c +SRCS-y += test_external_mem.c SRCS-y += test_ring.c SRCS-y += test_ring_perf.c
test/test/autotest_data.py +10 -4
@@ -477,10 +477,16 @@ "Report": None, }, { - "Name": "Fbarray autotest", - "Command": "fbarray_autotest", - "Func": default_autotest, - "Report": None, + "Name": "Fbarray autotest", + "Command": "fbarray_autotest", + "Func": default_autotest, + "Report": None, + }, + { + "Name": "External memory autotest", + "Command": "external_mem_autotest", + "Func": default_autotest, + "Report": None, }, # #Please always keep all dump tests at the end and together!
test/test/meson.build +1 -0
@@ -155,6 +155,7 @@ test_names = [ 'eventdev_common_autotest', 'eventdev_octeontx_autotest', 'eventdev_sw_autotest', + 'external_mem_autotest', 'func_reentrancy_autotest', 'flow_classify_autotest', 'hash_scaling_autotest',
test/test/test_external_mem.c new file +389 -0
389 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v2 8/20 malloc: add name to malloc heaps
In reply to Anatoly BurakovWe will need to refer to external heaps in some way. While we use heap ID's internally, for external API use it has to be something more user-friendly. So, we will be using a string to uniquely identify a heap.
--- lib/librte_eal/common/include/rte_malloc_heap.h | 2 ++ lib/librte_eal/common/malloc_heap.c | 15 ++++++++++++++- lib/librte_eal/common/rte_malloc.c | 1 + 3 files changed, 17 insertions(+), 1 deletion(-)
lib/librte_eal/common/include/rte_malloc_heap.h +2 -0
@@ -12,6 +12,7 @@/* Number of free lists per heap, grouped by size. */ #define RTE_HEAP_NUM_FREELISTS 13 +#define RTE_HEAP_NAME_MAX_LEN 32/* dummy definition, for pointers */ struct malloc_elem ;
@@ -28,6 +29,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size;unsigned int socket_id; + char name[RTE_HEAP_NAME_MAX_LEN]; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +14 -1
@@ -127,7 +127,6 @@ malloc_add_seg(const struct rte_memseg_list *msl,malloc_heap_add_memory (heap, found_msl, ms->addr, len); heap->total_size += len; - heap->socket_id = msl->socket_id;RTE_LOG (DEBUG ,EAL ,"Added %zuM to heap on socket %i\n" , len >>20 , msl->socket_id);
@@ -1022,6 +1021,20 @@ intrte_eal_malloc_heap_init (void) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +unsigned int i; + +/* assign names to default DPDK heaps */ +for (i =0 ; i <rte_socket_count (); i++) { +struct malloc_heap *heap = &mcfg->malloc_heaps[i]; +char heap_name[RTE_HEAP_NAME_MAX_LEN ]; +int socket_id =rte_socket_id_by_idx (i); + +snprintf (heap_name,sizeof (heap_name) -1 , +"socket_%i" , socket_id); +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); + heap->socket_id = socket_id; + } +if (register_mp_requests ()) {RTE_LOG (ERR ,EAL ,"Couldn't register malloc multiprocess actions\n" );
lib/librte_eal/common/rte_malloc.c +1 -0
@@ -202,6 +202,7 @@ rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)malloc_heap_get_stats (heap, &sock_stats);fprintf (f,"Heap id:%u\n" , heap_id); +fprintf (f,"\tHeap name:%s\n" , heap->name);fprintf (f,"\tHeap_size:%zu,\n" , sock_stats.heap_totalsz_bytes);fprintf (f,"\tFree_size:%zu,\n" , sock_stats.heap_freesz_bytes);fprintf (f,"\tAlloc_size:%zu,\n" , sock_stats.heap_allocsz_bytes);
Signature · 1 line
2.17.1
PATCH v2 20/20 doc: add external memory sample application guide
In reply to Anatoly BurakovAdd a guide for external memory sample application. The application is identical to Basic Forwarding example in everything except parts of initialization code, so the bits that are identical will not be described.
It is also not necessary to describe how external memory is being allocated due to the expectation being that user will have their own mechanisms to allocate memory outside of DPDK, and will only be interested in how to integrate said memory into DPDK.
--- doc/guides/sample_app_ug/external_mem.rst | 115 ++++++++++++++++++++++ doc/guides/sample_app_ug/index.rst | 1 + 2 files changed, 116 insertions(+) create mode 100644 doc/guides/sample_app_ug/external_mem.rst
doc/guides/sample_app_ug/external_mem.rst new file +115 -0
@@ -0,0 +1,115 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2015-2018 Intel Corporation. + +External Memory Sample Application +================================== + +The External Memory sample application is a simple *skeleton* example of a +forwarding application using externally allocated memory. + +It is intended as a demonstration of the basic workflow of using externally +allocated memory in DPDK. This application is based on Basic Forwarding sample +application, and differs only in its initialization path. For more detailed +explanation of port initialization and packet forwarding code, please refer to +*Basic Forwarding sample application user guide*. + +Compiling the Application +------------------------- + +To compile the sample application see :doc:`compiling`. + +The application is located in the ``external_mem`` sub-directory. + +Running the Application +----------------------- + +To run the example in a ``linuxapp`` environment: + +.. code-block:: console + + ./build/extmem -l 1 -n 4 + +Refer to *DPDK Getting Started Guide* for general information on running +applications and the Environment Abstraction Layer (EAL) options. + + +Explanation +----------- + +For general overview of the code and explanation of the main components of this +application, please refer to *Basic Forwarding sample application user guide*. +This guide will only explain sections of the code relevant to using external +memory in DPDK. + +All DPDK library functions used in the sample code are prefixed with ``rte_`` +and are explained in detail in the *DPDK API Documentation*. + + +External Memory Initialization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``main()`` function performs the initialization and calls the execution +threads for each lcore. + +After initializing the Environment Abstraction Layer, the application also +initializes external memory (in this case, it's allocating a chunk of memory +using anonymous hugepages) inside the ``setup_extmem()`` local function. + +The first step in this process is to create an external heap: + +.. code-block:: c + + ret = rte_malloc_heap_create(EXTMEM_HEAP_NAME); + if (ret < 0) { + printf("Cannot create heap\n"); + return -1; + } + +Once the heap is created, ``create_extmem`` function is called to create the +actual external memory area the application will be using. While the details of +that process will not be described as they are not pertinent to the external +memory API (it is expected that the user will have their own procedures to +create external memory), there are a few important things to note. + +In order to add an externally allocated memory area to the newly created heap, +the application needs the following pieces of information: + +* Pointer to start address of external memory area +* Length of this area +* Page size of memory backing this memory area +* Optionally, a per-page IOVA table + +All of this information is to be provided by the user. Additionally, if VFIO is +in use and if application intends to do DMA using the memory area, VFIO DMA +mapping must also be performed using ``rte_vfio_dma_map`` function. + +Once the external memory is created and mapped for DMA, the application also has +to add this memory to the heap that was created earlier: + +.. code-block:: c + + ret = rte_malloc_heap_memory_add(EXTMEM_HEAP_NAME, + param.addr, param.len, param.iova_table, + param.iova_table_len, param.pgsz); + +If return value indicates success, the memory area has been successfully added +to the heap. The next step is to retrieve the socket ID of this heap: + +.. code-block:: c + + socket_id = rte_malloc_heap_get_socket(EXTMEM_HEAP_NAME); + if (socket_id < 0) + rte_exit(EXIT_FAILURE, "Invalid socket for external heap\n"); + +After that, the socket ID has to be supplied to the mempool creation function: + +.. code-block:: c + + mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", + nb_mbufs_per_port * nb_ports, MBUF_CACHE_SIZE, 0, + mbuf_sz, socket_id); + +The rest of the application is identical to the Basic Forwarding example. + +The forwarding loop can be interrupted and the application closed using +``Ctrl-C``.
doc/guides/sample_app_ug/index.rst +1 -0
@@ -15,6 +15,7 @@ Sample Applications User Guides exception_path hello_world skeleton + external_mem rxtx_callbacks flow_classify flow_filtering
Signature · 1 line
2.17.1
PATCH v2 15/20 malloc: allow detaching from external memory
In reply to Anatoly BurakovAdd API to detach from existing chunk of external memory in a process.
--- lib/librte_eal/common/include/rte_malloc.h | 27 ++++++++++++++++++++++ lib/librte_eal/common/rte_malloc.c | 27 ++++++++++++++++++---- lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 50 insertions(+), 5 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -315,6 +315,9 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,* @note Memory area must not contain any allocated elements to allow its * removal from the heap * +* @note All other processes must detach from the memory chunk prior to it being +* removed from the heap. +* * @param heap_name * Name of the heap to remove memory from * @param va_addr
@@ -357,6 +360,30 @@ rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len); int __rte_experimentalrte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** +* Detach from a chunk of external memory in secondary process. +* +* @note This function must be called in before any attempt is made to remove +* external memory from the heap in another process. This function does *not* +* need to be called if a call to ``rte_malloc_heap_memory_remove`` will be +* called in current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful detach +* -1 on unsuccessful detach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to detach memory from a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/rte_malloc.c +22 -5
@@ -397,10 +397,11 @@ struct sync_mem_walk_arg {void *va_addr;size_t len;int result; +bool attach; };static int -attach_mem_walk (const struct rte_memseg_list *msl,void *arg) +sync_mem_walk (const struct rte_memseg_list *msl,void *arg) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct sync_mem_walk_arg *wa = arg;
@@ -415,7 +416,10 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg) msl_idx = msl - mcfg->memsegs; found_msl = &mcfg->memsegs[msl_idx]; - ret =rte_fbarray_attach (&found_msl->memseg_arr); +if (wa->attach) + ret =rte_fbarray_attach (&found_msl->memseg_arr); +else + ret =rte_fbarray_detach (&found_msl->memseg_arr);if (ret <0 ) wa->result = -rte_errno;
@@ -426,8 +430,8 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg)return 0 ; } -int -rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +static int +sync_memory (const char *heap_name,void *va_addr,size_t len,bool attach) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct malloc_heap *heap =NULL ;
@@ -461,9 +465,10 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len) wa.va_addr = va_addr; wa.len = len; wa.result = -ENOENT ;/* fail unless explicitly told to succeed */ + wa.attach = attach;/* we're already holding a read lock */ -rte_memseg_list_walk_thread_unsafe (attach_mem_walk, &wa); +rte_memseg_list_walk_thread_unsafe (sync_mem_walk, &wa);if (wa.result <0 ) { rte_errno = -wa.result;
@@ -476,6 +481,18 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)return ret; } +int +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, true); +} + +int +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, false); +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -316,6 +316,7 @@ EXPERIMENTAL { rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; rte_malloc_heap_memory_attach; + rte_malloc_heap_memory_detach; rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
Re: [PATCH v2 02/20] mem: allow memseg lists to be marked as external
In reply to Anatoly BurakovQuoted 11 lines · Anatoly Burakov
When we allocate and use DPDK memory, we need to be able to differentiate between DPDK hugepage segments and segments that were made part of DPDK but are externally allocated. Add such a property to memseg lists.
This breaks the ABI, so bump the EAL library ABI version and document the change in release notes.
All current calls for memseg walk functions were adjusted to ignore external segments where it made sense.
Quoted 8 lines
Mempools is a special case, because we may be asked to allocate a mempool on a specific socket, and we need to ignore all page sizes on other heaps or other sockets. Previously, this assumption of knowing all page sizes was not a problem, but it will be now, so we have to match socket ID with page size when calculating minimum page size for a mempool.
Signed-off-by Anatoly Burakov <[email protected]>
A couple of minor questions/suggestions below, but it is OK to go as is even if rejected.
<...>
Quoted 13 lines
lib/librte_mempool/rte_mempool.c +5
-0@@ -99,25 +99,40 @@ static unsigned optimize_object_size(unsigned obj_size)return new_obj_size *RTE_MEMPOOL_ALIGN ; } +struct pagesz_walk_arg { +int socket_id; +size_t min; +}; +
lib/librte_mempool/rte_mempool.c static unsigned optimize_object_size(unsigned obj_size) · line 113
Quoted 10 lines
lib/librte_mempool/rte_mempool.c +3
-3static int find_min_pagesz (const struct rte_memseg_list *msl,void *arg) { -size_t *min = arg; +struct pagesz_walk_arg *wa = arg; +bool valid; -if (msl->page_sz < *min) - *min = msl->page_sz; + valid = msl->socket_id == wa->socket_id;
Is it intended that we accept externally allocated segment if it is on requested socket? If so, it would be good to add comment to explain why.
lib/librte_mempool/rte_mempool.c static unsigned optimize_object_size(unsigned obj_size) · line 120
Quoted 7 lines
lib/librte_mempool/rte_mempool.c +7
-0+ valid |= wa->socket_id ==SOCKET_ID_ANY && msl->external ==0 ; + +if (!valid) +return 0 ; + +if (msl->page_sz < wa->min) + wa->min = msl->page_sz;
I'd suggest to keep single return (it is just a bit shorter) if (valid && msl->page_sz < wa->min)
wa->min = msl->page_sz;
<...>
Re: [PATCH v2 02/20] mem: allow memseg lists to be marked as external
Quoted 46 lines · Andrew Rybchenko
Quoted 11 lines · Anatoly Burakov
When we allocate and use DPDK memory, we need to be able to differentiate between DPDK hugepage segments and segments that were made part of DPDK but are externally allocated. Add such a property to memseg lists.
This breaks the ABI, so bump the EAL library ABI version and document the change in release notes.
All current calls for memseg walk functions were adjusted to ignore external segments where it made sense.
Quoted 8 lines
Mempools is a special case, because we may be asked to allocate a mempool on a specific socket, and we need to ignore all page sizes on other heaps or other sockets. Previously, this assumption of knowing all page sizes was not a problem, but it will be now, so we have to match socket ID with page size when calculating minimum page size for a mempool.
Signed-off-by Anatoly Burakov <[email protected]>A couple of minor questions/suggestions below, but it is OK to go as is even if rejected.
Acked-by Andrew Rybchenko <[email protected]><...>
Quoted 18 lines
lib/librte_mempool/rte_mempool.c +5
-0@@ -99,25 +99,40 @@ static unsigned optimize_object_size(unsigned obj_size) return new_obj_size * RTE_MEMPOOL_ALIGN; } +struct pagesz_walk_arg { + int socket_id; + size_t min; +}; +static int
find_min_pagesz(const struct rte_memseg_list *msl, void *arg){
-size_t*min = arg;
+ structpagesz_walk_arg*wa = arg;
Quoted 8 lines
Quoted 4 lines
+ bool valid;
- if (msl->page_sz< *min)
- *min =msl->page_sz;
+ valid =msl->socket_id==wa->socket_id;Is it intended that we accept externally allocated segment if it is on requested socket? If so, it would be good to add comment to explain why.
Accepting externally allocated segments is precisely the point here - we want to find page size of underlying memory, regardless of whether it's internal or external. We use socket ID to identify valid page sizes for a particular heap (since socket ID is technically a heap identifier, as far as external code is concerned), but within that heap there can be multiple segment lists corresponding to that socket ID, each with its own page size.
Quoted 12 lines
lib/librte_mempool/rte_mempool.c static unsigned optimize_object_size(unsigned obj_size) · line 120
Quoted 8 lines
+ valid |=
wa->socket_id==SOCKET_ID_ANY&&msl->external== 0;lib/librte_mempool/rte_mempool.c +6
-0+ + if (!valid) + return 0; + + if (msl->page_sz < wa->min) + wa->min = msl->page_sz;I'd suggest to keep single return (it is just a bit shorter) if (valid &&
msl->page_sz<wa->min)
wa->min=msl->page_sz;
Sure. If there will be other comments that warrant a v3 respin, i'll incorporate this feedback :)
Thanks for the review!
Quoted 3 lines
<...>
Signature · 2 lines
Thanks, Anatoly
v3 cover Support externally allocated memory in DPDK
In reply to Anatoly BurakovThis is a proposal to enable using externally allocated memory in DPDK.
In a nutshell, here is what is being done here:
- Index internal malloc heaps by NUMA node index, rather than NUMA node itself (external heaps will have ID's in order of creation)
- Add identifier string to malloc heap, to uniquely identify it
- Each new heap will receive a unique socket ID that will be used by allocator to decide from which heap (internal or external) to allocate requested amount of memory
- Allow creating named heaps and add/remove memory to/from those heaps
- Allocate memseg lists at runtime, to keep track of IOVA addresses of externally allocated memory
- If IOVA addresses aren't provided, use RTE_BAD_IOVA
- Allow malloc and memzones to allocate from external heaps
- Allow other data structures to allocate from externall heaps
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
The general approach is to create heap and add memory into it. For any other process wishing to use the same memory, said memory must first be attached (otherwise some things will not work).
A design decision was made to make multiprocess synchronization a manual process. Due to underlying issues with attaching to fbarrays in secondary processes, this design was deemed to be better because we don't want to fail to create external heap in the primary because something in the secondary has failed when in fact we may not eve have wanted this memory to be accessible in the secondary in the first place.
Using external memory in multiprocess is *hard*, because not only memory space needs to be preallocated, but it also needs to be attached in each process to allow other processes to access the page table. The attach API call may or may not succeed, depending on memory layout, for reasons similar to other multiprocess failures. This is treated as a "known issue" for this release.
Creating and destroying heaps is currently restricted to primary processes, because we need to keep track of all socket ID's we've ever used to prevent their reuse, and obviously different processes would have kept different socket ID counters, and it isn't important enough to put into shared memory. This means that secondary processes will not be able to create new heaps. If this use case is important enough, we can put the max socket ID into shared memory, or allow socket ID reuse (which i do not think is a good idea because it has the potential to make things harder to debug).
v3 -> v2 changes:
- Rebase on top of latest master
- Clarifications added to mempool code as per Andrew Rynchenko's comments
v2 -> v1 changes:
- Fixed NULL dereference on heap socket ID lookup
- Fixed memseg offset calculation on adding memory to heap
- Improved unit test to test for above bugfixes
- Restricted heap creation to primary processes only
- Added sample application
- Added documentation
RFC -> v1 changes:
- Removed the "named heaps" API, allocate using fake socket ID instead
- Added multiprocess support
- Everything is now thread-safe
- Numerous bugfixes and API improvements
Anatoly Burakov (20):
mem: add length to memseg list
mem: allow memseg lists to be marked as external
malloc: index heaps using heap ID rather than NUMA node
mem: do not check for invalid socket ID
flow_classify: do not check for invalid socket ID
pipeline: do not check for invalid socket ID
sched: do not check for invalid socket ID
malloc: add name to malloc heaps
malloc: add function to query socket ID of named heap
malloc: allow creating malloc heaps
malloc: allow destroying heaps
malloc: allow adding memory to named heaps
malloc: allow removing memory from named heaps
malloc: allow attaching to external memory chunks
malloc: allow detaching from external memory
test: add unit tests for external memory support
examples: add external memory example app
doc: add external memory feature to the release notes
doc: add external memory feature to programmer's guide
doc: add external memory sample application guide
config/common_base | 1 + config/rte_config.h | 1 + .../prog_guide/env_abstraction_layer.rst | 38 ++ doc/guides/rel_notes/deprecation.rst | 15 - doc/guides/rel_notes/release_18_11.rst | 24 +- doc/guides/sample_app_ug/external_mem.rst | 115 +++++ doc/guides/sample_app_ug/index.rst | 1 + drivers/bus/fslmc/fslmc_vfio.c | 7 +- drivers/bus/pci/linux/pci.c | 2 +- drivers/net/mlx4/mlx4_mr.c | 3 + drivers/net/mlx5/mlx5.c | 5 +- drivers/net/mlx5/mlx5_mr.c | 3 + drivers/net/virtio/virtio_user/vhost_kernel.c | 5 +- examples/external_mem/Makefile | 62 +++ examples/external_mem/extmem.c | 461 ++++++++++++++++++ examples/external_mem/meson.build | 12 + lib/librte_eal/bsdapp/eal/Makefile | 2 +- lib/librte_eal/bsdapp/eal/eal.c | 3 + lib/librte_eal/bsdapp/eal/eal_memory.c | 9 +- lib/librte_eal/common/eal_common_memory.c | 8 +- lib/librte_eal/common/eal_common_memzone.c | 8 +- .../common/include/rte_eal_memconfig.h | 6 +- lib/librte_eal/common/include/rte_malloc.h | 183 +++++++ .../common/include/rte_malloc_heap.h | 3 + lib/librte_eal/common/include/rte_memory.h | 9 + lib/librte_eal/common/malloc_heap.c | 300 ++++++++++-- lib/librte_eal/common/malloc_heap.h | 17 + lib/librte_eal/common/rte_malloc.c | 393 ++++++++++++++- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 10 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 12 +- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +- lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 +- lib/librte_eal/meson.build | 2 +- lib/librte_eal/rte_eal_version.map | 7 + lib/librte_flow_classify/rte_flow_classify.c | 3 +- lib/librte_mempool/rte_mempool.c | 35 +- lib/librte_pipeline/rte_pipeline.c | 3 +- lib/librte_sched/rte_sched.c | 2 +- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 +++++++++++++++ test/test/test_malloc.c | 3 + test/test/test_memzone.c | 3 + 45 files changed, 2099 insertions(+), 105 deletions(-) create mode 100644 doc/guides/sample_app_ug/external_mem.rst create mode 100644 examples/external_mem/Makefile create mode 100644 examples/external_mem/extmem.c create mode 100644 examples/external_mem/meson.build create mode 100644 test/test/test_external_mem.c
Signature · 1 line
2.17.1
PATCH v3 3/20 malloc: index heaps using heap ID rather than NUMA node
Switch over all parts of EAL to use heap ID instead of NUMA node ID to identify heaps. Heap ID for DPDK-internal heaps is NUMA node's index within the detected NUMA node list. Heap ID for external heaps will be order of their creation.
--- config/common_base | 1 + config/rte_config.h | 1 + .../common/include/rte_eal_memconfig.h | 4 +- .../common/include/rte_malloc_heap.h | 1 + lib/librte_eal/common/malloc_heap.c | 98 +++++++++++++------ lib/librte_eal/common/malloc_heap.h | 3 + lib/librte_eal/common/rte_malloc.c | 41 +++++--- 7 files changed, 106 insertions(+), 43 deletions(-)
config/common_base +1 -0
@@ -61,6 +61,7 @@ CONFIG_RTE_CACHE_LINE_SIZE=64 CONFIG_RTE_LIBRTE_EAL=y CONFIG_RTE_MAX_LCORE=128 CONFIG_RTE_MAX_NUMA_NODES=8 +CONFIG_RTE_MAX_HEAPS=32 CONFIG_RTE_MAX_MEMSEG_LISTS=64 # each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages # or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
config/rte_config.h +1 -0
@@ -24,6 +24,7 @@#define RTE_BUILD_SHARED_LIB /* EAL defines */ +#define RTE_MAX_HEAPS 32#define RTE_MAX_MEMSEG_LISTS 128#define RTE_MAX_MEMSEG_PER_LIST 8192#define RTE_MAX_MEM_MB_PER_LIST 32768
lib/librte_eal/common/include/rte_eal_memconfig.h +2 -2
@@ -72,8 +72,8 @@ struct rte_mem_config {struct rte_tailq_head tailq_head[RTE_MAX_TAILQ ];/**< Tailqs for objects */ -/* Heaps of Malloc per socket */ -struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES ]; +/* Heaps of Malloc */ +struct malloc_heap malloc_heaps[RTE_MAX_HEAPS ];/* address of mem_config in primary process. used to map shared config into * exact same address the primary process maps it.
lib/librte_eal/common/include/rte_malloc_heap.h +1 -0
@@ -27,6 +27,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size; + unsigned int socket_id; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +69 -29
204 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -46,6 +46,9 @@ malloc_heap_get_stats(struct malloc_heap *heap,void malloc_heap_dump (struct malloc_heap *heap,FILE *f); +int +malloc_socket_to_heap_id (unsigned int socket_id); +int rte_eal_malloc_heap_init (void );
lib/librte_eal/common/rte_malloc.c +29 -12
72 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v3 5/20 flow_classify: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_flow_classify/rte_flow_classify.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_flow_classify/rte_flow_classify.c +1 -2
@@ -247,8 +247,7 @@ rte_flow_classifier_check_params(struct rte_flow_classifier_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_FLOW_CLASSIFY_LOG (ERR ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH v3 7/20 sched: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_sched/rte_sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/librte_sched/rte_sched.c +1 -1
@@ -329,7 +329,7 @@ rte_sched_port_check_params(struct rte_sched_port_params *params)return -1 ;/* socket */ -if ((params->socket <0 ) || (params->socket >=RTE_MAX_NUMA_NODES )) +if (params->socket <0 )return -3 ;/* rate */
Signature · 1 line
2.17.1
PATCH v3 11/20 malloc: allow destroying heaps
In reply to Anatoly BurakovAdd an API to destroy specified heap.
--- lib/librte_eal/common/include/rte_malloc.h | 23 +++++++++ lib/librte_eal/common/malloc_heap.c | 22 ++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 58 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 107 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +23 -0
@@ -282,6 +282,29 @@ rte_malloc_get_socket_stats(int socket, int __rte_experimentalrte_malloc_heap_create (const char *heap_name); +/** +* Destroys a previously created malloc heap with specified name. +* +* @note This function will return a failure result if not all memory allocated +* from the heap has been freed back to the heap +* +* @note This function will return a failure result if not all memory segments +* were removed from the heap prior to its destruction +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* ENOENT - heap by the name of ``heap_name`` was not found +* EPERM - attempting to destroy reserved heap +* EBUSY - heap still contains data +*/ +int __rte_experimental +rte_malloc_heap_destroy (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +22 -0
@@ -1045,6 +1045,28 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name)return 0 ; } +int +malloc_heap_destroy (struct malloc_heap *heap) +{ +if (heap->alloc_count !=0 ) { +RTE_LOG (ERR ,EAL ,"Heap is still in use\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->first !=NULL || heap->last !=NULL ) { +RTE_LOG (ERR ,EAL ,"Heap still contains memory segments\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->total_size !=0 ) +RTE_LOG (ERR ,EAL ,"Total size not zero, heap is likely corrupt\n" ); + +/* after this, the lock will be dropped */ +memset (heap,0 ,sizeof (*heap)); + +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -36,6 +36,9 @@ malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags,int malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int +malloc_heap_destroy (struct malloc_heap *heap); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +58 -0
67 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -319,6 +319,7 @@ EXPERIMENTAL { rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; rte_malloc_heap_create; + rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v3 13/20 malloc: allow removing memory from named heaps
In reply to Anatoly BurakovAdd an API to remove memory from specified heaps. This will first check if all elements within the region are free, and that the region is the original region that was added to the heap (by comparing its length to length of memory addressed by the underlying memseg list).
--- lib/librte_eal/common/include/rte_malloc.h | 27 +++++++++++ lib/librte_eal/common/malloc_heap.c | 54 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 39 ++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 125 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -302,6 +302,33 @@ int __rte_experimental rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +/** +* Remove memory chunk from heap with specified name. +* +* @note Memory chunk being removed must be the same as one that was added; +* partially removing memory chunks is not supported +* +* @note Memory area must not contain any allocated elements to allow its +* removal from the heap +* +* @param heap_name +* Name of the heap to remove memory from +* @param va_addr +* Virtual address to remove from the heap +* @param len +* Length of virtual area to remove from the heap +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to remove memory from a reserved heap +* ENOENT - heap or memory chunk was not found +* EBUSY - memory chunk still contains data +*/ +int __rte_experimental +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +54 -0
66 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -43,6 +43,10 @@ int malloc_heap_add_external_memory(struct malloc_heap *heap, void *va_addr, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +int +malloc_heap_remove_external_memory (struct malloc_heap *heap,void *va_addr, +size_t len); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +39 -0
@@ -354,6 +354,45 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,return ret; } +int +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +int ret; + +if (heap_name ==NULL || va_addr ==NULL || len ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_write_lock (&mcfg->memory_hotplug_lock); +/* find our heap */ + heap =find_named_heap (heap_name); +if (heap ==NULL ) { + rte_errno =ENOENT ; + ret =-1 ; + goto unlock; + } +if (heap->socket_id <RTE_MAX_NUMA_NODES ) { +/* cannot remove memory from internal heaps */ + rte_errno =EPERM ; + ret =-1 ; + goto unlock; + } + +rte_spinlock_lock (&heap->lock); + ret =malloc_heap_remove_external_memory (heap, va_addr, len); +rte_spinlock_unlock (&heap->lock); + +unlock: +rte_rwlock_write_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -322,6 +322,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v3 14/20 malloc: allow attaching to external memory chunks
In reply to Anatoly BurakovIn order to use external memory in multiple processes, we need to attach to primary process's memseg lists, so add a new API to do that. It is the responsibility of the user to ensure that memory is accessible and that it has been previously added to the malloc heap by another process.
--- lib/librte_eal/common/include/rte_malloc.h | 48 +++++++++-- lib/librte_eal/common/rte_malloc.c | 93 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 135 insertions(+), 7 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +41 -7
@@ -268,6 +268,10 @@ rte_malloc_get_socket_stats(int socket,* * @note Multiple memory chunks can be added to the same heap * +* @note Before accessing this memory in other processes, it needs to be +* attached in each of those processes by calling +* ``rte_malloc_heap_memory_attach`` in each other process. +* * @note Memory must be previously allocated for DPDK to be able to use it as a * malloc heap. Failing to do so will result in undefined behavior, up to and * including segmentation faults.
@@ -329,21 +333,48 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, int __rte_experimentalrte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** +* Attach to an already existing chunk of external memory in another process. +* +* @note This function must be called before any attempt is made to use an +* already existing external memory chunk. This function does *not* need to +* be called if a call to ``rte_malloc_heap_memory_add`` was made in the +* current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful attach +* -1 on unsuccessful attach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to attach memory to a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. * * @note Heaps created via this call will automatically get assigned a unique * socket ID, which can be found using ``rte_malloc_heap_get_socket()`` * +* @note This function can only be called in primary process. +* * @param heap_name * Name of the heap to create. * * @return * - 0 on successful creation * - -1 in case of error, with rte_errno set to one of the following: -* EINVAL - ``heap_name`` was NULL, empty or too long -* EEXIST - heap by name of ``heap_name`` already exists -* ENOSPC - no more space in internal config to store a new heap +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +* E_RTE_SECONDARY - attempted to create a heap in secondary process */ int __rte_experimentalrte_malloc_heap_create (const char *heap_name);
@@ -357,16 +388,19 @@ rte_malloc_heap_create(const char *heap_name);* @note This function will return a failure result if not all memory segments * were removed from the heap prior to its destruction * +* @note This function can only be called in primary process. +* * @param heap_name * Name of the heap to create. * * @return * - 0 on success * - -1 in case of error, with rte_errno set to one of the following: -* EINVAL - ``heap_name`` was NULL, empty or too long -* ENOENT - heap by the name of ``heap_name`` was not found -* EPERM - attempting to destroy reserved heap -* EBUSY - heap still contains data +* EINVAL - ``heap_name`` was NULL, empty or too long +* ENOENT - heap by the name of ``heap_name`` was not found +* EPERM - attempting to destroy reserved heap +* EBUSY - heap still contains data +* E_RTE_SECONDARY - attempted to destroy a heap in secondary process */ int __rte_experimentalrte_malloc_heap_destroy (const char *heap_name);
lib/librte_eal/common/rte_malloc.c +93 -0
111 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -322,6 +322,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_attach; rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v3 16/20 test: add unit tests for external memory support
In reply to Anatoly BurakovAdd simple unit tests to test external memory support. The tests are pretty basic and mostly consist of checking if invalid API calls are handled correctly, plus a simple allocation/deallocation test for malloc and memzone.
--- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 ++++++++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 test/test/test_external_mem.c
test/test/Makefile +1 -0
@@ -71,6 +71,7 @@ SRCS-y += test_bitmap.c SRCS-y += test_reciprocal_division.c SRCS-y += test_reciprocal_division_perf.c SRCS-y += test_fbarray.c +SRCS-y += test_external_mem.c SRCS-y += test_ring.c SRCS-y += test_ring_perf.c
test/test/autotest_data.py +10 -4
@@ -477,10 +477,16 @@ "Report": None, }, { - "Name": "Fbarray autotest", - "Command": "fbarray_autotest", - "Func": default_autotest, - "Report": None, + "Name": "Fbarray autotest", + "Command": "fbarray_autotest", + "Func": default_autotest, + "Report": None, + }, + { + "Name": "External memory autotest", + "Command": "external_mem_autotest", + "Func": default_autotest, + "Report": None, }, # #Please always keep all dump tests at the end and together!
test/test/meson.build +1 -0
@@ -155,6 +155,7 @@ test_names = [ 'eventdev_common_autotest', 'eventdev_octeontx_autotest', 'eventdev_sw_autotest', + 'external_mem_autotest', 'func_reentrancy_autotest', 'flow_classify_autotest', 'hash_scaling_autotest',
test/test/test_external_mem.c new file +389 -0
389 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v3 18/20 doc: add external memory feature to the release notes
In reply to Anatoly BurakovDocument the addition of external memory support to DPDK.
--- doc/guides/rel_notes/release_18_11.rst | 5 +++++ 1 file changed, 5 insertions(+)
doc/guides/rel_notes/release_18_11.rst +5 -0
@@ -67,6 +67,11 @@ New Features SR-IOV option in Hyper-V and Azure. This is an alternative to the previous vdev_netvsc, tap, and failsafe drivers combination. +* **Added support for using externally allocated memory in DPDK.** + + DPDK has gained support for creating new ``rte_malloc`` heaps referencing + memory that was created outside of DPDK's own page allocator, and using that + memory natively with any other DPDK library or data structure. API Changes -----------
Signature · 1 line
2.17.1
PATCH v3 2/20 mem: allow memseg lists to be marked as external
In reply to Anatoly BurakovWhen we allocate and use DPDK memory, we need to be able to differentiate between DPDK hugepage segments and segments that were made part of DPDK but are externally allocated. Add such a property to memseg lists.
This breaks the ABI, so bump the EAL library ABI version and document the change in release notes.
All current calls for memseg walk functions were adjusted to ignore external segments where it made sense.
Mempools is a special case, because we may be asked to allocate a mempool on a specific socket, and we need to ignore all page sizes on other heaps or other sockets. Previously, this assumption of knowing all page sizes was not a problem, but it will be now, so we have to match socket ID with page size when calculating minimum page size for a mempool.
---
Notes:
v3:
- Add comment to explain the process of picking up minimum page sizes for mempool
v2:
- Add documentation changes and ABI break
v1:
- Adjust all calls to memseg walk functions to ignore external segments where it made sense to do so
doc/guides/rel_notes/deprecation.rst | 15 -------- doc/guides/rel_notes/release_18_11.rst | 12 ++++++- drivers/bus/fslmc/fslmc_vfio.c | 7 ++-- drivers/net/mlx4/mlx4_mr.c | 3 ++ drivers/net/mlx5/mlx5.c | 5 ++- drivers/net/mlx5/mlx5_mr.c | 3 ++ drivers/net/virtio/virtio_user/vhost_kernel.c | 5 ++- lib/librte_eal/bsdapp/eal/Makefile | 2 +- lib/librte_eal/bsdapp/eal/eal.c | 3 ++ lib/librte_eal/bsdapp/eal/eal_memory.c | 7 ++-- lib/librte_eal/common/eal_common_memory.c | 3 ++ .../common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/common/include/rte_memory.h | 9 +++++ lib/librte_eal/common/malloc_heap.c | 9 +++-- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 10 +++++- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 9 +++++ lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 ++++++--- lib/librte_eal/meson.build | 2 +- lib/librte_mempool/rte_mempool.c | 35 ++++++++++++++----- test/test/test_malloc.c | 3 ++ test/test/test_memzone.c | 3 ++ 22 files changed, 125 insertions(+), 40 deletions(-)
doc/guides/rel_notes/deprecation.rst +0 -15
@@ -11,21 +11,6 @@ API and ABI deprecation notices are to be posted here. Deprecation Notices ------------------- -* eal: certain structures will change in EAL on account of upcoming external - memory support. Aside from internal changes leading to an ABI break, the - following externally visible changes will also be implemented: - - - ``rte_memseg_list`` will change to include a boolean flag indicating - whether a particular memseg list is externally allocated. This will have - implications for any users of memseg-walk-related functions, as they will - now have to skip externally allocated segments in most cases if the intent - is to only iterate over internal DPDK memory. - - ``socket_id`` parameter across the entire DPDK will gain additional meaning, - as some socket ID's will now be representing externally allocated memory. No - changes will be required for existing code as backwards compatibility will - be kept, and those who do not use this feature will not see these extra - socket ID's. - * eal: both declaring and identifying devices will be streamlined in v18.11. New functions will appear to query a specific port from buses, classes of device and device drivers. Device declaration will be made coherent with the
doc/guides/rel_notes/release_18_11.rst +11 -1
@@ -91,6 +91,13 @@ API Changes flag the MAC can be properly configured in any case. This is particularly important for bonding. +* eal: The following API changes were made in 18.11: + + - ``rte_memseg_list`` structure now has an additional flag indicating whether + the memseg list is externally allocated. This will have implications for any + users of memseg-walk-related functions, as they will now have to skip + externally allocated segments in most cases if the intent is to only iterate + over internal DPDK memory. ABI Changes -----------
@@ -107,6 +114,9 @@ ABI Changes ========================================================= +* eal: EAL library ABI version was changed due to previously announced work on + supporting external memory in DPDK. + Removed Items -------------
@@ -152,7 +162,7 @@ The libraries prepended with a plus sign were incremented in this version. librte_compressdev.so.1 librte_cryptodev.so.5 librte_distributor.so.1 - librte_eal.so.8 + + librte_eal.so.9 librte_ethdev.so.10 librte_eventdev.so.4 librte_flow_classify.so.1
drivers/bus/fslmc/fslmc_vfio.c +5 -2
@@ -317,12 +317,15 @@ fslmc_unmap_dma(uint64_t vaddr, uint64_t iovaddr __rte_unused, size_t len) }static int -fslmc_dmamap_seg(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +fslmc_dmamap_seg (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *n_segs = arg;int ret; +if (msl->external) +return 0 ; + ret =fslmc_map_dma (ms->addr_64, ms->iova, ms->len);if (ret) DPAA2_BUS_ERR("Unable to VFIO map (addr=%p, len=%zu)",
drivers/net/mlx4/mlx4_mr.c +3 -0
@@ -496,6 +496,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/mlx5/mlx5.c +4 -1
@@ -568,11 +568,14 @@ static struct rte_pci_driver mlx5_driver;static void *uar_base;static int -find_lower_va_bound(const struct rte_memseg_list *msl __rte_unused, +find_lower_va_bound (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {void **addr = arg; +if (msl->external) +return 0 ; +if (*addr ==NULL ) *addr = ms->addr;else
drivers/net/mlx5/mlx5_mr.c +3 -0
@@ -486,6 +486,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/virtio/virtio_user/vhost_kernel.c +4 -1
@@ -75,13 +75,16 @@ struct walk_arg {uint32_t region_nr; };static int -add_memory_region(const struct rte_memseg_list *msl __rte_unused, +add_memory_region (const struct rte_memseg_list *msl,const struct rte_memseg *ms,size_t len,void *arg) {struct walk_arg *wa = arg;struct vhost_memory_region *mr;void *start_addr; +if (msl->external) +return 0 ; +if (wa->region_nr >= max_regions)return -1 ;
lib/librte_eal/bsdapp/eal/Makefile +1 -1
@@ -22,7 +22,7 @@ LDLIBS += -lrte_kvargs EXPORT_MAP := ../../rte_eal_version.map -LIBABIVER := 8 +LIBABIVER := 9 # specific to bsdapp exec-env SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
lib/librte_eal/bsdapp/eal/eal.c +3 -0
@@ -502,6 +502,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +if (msl->socket_id == *socket_id && msl->memseg_arr.count !=0 )return 1 ;
lib/librte_eal/bsdapp/eal/eal_memory.c +5 -2
@@ -236,12 +236,15 @@ struct attach_walk_args {int seg_idx; };static int -attach_segment(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +attach_segment (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {struct attach_walk_args *wa = arg;void *addr; +if (msl->external) +return 0 ; + addr =mmap (ms->addr, ms->len,PROT_READ |PROT_WRITE ,MAP_SHARED |MAP_FIXED , wa->fd_hugepage, wa->seg_idx *EAL_PAGE_SIZE );
lib/librte_eal/common/eal_common_memory.c +3 -0
@@ -272,6 +272,9 @@ physmem_size(const struct rte_memseg_list *msl, void *arg) {uint64_t *total_len = arg; +if (msl->external) +return 0 ; + *total_len += msl->memseg_arr.count * msl->page_sz;return 0 ;
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -33,6 +33,7 @@ struct rte_memseg_list {size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ +unsigned int external;/**< 1 if this list points to external memory */ volatile uint32_t version;/**< version number for multiprocess sync. */ struct rte_fbarray memseg_arr; };
lib/librte_eal/common/include/rte_memory.h +9 -0
@@ -215,6 +215,9 @@ typedef int (*rte_memseg_list_walk_t)(const struct rte_memseg_list *msl,* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -233,6 +236,9 @@ rte_memseg_walk(rte_memseg_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -251,6 +257,9 @@ rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
lib/librte_eal/common/malloc_heap.c +7 -2
@@ -95,6 +95,9 @@ malloc_add_seg(const struct rte_memseg_list *msl,struct malloc_heap *heap;int msl_idx; +if (msl->external) +return 0 ; + heap = &mcfg->malloc_heaps[msl->socket_id];/* msl is const, so find it */
@@ -754,8 +757,10 @@ malloc_heap_free(struct malloc_elem *elem)/* anything after this is a bonus */ ret =0 ; -/* ...of which we can't avail if we are in legacy mode */ -if (internal_config.legacy_mem) +/* ...of which we can't avail if we are in legacy mode, or if this is an +* externally allocated segment. +*/ +if (internal_config.legacy_mem || msl->external) goto free_unlock;/* check if we can free any memory back to the system */
lib/librte_eal/linuxapp/eal/Makefile +1 -1
@@ -10,7 +10,7 @@ ARCH_DIR ?= $(RTE_ARCH) EXPORT_MAP := ../../rte_eal_version.map VPATH += $(RTE_SDK)/lib/librte_eal/common/arch/$(ARCH_DIR) -LIBABIVER := 8 +LIBABIVER := 9 VPATH += $(RTE_SDK)/lib/librte_eal/common
lib/librte_eal/linuxapp/eal/eal.c +9 -1
@@ -725,6 +725,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +return *socket_id == msl->socket_id; }
@@ -1059,7 +1062,12 @@ mark_freeable(const struct rte_memseg_list *msl, const struct rte_memseg *ms, void *arg __rte_unused) {/* ms is const, so find this memseg */ -struct rte_memseg *found =rte_mem_virt2memseg (ms->addr, msl); +struct rte_memseg *found; + +if (msl->external) +return 0 ; + + found =rte_mem_virt2memseg (ms->addr, msl); found->flags &= ~RTE_MEMSEG_FLAG_DO_NOT_FREE ;
lib/librte_eal/linuxapp/eal/eal_memalloc.c +9 -0
@@ -1408,6 +1408,9 @@ sync_walk(const struct rte_memseg_list *msl, void *arg __rte_unused)unsigned int i;int msl_idx; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1456,6 +1459,9 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,char name[PATH_MAX ];int msl_idx, ret; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1509,6 +1515,9 @@ fd_list_create_walk(const struct rte_memseg_list *msl,unsigned int len;int msl_idx; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; len = msl->memseg_arr.len;
lib/librte_eal/linuxapp/eal/eal_vfio.c +13 -4
@@ -1082,11 +1082,14 @@ rte_vfio_get_group_num(const char *sysfs_base, }static int -type1_map(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +type1_map (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_type1_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1196,11 +1199,14 @@ vfio_spapr_dma_do_map(int vfio_container_fd, uint64_t vaddr, uint64_t iova, }static int -vfio_spapr_map_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_map_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_spapr_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1210,12 +1216,15 @@ struct spapr_walk_param {uint64_t hugepage_sz; };static int -vfio_spapr_window_size_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_window_size_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {struct spapr_walk_param *param = arg;uint64_t max = ms->iova + ms->len; +if (msl->external) +return 0 ; +if (max > param->window_size) { param->hugepage_sz = ms->hugepage_sz; param->window_size = max;
lib/librte_eal/meson.build +1 -1
@@ -21,7 +21,7 @@ else error('unsupported system type "@0@"'.format(host_machine.system())) endif -version = 8 # the version of the EAL API +version = 9 # the version of the EAL API allow_experimental_apis = true deps += 'compat' deps += 'kvargs'
lib/librte_mempool/rte_mempool.c +27 -8
@@ -99,25 +99,44 @@ static unsigned optimize_object_size(unsigned obj_size)return new_obj_size *RTE_MEMPOOL_ALIGN ; } +struct pagesz_walk_arg { +int socket_id; +size_t min; +}; +static int find_min_pagesz (const struct rte_memseg_list *msl,void *arg) { -size_t *min = arg; +struct pagesz_walk_arg *wa = arg; +bool valid; -if (msl->page_sz < *min) - *min = msl->page_sz; +/* +* we need to only look at page sizes available for a particular socket +* ID. so, we either need an exact match on socket ID (can match both +* native and external memory), or, if SOCKET_ID_ANY was specified as a +* socket ID argument, we must only look at native memory and ignore any +* page sizes associated with external memory. +*/ + valid = msl->socket_id == wa->socket_id; + valid |= wa->socket_id ==SOCKET_ID_ANY && msl->external ==0 ; + +if (valid && msl->page_sz < wa->min) + wa->min = msl->page_sz;return 0 ; }static size_t -get_min_page_size (void ) +get_min_page_size (int socket_id) { -size_t min_pagesz =SIZE_MAX ; +struct pagesz_walk_arg wa; -rte_memseg_list_walk (find_min_pagesz, &min_pagesz); + wa.min =SIZE_MAX ; + wa.socket_id = socket_id; -return min_pagesz ==SIZE_MAX ? (size_t )getpagesize () : min_pagesz; +rte_memseg_list_walk (find_min_pagesz, &wa); + +return wa.min ==SIZE_MAX ? (size_t )getpagesize () : wa.min; }
@@ -470,7 +489,7 @@ rte_mempool_populate_default(struct rte_mempool *mp) pg_sz =0 ; pg_shift =0 ; }else if (try_contig) { - pg_sz =get_min_page_size (); + pg_sz =get_min_page_size (mp->socket_id); pg_shift =rte_bsf32 (pg_sz); }else { pg_sz =getpagesize ();
test/test/test_malloc.c +3 -0
@@ -711,6 +711,9 @@ check_socket_mem(const struct rte_memseg_list *msl, void *arg) {int32_t *socket = arg; +if (msl->external) +return 0 ; +return *socket == msl->socket_id; }
test/test/test_memzone.c +3 -0
@@ -115,6 +115,9 @@ find_available_pagesz(const struct rte_memseg_list *msl, void *arg) {struct walk_arg *wa = arg; +if (msl->external) +return 0 ; +if (msl->page_sz ==RTE_PGSIZE_2M ) wa->hugepage_2MB_avail =1 ;if (msl->page_sz ==RTE_PGSIZE_1G )
Signature · 1 line
2.17.1
PATCH v3 19/20 doc: add external memory feature to programmer's guide
In reply to Anatoly BurakovAdd a short chapter on usage of external memory in DPDK to the Programmer's Guide.
--- .../prog_guide/env_abstraction_layer.rst | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+)
doc/guides/prog_guide/env_abstraction_layer.rst +38 -0
@@ -213,6 +213,44 @@ Normally, these options do not need to be changed. can later be mapped into that preallocated VA space (if dynamic memory mode is enabled), and can optionally be mapped into it at startup. +Support for Externally Allocated Memory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to use externally allocated memory in DPDK, using a set of malloc +heap API's. Support for externally allocated memory is implemented through +overloading the socket ID - externally allocated heaps will have socket ID's +that would be considered invalid under normal circumstances. Requesting an +allocation to take place from a specified externally allocated memory is a +matter of supplying the correct socket ID to DPDK allocator, either directly +(e.g. through a call to ``rte_malloc``) or indirectly (through data +structure-specific allocation API's such as ``rte_ring_create``). + +Since there is no way DPDK can verify whether memory are is available or valid, +this responsibility falls on the shoulders of the user. All multiprocess +synchronization is also user's responsibility, as well as ensuring that all +calls to add/attach/detach/remove memory are done in the correct order. It is +not required to attach to a memory area in all processes - only attach to memory +areas as needed. + +The expected workflow is as follows: + +* Get a pointer to memory area +* Create a named heap +* Add memory area(s) to the heap + * If IOVA table is not specified, IOVA addresses will be assumed to be + unavailable + * Any DMA mappings for the external area are responsibility of the user + * Other processes must attach to the memory area before they can use it +* Get socket ID used for the heap +* Use normal DPDK allocation procedures, using supplied socket ID +* If memory area is no longer needed, it can be removed from the heap + * Other processes must detach from this memory area before it can be removed +* If heap is no longer needed, remove it + * Socket ID will become invalid and will not be reused + +For more information, please refer to ``rte_malloc`` API documentation, +specifically the ``rte_malloc_heap_*`` family of function calls. + PCI Access ~~~~~~~~~~
Signature · 1 line
2.17.1
PATCH v3 20/20 doc: add external memory sample application guide
In reply to Anatoly BurakovAdd a guide for external memory sample application. The application is identical to Basic Forwarding example in everything except parts of initialization code, so the bits that are identical will not be described.
It is also not necessary to describe how external memory is being allocated due to the expectation being that user will have their own mechanisms to allocate memory outside of DPDK, and will only be interested in how to integrate said memory into DPDK.
--- doc/guides/sample_app_ug/external_mem.rst | 115 ++++++++++++++++++++++ doc/guides/sample_app_ug/index.rst | 1 + 2 files changed, 116 insertions(+) create mode 100644 doc/guides/sample_app_ug/external_mem.rst
doc/guides/sample_app_ug/external_mem.rst new file +115 -0
@@ -0,0 +1,115 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2015-2018 Intel Corporation. + +External Memory Sample Application +================================== + +The External Memory sample application is a simple *skeleton* example of a +forwarding application using externally allocated memory. + +It is intended as a demonstration of the basic workflow of using externally +allocated memory in DPDK. This application is based on Basic Forwarding sample +application, and differs only in its initialization path. For more detailed +explanation of port initialization and packet forwarding code, please refer to +*Basic Forwarding sample application user guide*. + +Compiling the Application +------------------------- + +To compile the sample application see :doc:`compiling`. + +The application is located in the ``external_mem`` sub-directory. + +Running the Application +----------------------- + +To run the example in a ``linuxapp`` environment: + +.. code-block:: console + + ./build/extmem -l 1 -n 4 + +Refer to *DPDK Getting Started Guide* for general information on running +applications and the Environment Abstraction Layer (EAL) options. + + +Explanation +----------- + +For general overview of the code and explanation of the main components of this +application, please refer to *Basic Forwarding sample application user guide*. +This guide will only explain sections of the code relevant to using external +memory in DPDK. + +All DPDK library functions used in the sample code are prefixed with ``rte_`` +and are explained in detail in the *DPDK API Documentation*. + + +External Memory Initialization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``main()`` function performs the initialization and calls the execution +threads for each lcore. + +After initializing the Environment Abstraction Layer, the application also +initializes external memory (in this case, it's allocating a chunk of memory +using anonymous hugepages) inside the ``setup_extmem()`` local function. + +The first step in this process is to create an external heap: + +.. code-block:: c + + ret = rte_malloc_heap_create(EXTMEM_HEAP_NAME); + if (ret < 0) { + printf("Cannot create heap\n"); + return -1; + } + +Once the heap is created, ``create_extmem`` function is called to create the +actual external memory area the application will be using. While the details of +that process will not be described as they are not pertinent to the external +memory API (it is expected that the user will have their own procedures to +create external memory), there are a few important things to note. + +In order to add an externally allocated memory area to the newly created heap, +the application needs the following pieces of information: + +* Pointer to start address of external memory area +* Length of this area +* Page size of memory backing this memory area +* Optionally, a per-page IOVA table + +All of this information is to be provided by the user. Additionally, if VFIO is +in use and if application intends to do DMA using the memory area, VFIO DMA +mapping must also be performed using ``rte_vfio_dma_map`` function. + +Once the external memory is created and mapped for DMA, the application also has +to add this memory to the heap that was created earlier: + +.. code-block:: c + + ret = rte_malloc_heap_memory_add(EXTMEM_HEAP_NAME, + param.addr, param.len, param.iova_table, + param.iova_table_len, param.pgsz); + +If return value indicates success, the memory area has been successfully added +to the heap. The next step is to retrieve the socket ID of this heap: + +.. code-block:: c + + socket_id = rte_malloc_heap_get_socket(EXTMEM_HEAP_NAME); + if (socket_id < 0) + rte_exit(EXIT_FAILURE, "Invalid socket for external heap\n"); + +After that, the socket ID has to be supplied to the mempool creation function: + +.. code-block:: c + + mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", + nb_mbufs_per_port * nb_ports, MBUF_CACHE_SIZE, 0, + mbuf_sz, socket_id); + +The rest of the application is identical to the Basic Forwarding example. + +The forwarding loop can be interrupted and the application closed using +``Ctrl-C``.
doc/guides/sample_app_ug/index.rst +1 -0
@@ -15,6 +15,7 @@ Sample Applications User Guides exception_path hello_world skeleton + external_mem rxtx_callbacks flow_classify flow_filtering
Signature · 1 line
2.17.1
PATCH v3 6/20 pipeline: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_pipeline/rte_pipeline.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_pipeline/rte_pipeline.c +1 -2
@@ -178,8 +178,7 @@ rte_pipeline_check_params(struct rte_pipeline_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_LOG (ERR ,PIPELINE ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH v3 17/20 examples: add external memory example app
In reply to Anatoly BurakovIntroduce an example application demonstrating the use of external memory support. This is a simple application based on skeleton app, but instead of using internal DPDK memory, it is using externally allocated memory.
The RX/TX and init path is a carbon-copy of skeleton app, with no modifications whatseoever. The only difference is an additional init stage to allocate memory and create a heap for it, and the socket ID supplied to the mempool initialization function. The memory used by this app is hugepage memory allocated anonymously.
Anonymous hugepage memory will not be allocated in a NUMA-aware fashion, so there is a chance of performance degradation when using this app, but given that kernel usually gives hugepages on local socket first, this should not be a problem in most cases.
--- examples/external_mem/Makefile | 62 ++++ examples/external_mem/extmem.c | 461 ++++++++++++++++++++++++++++++ examples/external_mem/meson.build | 12 + 3 files changed, 535 insertions(+) create mode 100644 examples/external_mem/Makefile create mode 100644 examples/external_mem/extmem.c create mode 100644 examples/external_mem/meson.build
examples/external_mem/Makefile new file +62 -0
@@ -0,0 +1,62 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2018 Intel Corporation + +# binary name +APP = extmem + +# all source are stored in SRCS-y +SRCS-y := extmem.c + +# Build using pkg-config variables if possible +$(shell pkg-config --exists libdpdk) +ifeq ($(.SHELLSTATUS),0) + +all: shared +.PHONY: shared static +shared: build/$(APP)-shared + ln -sf $(APP)-shared build/$(APP) +static: build/$(APP)-static + ln -sf $(APP)-static build/$(APP) + +PC_FILE := $(shell pkg-config --path libdpdk) +CFLAGS += -O3 $(shell pkg-config --cflags libdpdk) +CFLAGS += -DALLOW_EXPERIMENTAL_API +LDFLAGS_SHARED = $(shell pkg-config --libs libdpdk) +LDFLAGS_STATIC = -Wl,-Bstatic $(shell pkg-config --static --libs libdpdk) + +build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) + +build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) + +build: + @mkdir -p $@ + +.PHONY: clean +clean: + rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared + rmdir --ignore-fail-on-non-empty build + +else # Build using legacy build system + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +CFLAGS += $(WERROR_FLAGS) +CFLAGS += -DALLOW_EXPERIMENTAL_API + +# workaround for a gcc bug with noreturn attribute +# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603 +ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y) +CFLAGS_main.o += -Wno-return-type +endif + +include $(RTE_SDK)/mk/rte.extapp.mk +endif
examples/external_mem/extmem.c new file +461 -0
461 lines not on this page · raw message
examples/external_mem/meson.build new file +12 -0
@@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +# meson file, for building this example as part of a main DPDK build. +# +# To build this example as a standalone application with an already-installed +# DPDK instance, use 'make' + +allow_experimental_apis = true +sources = files( + 'extmem.c' +)
Signature · 1 line
2.17.1
PATCH v3 1/20 mem: add length to memseg list
In reply to Anatoly BurakovPreviously, to calculate length of memory area covered by a memseg list, we would've needed to multiply page size by length of fbarray backing that memseg list. This is not obvious and unnecessarily low level, so store length in the memseg list itself.
--- drivers/bus/pci/linux/pci.c | 2 +- lib/librte_eal/bsdapp/eal/eal_memory.c | 2 ++ lib/librte_eal/common/eal_common_memory.c | 5 ++--- lib/librte_eal/common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/linuxapp/eal/eal_memalloc.c | 3 ++- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +++- 6 files changed, 11 insertions(+), 6 deletions(-)
drivers/bus/pci/linux/pci.c +1 -1
@@ -119,7 +119,7 @@ rte_pci_unmap_device(struct rte_pci_device *dev)static int find_max_end_va (const struct rte_memseg_list *msl,void *arg) { -size_t sz = msl->memseg_arr.len * msl->page_sz; +size_t sz = msl->len;void *end_va =RTE_PTR_ADD (msl->base_va, sz);void **max_va = arg;
lib/librte_eal/bsdapp/eal/eal_memory.c +2 -0
@@ -79,6 +79,7 @@ rte_eal_hugepage_init(void) } msl->base_va = addr; msl->page_sz = page_sz; + msl->len = internal_config.memory; msl->socket_id =0 ;/* populate memsegs. each memseg is 1 page long */
@@ -370,6 +371,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
lib/librte_eal/common/eal_common_memory.c +2 -3
@@ -171,7 +171,7 @@ virt2memseg(const void *addr, const struct rte_memseg_list *msl)/* a memseg list was specified, check if it's the right one */ start = msl->base_va; - end =RTE_PTR_ADD (start, (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr < start || addr >= end)return NULL ;
@@ -194,8 +194,7 @@ virt2memseg_list(const void *addr) msl = &mcfg->memsegs[msl_idx]; start = msl->base_va; - end =RTE_PTR_ADD (start, - (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr >= start && addr < end)break ; }
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -30,6 +30,7 @@ struct rte_memseg_list {uint64_t addr_64;/**< Makes sure addr is always 64-bits */ }; +size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ volatile uint32_t version;/**< version number for multiprocess sync. */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +2 -1
@@ -986,7 +986,7 @@ free_seg_walk(const struct rte_memseg_list *msl, void *arg)int msl_idx, seg_idx, ret, dir_fd =-1 ; start_addr = (uintptr_t ) msl->base_va; - end_addr = start_addr + msl->memseg_arr.len * (size_t )msl->page_sz; + end_addr = start_addr + msl->len;if ((uintptr_t )wa->ms->addr < start_addr || (uintptr_t )wa->ms->addr >= end_addr)
@@ -1472,6 +1472,7 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,return -1 ; } local_msl->base_va = primary_msl->base_va; + local_msl->len = primary_msl->len;return 0 ; }
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -1
@@ -861,6 +861,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
@@ -1369,6 +1370,7 @@ eal_legacy_hugepage_init(void) msl->base_va = addr; msl->page_sz = page_sz; msl->socket_id =0 ; + msl->len = internal_config.memory;/* populate memsegs. each memseg is one page long */ for (cur_seg =0 ; cur_seg < n_segs; cur_seg++) {
@@ -1615,7 +1617,7 @@ eal_legacy_hugepage_init(void)if (msl->memseg_arr.count >0 )continue ;/* this is an unused list, deallocate it */ - mem_sz = (size_t )msl->page_sz * msl->memseg_arr.len; + mem_sz = msl->len;munmap (msl->base_va, mem_sz); msl->base_va =NULL ;
Signature · 1 line
2.17.1
PATCH v3 9/20 malloc: add function to query socket ID of named heap
In reply to Anatoly BurakovWhen we will be creating external heaps, they will have their own "fake" socket ID, so add a function that will map the heap name to its socket ID.
--- lib/librte_eal/common/include/rte_malloc.h | 14 ++++++++ lib/librte_eal/common/rte_malloc.c | 37 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 52 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +14 -0
@@ -263,6 +263,20 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Find socket ID corresponding to a named heap. +* +* @param name +* Heap name to find socket ID for +* @return +* Socket ID in case of success (a non-negative number) +* -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``name`` was NULL +* ENOENT - heap identified by the name ``name`` was not found +*/ +int __rte_experimental +rte_malloc_heap_get_socket (const char *name); +/** * Dump statistics. *
lib/librte_eal/common/rte_malloc.c +37 -0
@@ -8,6 +8,7 @@#include <string.h> #include <sys/queue.h> +#include <rte_errno.h> #include <rte_memcpy.h> #include <rte_memory.h> #include <rte_eal.h>
@@ -183,6 +184,42 @@ rte_malloc_dump_heaps(FILE *f)rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); } +int +rte_malloc_heap_get_socket (const char *name) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +unsigned int idx; +int ret; + +if (name ==NULL || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_read_lock (&mcfg->memory_hotplug_lock); +for (idx =0 ; idx <RTE_MAX_HEAPS ; idx++) { +struct malloc_heap *tmp = &mcfg->malloc_heaps[idx]; + +if (!strncmp (name, tmp->name,RTE_HEAP_NAME_MAX_LEN )) { + heap = tmp; +break ; + } + } + +if (heap !=NULL ) { + ret = heap->socket_id; + }else { + rte_errno =ENOENT ; + ret =-1 ; + } +rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +/* * Print stats on memory type. If type is NULL, info on all types is printed */
lib/librte_eal/rte_eal_version.map +1 -0
@@ -318,6 +318,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v3 10/20 malloc: allow creating malloc heaps
In reply to Anatoly BurakovAdd API to allow creating new malloc heaps. They will be created with socket ID's going above RTE_MAX_NUMA_NODES, to avoid clashing with internal heaps.
--- lib/librte_eal/common/include/rte_malloc.h | 19 ++++++++ lib/librte_eal/common/malloc_heap.c | 30 +++++++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 52 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 105 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +19 -0
@@ -263,6 +263,25 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Creates a new empty malloc heap with a specified name. +* +* @note Heaps created via this call will automatically get assigned a unique +* socket ID, which can be found using ``rte_malloc_heap_get_socket()`` +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on successful creation +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +*/ +int __rte_experimental +rte_malloc_heap_create (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +30 -0
@@ -29,6 +29,10 @@#include "malloc_heap.h" #include "malloc_mp.h" +/* start external socket ID's at a very high number */ +#define CONST_MAX (a, b) (a > b ? a : b)/* RTE_MAX is not a constant */ +#define EXTERNAL_HEAP_MIN_SOCKET_ID (CONST_MAX((1 << 8), RTE_MAX_NUMA_NODES)) +static unsigned check_hugepage_sz (unsigned flags,uint64_t hugepage_sz) {
@@ -1015,6 +1019,32 @@ malloc_heap_dump(struct malloc_heap *heap, FILE *f)rte_spinlock_unlock (&heap->lock); } +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name) +{ +static uint32_t next_socket_id =EXTERNAL_HEAP_MIN_SOCKET_ID ; + +/* prevent overflow. did you really create 2 billion heaps??? */ +if (next_socket_id >INT32_MAX ) { +RTE_LOG (ERR ,EAL ,"Cannot assign new socket ID's\n" ); + rte_errno =ENOSPC ; +return -1 ; + } + +/* initialize empty heap */ + heap->alloc_count =0 ; + heap->first =NULL ; + heap->last =NULL ; +LIST_INIT (heap->free_head); +rte_spinlock_init (&heap->lock); + heap->total_size =0 ; + heap->socket_id = next_socket_id++; + +/* set up name */ +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -33,6 +33,9 @@ void * malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags, size_t align, bool contig); +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +52 -0
61 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -318,6 +318,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_create; rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v3 12/20 malloc: allow adding memory to named heaps
In reply to Anatoly BurakovAdd an API to add externally allocated memory to malloc heap. The memory will be stored in memseg lists like regular DPDK memory. Multiple segments are allowed within a heap. If IOVA table is not provided, IOVA addresses are filled in with RTE_BAD_IOVA.
--- lib/librte_eal/common/include/rte_malloc.h | 39 ++++++++++++ lib/librte_eal/common/malloc_heap.c | 74 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 51 +++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 169 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +39 -0
@@ -263,6 +263,45 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Add memory chunk to a heap with specified name. +* +* @note Multiple memory chunks can be added to the same heap +* +* @note Memory must be previously allocated for DPDK to be able to use it as a +* malloc heap. Failing to do so will result in undefined behavior, up to and +* including segmentation faults. +* +* @note Calling this function will erase any contents already present at the +* supplied memory address. +* +* @param heap_name +* Name of the heap to add memory chunk to +* @param va_addr +* Start of virtual area to add to the heap +* @param len +* Length of virtual area to add to the heap +* @param iova_addrs +* Array of page IOVA addresses corresponding to each page in this memory +* area. Can be NULL, in which case page IOVA addresses will be set to +* RTE_BAD_IOVA. +* @param n_pages +* Number of elements in the iova_addrs array. Ignored if ``iova_addrs`` +* is NULL. +* @param page_sz +* Page size of the underlying memory +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to add memory to a reserved heap +* ENOSPC - no more space in internal config to store a new memory chunk +*/ +int __rte_experimental +rte_malloc_heap_memory_add (const char *heap_name,void *va_addr,size_t len, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +74 -0
80 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -39,6 +39,10 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name);int malloc_heap_destroy (struct malloc_heap *heap); +int +malloc_heap_add_external_memory (struct malloc_heap *heap,void *va_addr, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +51 -0
57 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -321,6 +321,7 @@ EXPERIMENTAL { rte_malloc_heap_create; rte_malloc_heap_destroy; rte_malloc_heap_get_socket; + rte_malloc_heap_memory_add; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v3 15/20 malloc: allow detaching from external memory
In reply to Anatoly BurakovAdd API to detach from existing chunk of external memory in a process.
--- lib/librte_eal/common/include/rte_malloc.h | 27 ++++++++++++++++++++++ lib/librte_eal/common/rte_malloc.c | 27 ++++++++++++++++++---- lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 50 insertions(+), 5 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -315,6 +315,9 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,* @note Memory area must not contain any allocated elements to allow its * removal from the heap * +* @note All other processes must detach from the memory chunk prior to it being +* removed from the heap. +* * @param heap_name * Name of the heap to remove memory from * @param va_addr
@@ -357,6 +360,30 @@ rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len); int __rte_experimentalrte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** +* Detach from a chunk of external memory in secondary process. +* +* @note This function must be called in before any attempt is made to remove +* external memory from the heap in another process. This function does *not* +* need to be called if a call to ``rte_malloc_heap_memory_remove`` will be +* called in current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful detach +* -1 on unsuccessful detach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to detach memory from a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/rte_malloc.c +22 -5
@@ -397,10 +397,11 @@ struct sync_mem_walk_arg {void *va_addr;size_t len;int result; +bool attach; };static int -attach_mem_walk (const struct rte_memseg_list *msl,void *arg) +sync_mem_walk (const struct rte_memseg_list *msl,void *arg) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct sync_mem_walk_arg *wa = arg;
@@ -415,7 +416,10 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg) msl_idx = msl - mcfg->memsegs; found_msl = &mcfg->memsegs[msl_idx]; - ret =rte_fbarray_attach (&found_msl->memseg_arr); +if (wa->attach) + ret =rte_fbarray_attach (&found_msl->memseg_arr); +else + ret =rte_fbarray_detach (&found_msl->memseg_arr);if (ret <0 ) wa->result = -rte_errno;
@@ -426,8 +430,8 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg)return 0 ; } -int -rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +static int +sync_memory (const char *heap_name,void *va_addr,size_t len,bool attach) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct malloc_heap *heap =NULL ;
@@ -461,9 +465,10 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len) wa.va_addr = va_addr; wa.len = len; wa.result = -ENOENT ;/* fail unless explicitly told to succeed */ + wa.attach = attach;/* we're already holding a read lock */ -rte_memseg_list_walk_thread_unsafe (attach_mem_walk, &wa); +rte_memseg_list_walk_thread_unsafe (sync_mem_walk, &wa);if (wa.result <0 ) { rte_errno = -wa.result;
@@ -476,6 +481,18 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)return ret; } +int +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, true); +} + +int +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, false); +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -323,6 +323,7 @@ EXPERIMENTAL { rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; rte_malloc_heap_memory_attach; + rte_malloc_heap_memory_detach; rte_malloc_heap_memory_remove; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v3 4/20 mem: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
This changes the semantics of what we understand by "socket ID", so document the change in the release notes.
--- doc/guides/rel_notes/release_18_11.rst | 7 +++++++ lib/librte_eal/common/eal_common_memzone.c | 8 +++++--- lib/librte_eal/common/malloc_heap.c | 2 +- lib/librte_eal/common/rte_malloc.c | 4 ---- 4 files changed, 13 insertions(+), 8 deletions(-)
doc/guides/rel_notes/release_18_11.rst +7 -0
@@ -98,6 +98,13 @@ API Changes users of memseg-walk-related functions, as they will now have to skip externally allocated segments in most cases if the intent is to only iterate over internal DPDK memory. + - ``socket_id`` parameter across the entire DPDK has gained additional + meaning, as some socket ID's will now be representing externally allocated + memory. No changes will be required for existing code as backwards + compatibility will be kept, and those who do not use this feature will not + see these extra socket ID's. Any new API's must not check socket ID + parameters themselves, and must instead leave it to the memory subsystem to + decide whether socket ID is a valid one. ABI Changes -----------
lib/librte_eal/common/eal_common_memzone.c +5 -3
@@ -120,13 +120,15 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,return NULL ; } -if ((socket_id !=SOCKET_ID_ANY ) && - (socket_id >=RTE_MAX_NUMA_NODES || socket_id <0 )) { +if ((socket_id !=SOCKET_ID_ANY ) && socket_id <0 ) { rte_errno =EINVAL ;return NULL ; } -if (!rte_eal_has_hugepages ()) +/* only set socket to SOCKET_ID_ANY if we aren't allocating for an +* external heap. +*/ +if (!rte_eal_has_hugepages () && socket_id <RTE_MAX_NUMA_NODES ) socket_id =SOCKET_ID_ANY ; contig = (flags &RTE_MEMZONE_IOVA_CONTIG ) !=0 ;
lib/librte_eal/common/malloc_heap.c +1 -1
@@ -647,7 +647,7 @@ malloc_heap_alloc(const char *type, size_t size, int socket_arg,if (size ==0 || (align && !rte_is_power_of_2 (align)))return NULL ; -if (!rte_eal_has_hugepages ()) +if (!rte_eal_has_hugepages () && socket_arg <RTE_MAX_NUMA_NODES ) socket_arg =SOCKET_ID_ANY ;if (socket_arg ==SOCKET_ID_ANY )
lib/librte_eal/common/rte_malloc.c +0 -4
@@ -47,10 +47,6 @@ rte_malloc_socket(const char *type, size_t size, unsigned int align,if (!rte_eal_has_hugepages ()) socket_arg =SOCKET_ID_ANY ; -/* Check socket parameter */ -if (socket_arg >=RTE_MAX_NUMA_NODES ) -return NULL ; -return malloc_heap_alloc (type, size, socket_arg,0 , align ==0 ?1 : align,0 , false); }
Signature · 1 line
2.17.1
PATCH v3 8/20 malloc: add name to malloc heaps
In reply to Anatoly BurakovWe will need to refer to external heaps in some way. While we use heap ID's internally, for external API use it has to be something more user-friendly. So, we will be using a string to uniquely identify a heap.
--- lib/librte_eal/common/include/rte_malloc_heap.h | 2 ++ lib/librte_eal/common/malloc_heap.c | 15 ++++++++++++++- lib/librte_eal/common/rte_malloc.c | 1 + 3 files changed, 17 insertions(+), 1 deletion(-)
lib/librte_eal/common/include/rte_malloc_heap.h +2 -0
@@ -12,6 +12,7 @@/* Number of free lists per heap, grouped by size. */ #define RTE_HEAP_NUM_FREELISTS 13 +#define RTE_HEAP_NAME_MAX_LEN 32/* dummy definition, for pointers */ struct malloc_elem ;
@@ -28,6 +29,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size;unsigned int socket_id; + char name[RTE_HEAP_NAME_MAX_LEN]; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +14 -1
@@ -127,7 +127,6 @@ malloc_add_seg(const struct rte_memseg_list *msl,malloc_heap_add_memory (heap, found_msl, ms->addr, len); heap->total_size += len; - heap->socket_id = msl->socket_id;RTE_LOG (DEBUG ,EAL ,"Added %zuM to heap on socket %i\n" , len >>20 , msl->socket_id);
@@ -1020,6 +1019,20 @@ intrte_eal_malloc_heap_init (void) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +unsigned int i; + +/* assign names to default DPDK heaps */ +for (i =0 ; i <rte_socket_count (); i++) { +struct malloc_heap *heap = &mcfg->malloc_heaps[i]; +char heap_name[RTE_HEAP_NAME_MAX_LEN ]; +int socket_id =rte_socket_id_by_idx (i); + +snprintf (heap_name,sizeof (heap_name) -1 , +"socket_%i" , socket_id); +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); + heap->socket_id = socket_id; + } +if (register_mp_requests ()) {RTE_LOG (ERR ,EAL ,"Couldn't register malloc multiprocess actions\n" );
lib/librte_eal/common/rte_malloc.c +1 -0
@@ -202,6 +202,7 @@ rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)malloc_heap_get_stats (heap, &sock_stats);fprintf (f,"Heap id:%u\n" , heap_id); +fprintf (f,"\tHeap name:%s\n" , heap->name);fprintf (f,"\tHeap_size:%zu,\n" , sock_stats.heap_totalsz_bytes);fprintf (f,"\tFree_size:%zu,\n" , sock_stats.heap_freesz_bytes);fprintf (f,"\tAlloc_size:%zu,\n" , sock_stats.heap_allocsz_bytes);
Signature · 1 line
2.17.1
Re: [PATCH v3 17/20] examples: add external memory example app
In reply to Anatoly BurakovHi Anatoly
Quoted 12 lines
Introduce an example application demonstrating the use of external memory support. This is a simple application based on skeleton app, but instead of using internal DPDK memory, it is using externally allocated memory.
The RX/TX and init path is a carbon-copy of skeleton app, with no modifications whatseoever. The only difference is an additional init stage to allocate memory and create a heap for it, and the socket ID supplied to the mempool initialization function. The memory used by this app is hugepage memory allocated anonymously.
Quoted 4 lines
Anonymous hugepage memory will not be allocated in a NUMA-aware fashion, so there is a chance of performance degradation when using this app, but given that kernel usually gives hugepages on local socket first, this should not be a problem in most cases.
Do we need a new sample app just for that?
Couldn't it be added into testpmd, same, as we have now 'mp-anon' to use mempool over anonymous memory?
Konstantin
Quoted 566 lines
Signed-off-by Anatoly Burakov <[email protected]>--- examples/external_mem/Makefile | 62 ++++ examples/external_mem/extmem.c | 461 ++++++++++++++++++++++++++++++ examples/external_mem/meson.build | 12 + 3 files changed, 535 insertions(+) create mode 100644 examples/external_mem/Makefile create mode 100644 examples/external_mem/extmem.c create mode 100644 examples/external_mem/meson.buildexamples/external_mem/Makefile new file +62
-0@@ -0,0 +1,62 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2018 Intel Corporation + +# binary name +APP = extmem + +# all source are stored in SRCS-y +SRCS-y := extmem.c + +# Build using pkg-config variables if possible +$(shell pkg-config --exists libdpdk) +ifeq ($(.SHELLSTATUS),0) + +all: shared +.PHONY: shared static +shared: build/$(APP)-shared + ln -sf $(APP)-shared build/$(APP) +static: build/$(APP)-static + ln -sf $(APP)-static build/$(APP) + +PC_FILE := $(shell pkg-config --path libdpdk) +CFLAGS += -O3 $(shell pkg-config --cflags libdpdk) +CFLAGS += -DALLOW_EXPERIMENTAL_API +LDFLAGS_SHARED = $(shell pkg-config --libs libdpdk) +LDFLAGS_STATIC = -Wl,-Bstatic $(shell pkg-config --static --libs libdpdk) + +build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) + +build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) + +build: + @mkdir -p $@ + +.PHONY: clean +clean: + rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared + rmdir --ignore-fail-on-non-empty build + +else # Build using legacy build system + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +CFLAGS += $(WERROR_FLAGS) +CFLAGS += -DALLOW_EXPERIMENTAL_API + +# workaround for a gcc bug with noreturn attribute +# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603 +ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y) +CFLAGS_main.o += -Wno-return-type +endif + +include $(RTE_SDK)/mk/rte.extapp.mk +endifexamples/external_mem/extmem.c new file +461
-0@@ -0,0 +1,461 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* Copyright(c) 2010-2018 Intel Corporation +*/ + +#include <stdint.h> +#include <inttypes.h> +#include <stdbool.h> +#include <unistd.h> +#include <sys/mman.h> + +#include <rte_eal.h> +#include <rte_ethdev.h> +#include <rte_cycles.h> +#include <rte_lcore.h> +#include <rte_mbuf.h> +#include <rte_malloc.h> +#include <rte_memory.h> +#include <rte_vfio.h> + +#define RX_RING_SIZE 1024 +#define TX_RING_SIZE 1024 + +#define NUM_MBUFS 8191 +#define MBUF_CACHE_SIZE 250 +#define BURST_SIZE 32 +#define EXTMEM_HEAP_NAME "extmem" + +static const struct rte_eth_conf port_conf_default = { + .rxmode = { + .max_rx_pkt_len =ETHER_MAX_LEN , + }, +}; + +/* extmem.c: Basic DPDK skeleton forwarding example using external memory. */ + +/* +* Initializes a given port using global settings and with the RX buffers +* coming from the mbuf_pool passed as a parameter. +*/ +static inline int +port_init (uint16_t port,struct rte_mempool *mbuf_pool) +{ +struct rte_eth_conf port_conf = port_conf_default; +const uint16_t rx_rings =1 , tx_rings =1 ; +uint16_t nb_rxd =RX_RING_SIZE ; +uint16_t nb_txd =TX_RING_SIZE ; +int retval; +uint16_t q; +struct rte_eth_dev_info dev_info; +struct rte_eth_txconf txconf; + +if (!rte_eth_dev_is_valid_port (port)) +return -1 ; + +rte_eth_dev_info_get (port, &dev_info); +if (dev_info.tx_offload_capa &DEV_TX_OFFLOAD_MBUF_FAST_FREE ) + port_conf.txmode.offloads |= +DEV_TX_OFFLOAD_MBUF_FAST_FREE ; + +/* Configure the Ethernet device. */ + retval =rte_eth_dev_configure (port, rx_rings, tx_rings, &port_conf); +if (retval !=0 ) +return retval; + + retval =rte_eth_dev_adjust_nb_rx_tx_desc (port, &nb_rxd, &nb_txd); +if (retval !=0 ) +return retval; + +/* Allocate and set up 1 RX queue per Ethernet port. */ +for (q =0 ; q < rx_rings; q++) { + retval =rte_eth_rx_queue_setup (port, q, nb_rxd, +rte_eth_dev_socket_id (port),NULL , mbuf_pool); +if (retval <0 ) +return retval; + } + + txconf = dev_info.default_txconf; + txconf.offloads = port_conf.txmode.offloads; +/* Allocate and set up 1 TX queue per Ethernet port. */ +for (q =0 ; q < tx_rings; q++) { + retval =rte_eth_tx_queue_setup (port, q, nb_txd, +rte_eth_dev_socket_id (port), &txconf); +if (retval <0 ) +return retval; + } + +/* Start the Ethernet port. */ + retval =rte_eth_dev_start (port); +if (retval <0 ) +return retval; + +/* Display the port MAC address. */ +struct ether_addr addr; +rte_eth_macaddr_get (port, &addr); +printf ("Port %u MAC: %02" PRIx8" %02" PRIx8" %02" PRIx8 +" %02" PRIx8" %02" PRIx8" %02" PRIx8"\n" , + port, + addr.addr_bytes[0 ], addr.addr_bytes[1 ], + addr.addr_bytes[2 ], addr.addr_bytes[3 ], + addr.addr_bytes[4 ], addr.addr_bytes[5 ]); + +/* Enable RX in promiscuous mode for the Ethernet device. */ +rte_eth_promiscuous_enable (port); + +return 0 ; +} + +/* +* The lcore main. This is the main thread that does the work, reading from +* an input port and writing to an output port. +*/ +static __attribute__((noreturn))void +lcore_main (void ) +{ +uint16_t port; + +/* +* Check that the port is on the same NUMA node as the polling thread +* for best performance. +*/ + RTE_ETH_FOREACH_DEV(port) + if (rte_eth_dev_socket_id(port) > 0 && + rte_eth_dev_socket_id(port) != + (int)rte_socket_id()) + printf("WARNING, port %u is on remote NUMA node to " +"polling thread.\n\tPerformance will " + "not be optimal.\n", port); + +printf ("\nCore %u forwarding packets. [Ctrl+C to quit]\n" , +rte_lcore_id ()); + +/* Run until the application is quit or killed. */ +for (;;) { +/* +* Receive packets on a port and forward them on the paired +* port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc. +*/ +RTE_ETH_FOREACH_DEV (port) { + +/* Get burst of RX packets, from first port of pair. */ +struct rte_mbuf *bufs[BURST_SIZE ]; +const uint16_t nb_rx =rte_eth_rx_burst (port,0 , + bufs,BURST_SIZE ); + +if (unlikely (nb_rx ==0 )) +continue ; + +/* Send burst of TX packets, to second port of pair. */ +const uint16_t nb_tx =rte_eth_tx_burst (port ^1 ,0 , + bufs, nb_rx); + +/* Free any unsent packets. */ +if (unlikely (nb_tx < nb_rx)) { +uint16_t buf; +for (buf = nb_tx; buf < nb_rx; buf++) +rte_pktmbuf_free (bufs[buf]); + } + } + } +} + +/* extremely pessimistic estimation of memory required to create a mempool */ +static int +calc_mem_size (uint32_t nb_ports,uint32_t nb_mbufs_per_port, +uint32_t mbuf_sz,size_t pgsz,size_t *out) +{ +uint32_t nb_mbufs = nb_ports * nb_mbufs_per_port; +uint64_t total_mem, mbuf_mem, obj_sz; + +/* there is no good way to predict how much space the mempool will +* occupy because it will allocate chunks on the fly, and some of those +* will come from default DPDK memory while some will come from our +* external memory, so just assume 16MB will be enough for everyone. +*/ +uint64_t hdr_mem =16 <<20 ; + + obj_sz =rte_mempool_calc_obj_size (mbuf_sz,0 ,NULL ); +if (rte_eal_iova_mode () ==RTE_IOVA_VA ) { +/* contiguous - no need to account for page boundaries */ + mbuf_mem = nb_mbufs * obj_sz; + }else { +/* account for possible non-contiguousness */ +unsigned int n_pages, mbuf_per_pg, leftover; + + mbuf_per_pg = pgsz / obj_sz; + leftover = (nb_mbufs % mbuf_per_pg) >0 ; + n_pages = (nb_mbufs / mbuf_per_pg) + leftover; + + mbuf_mem = n_pages * pgsz; + } + + total_mem =RTE_ALIGN (hdr_mem + mbuf_mem, pgsz); + +if (total_mem >SIZE_MAX ) { +printf ("Memory size too big\n" ); +return -1 ; + } + *out = (size_t )total_mem; + +return 0 ; +} + +static inline uint32_t +bsf64 (uint64_t v) +{ +return (uint32_t )__builtin_ctzll (v); +} + +static inline uint32_t +log2_u64 (uint64_t v) +{ +if (v ==0 ) +return 0 ; + v =rte_align64pow2 (v); +return bsf64 (v); +} + +#ifndef MAP_HUGE_SHIFT +#define HUGE_SHIFT 26 +#else +#define HUGE_SHIFT MAP_HUGE_SHIFT +#endif + +static int +pagesz_flags (uint64_t page_sz) +{ +/* as per mmap() manpage, all page sizes are log2 of page size +* shifted by MAP_HUGE_SHIFT +*/ +int log2 =log2_u64 (page_sz); +return log2 <<HUGE_SHIFT ; +} + +static void * +alloc_mem (size_t memsz,size_t pgsz) +{ +void *addr; +int flags; + +/* allocate anonymous hugepages */ + flags =MAP_ANONYMOUS |MAP_PRIVATE |MAP_HUGETLB |pagesz_flags (pgsz); + + addr =mmap (NULL , memsz,PROT_READ |PROT_WRITE , flags,-1 ,0 ); +if (addr ==MAP_FAILED ) +return NULL ; + +return addr; +} + +struct extmem_param { +void *addr; +size_t len; +size_t pgsz; +rte_iova_t *iova_table; +unsigned int iova_table_len; +}; + +static int +create_extmem (uint32_t nb_ports,uint32_t nb_mbufs_per_port,uint32_t mbuf_sz, +struct extmem_param *param) +{ +uint64_t pgsizes[] = {RTE_PGSIZE_2M ,RTE_PGSIZE_1G ,/* x86_64, ARM */ +RTE_PGSIZE_16M ,RTE_PGSIZE_16G };/* POWER */ +unsigned int n_pages, cur_page, pgsz_idx; +size_t mem_sz, offset, cur_pgsz; +bool vfio_supported = true; +rte_iova_t *iovas =NULL ; +void *addr; +int ret; + +for (pgsz_idx =0 ; pgsz_idx <RTE_DIM (pgsizes); pgsz_idx++) { +/* skip anything that is too big */ +if (pgsizes[pgsz_idx] >SIZE_MAX ) +continue ; + + cur_pgsz = pgsizes[pgsz_idx]; + + ret =calc_mem_size (nb_ports, nb_mbufs_per_port, + mbuf_sz, cur_pgsz, &mem_sz); +if (ret <0 ) { +printf ("Cannot calculate memory size\n" ); +return -1 ; + } + +/* allocate our memory */ + addr =alloc_mem (mem_sz, cur_pgsz); + +/* if we couldn't allocate memory with a specified page size, +* that doesn't mean we can't do it with other page sizes, so +* try another one. +*/ +if (addr ==NULL ) +continue ; + +/* store IOVA addresses for every page in this memory area */ + n_pages = mem_sz / cur_pgsz; + + iovas =malloc (sizeof (*iovas) * n_pages); + +if (iovas ==NULL ) { +printf ("Cannot allocate memory for iova addresses\n" ); + goto fail; + } + +/* populate IOVA table */ +for (cur_page =0 ; cur_page < n_pages; cur_page++) { +rte_iova_t iova; +void *cur; + + offset = cur_pgsz * cur_page; + cur =RTE_PTR_ADD (addr, offset); + + iova = (uintptr_t )rte_mem_virt2iova (cur); + + iovas[cur_page] = iova; + +if (vfio_supported) { +/* map memory for DMA */ + ret =rte_vfio_dma_map ((uintptr_t )addr, + iova, cur_pgsz); +if (ret <0 ) { +/* +* ENODEV means VFIO is not initialized +* ENOTSUP means current IOMMU mode +* doesn't support mapping +* both cases are not an error +*/ +if (rte_errno ==ENOTSUP || + rte_errno ==ENODEV ) +/* VFIO is unsupported, don't +* try again. +*/ + vfio_supported = false; +else +/* this is an actual error */ + goto fail; + } + } + } + +break ; + } +/* if we couldn't allocate anything */ +if (iovas ==NULL ) +return -1 ; + + param->addr = addr; + param->len = mem_sz; + param->pgsz = cur_pgsz; + param->iova_table = iovas; + param->iova_table_len = n_pages; + +return 0 ; +fail: +if (iovas) +free (iovas); +if (addr) +munmap (addr, mem_sz); + +return -1 ; +} + +static int +setup_extmem (uint32_t nb_ports,uint32_t nb_mbufs_per_port,uint32_t mbuf_sz) +{ +struct extmem_param param; +int ret; + +/* create our heap */ + ret =rte_malloc_heap_create (EXTMEM_HEAP_NAME ); +if (ret <0 ) { +printf ("Cannot create heap\n" ); +return -1 ; + } + + ret =create_extmem (nb_ports, nb_mbufs_per_port, mbuf_sz, ¶m); +if (ret <0 ) { +printf ("Cannot create memory area\n" ); +return -1 ; + } + +/* we now have a valid memory area, so add it to heap */ + ret =rte_malloc_heap_memory_add (EXTMEM_HEAP_NAME , + param.addr, param.len, param.iova_table, + param.iova_table_len, param.pgsz); + +/* not needed any more */ +free (param.iova_table); + +if (ret <0 ) { +printf ("Cannot add memory to heap\n" ); +munmap (param.addr, param.len); +return -1 ; + } + +printf ("Allocated %zuMB of memory\n" , param.len >>20 ); + +/* success */ +return 0 ; +} + + +/* +* The main function, which does initialization and calls the per-lcore +* functions. +*/ +int +main (int argc,char *argv[]) +{ +struct rte_mempool *mbuf_pool; +unsigned int nb_ports; +int socket_id; +uint16_t portid; +uint32_t nb_mbufs_per_port, mbuf_sz; + +/* Initialize the Environment Abstraction Layer (EAL). */ +int ret =rte_eal_init (argc, argv); +if (ret <0 ) +rte_exit (EXIT_FAILURE ,"Error with EAL initialization\n" ); + + argc -= ret; + argv += ret; + +/* Check that there is an even number of ports to send/receive on. */ + nb_ports =rte_eth_dev_count_avail (); +if (nb_ports <2 || (nb_ports &1 )) +rte_exit (EXIT_FAILURE ,"Error: number of ports must be even\n" ); + + nb_mbufs_per_port =NUM_MBUFS ; + mbuf_sz =RTE_MBUF_DEFAULT_BUF_SIZE ; + +if (setup_extmem (nb_ports, nb_mbufs_per_port, mbuf_sz) <0 ) +rte_exit (EXIT_FAILURE ,"Error: cannot set up external memory\n" ); + +/* retrieve socket ID for our heap */ + socket_id =rte_malloc_heap_get_socket (EXTMEM_HEAP_NAME ); +if (socket_id <0 ) +rte_exit (EXIT_FAILURE ,"Invalid socket for external heap\n" ); + +/* Creates a new mempool in memory to hold the mbufs. */ + mbuf_pool =rte_pktmbuf_pool_create ("MBUF_POOL" , + nb_mbufs_per_port * nb_ports,MBUF_CACHE_SIZE ,0 , + mbuf_sz, socket_id); + +if (mbuf_pool ==NULL ) +rte_exit (EXIT_FAILURE ,"Cannot create mbuf pool\n" ); + +/* Initialize all ports. */ + RTE_ETH_FOREACH_DEV(portid) + if (port_init(portid, mbuf_pool) != 0) + rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n", + portid); + +if (rte_lcore_count () >1 ) +printf ("\nWARNING: Too many lcores enabled. Only 1 used.\n" ); + +/* Call lcore_main on the master core only. */ +lcore_main (); + +return 0 ; +}examples/external_mem/meson.build new file +12
-1@@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +# meson file, for building this example as part of a main DPDK build. +# +# To build this example as a standalone application with an already-installed +# DPDK instance, use 'make' + +allow_experimental_apis = true +sources = files( + 'extmem.c' +) --2.17.1
Re: [PATCH v3 17/20] examples: add external memory example app
Quoted 20 lines · Konstantin
Hi Anatoly
Quoted 12 lines
Introduce an example application demonstrating the use of external memory support. This is a simple application based on skeleton app, but instead of using internal DPDK memory, it is using externally allocated memory.
The RX/TX and init path is a carbon-copy of skeleton app, with no modifications whatseoever. The only difference is an additional init stage to allocate memory and create a heap for it, and the socket ID supplied to the mempool initialization function. The memory used by this app is hugepage memory allocated anonymously.
Quoted 4 lines
Anonymous hugepage memory will not be allocated in a NUMA-aware fashion, so there is a chance of performance degradation when using this app, but given that kernel usually gives hugepages on local socket first, this should not be a problem in most cases.
Quoted 5 lines
Do we need a new sample app just for that?
Couldn't it be added into testpmd, same, as we have now 'mp-anon' to use mempool over anonymous memory?
Konstantin
Hi Konstantin,
The reason i made a new sample app and not put it in testpmd is that i felt putting it in testpmd hurts discoverability of sample code. However, i can do either, or both?
Signature · 2 lines
Thanks, Anatoly
v4 cover Support externally allocated memory in DPDK
In reply to Anatoly BurakovThis is a proposal to enable using externally allocated memory in DPDK.
In a nutshell, here is what is being done here:
- Index internal malloc heaps by NUMA node index, rather than NUMA node itself (external heaps will have ID's in order of creation)
- Add identifier string to malloc heap, to uniquely identify it
- Each new heap will receive a unique socket ID that will be used by allocator to decide from which heap (internal or external) to allocate requested amount of memory
- Allow creating named heaps and add/remove memory to/from those heaps
- Allocate memseg lists at runtime, to keep track of IOVA addresses of externally allocated memory
- If IOVA addresses aren't provided, use RTE_BAD_IOVA
- Allow malloc and memzones to allocate from external heaps
- Allow other data structures to allocate from externall heaps
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
The general approach is to create heap and add memory into it. For any other process wishing to use the same memory, said memory must first be attached (otherwise some things will not work).
A design decision was made to make multiprocess synchronization a manual process. Due to underlying issues with attaching to fbarrays in secondary processes, this design was deemed to be better because we don't want to fail to create external heap in the primary because something in the secondary has failed when in fact we may not eve have wanted this memory to be accessible in the secondary in the first place.
Using external memory in multiprocess is *hard*, because not only memory space needs to be preallocated, but it also needs to be attached in each process to allow other processes to access the page table. The attach API call may or may not succeed, depending on memory layout, for reasons similar to other multiprocess failures. This is treated as a "known issue" for this release.
Creating and destroying heaps is currently restricted to primary processes, because we need to keep track of all socket ID's we've ever used to prevent their reuse, and obviously different processes would have kept different socket ID counters, and it isn't important enough to put into shared memory. This means that secondary processes will not be able to create new heaps. If this use case is important enough, we can put the max socket ID into shared memory, or allow socket ID reuse (which i do not think is a good idea because it has the potential to make things harder to debug).
v4 -> v3 changes:
- Dropped sample application in favor of new testpmd flag
- Added new flag to testpmd, with four options of mempool allocation
- Added new API to check if a socket ID belongs to an external heap
- Adjusted malloc and mempool code to not make any assumptions about IOVA-contiguousness when dealing with externally allocated memory
v3 -> v2 changes:
- Rebase on top of latest master
- Clarifications added to mempool code as per Andrew Rynchenko's comments
v2 -> v1 changes:
- Fixed NULL dereference on heap socket ID lookup
- Fixed memseg offset calculation on adding memory to heap
- Improved unit test to test for above bugfixes
- Restricted heap creation to primary processes only
- Added sample application
- Added documentation
RFC -> v1 changes:
- Removed the "named heaps" API, allocate using fake socket ID instead
- Added multiprocess support
- Everything is now thread-safe
- Numerous bugfixes and API improvements
Anatoly Burakov (20):
mem: add length to memseg list
mem: allow memseg lists to be marked as external
malloc: index heaps using heap ID rather than NUMA node
mem: do not check for invalid socket ID
flow_classify: do not check for invalid socket ID
pipeline: do not check for invalid socket ID
sched: do not check for invalid socket ID
malloc: add name to malloc heaps
malloc: add function to query socket ID of named heap
malloc: add function to check if socket is external
malloc: allow creating malloc heaps
malloc: allow destroying heaps
malloc: allow adding memory to named heaps
malloc: allow removing memory from named heaps
malloc: allow attaching to external memory chunks
malloc: allow detaching from external memory
test: add unit tests for external memory support app/testpmd: add support for external memory
doc: add external memory feature to the release notes
doc: add external memory feature to programmer's guide
app/test-pmd/config.c | 21 +- app/test-pmd/parameters.c | 23 +- app/test-pmd/testpmd.c | 337 +++++++++++++- app/test-pmd/testpmd.h | 13 +- config/common_base | 1 + config/rte_config.h | 1 + .../prog_guide/env_abstraction_layer.rst | 38 ++ doc/guides/rel_notes/deprecation.rst | 15 - doc/guides/rel_notes/release_18_11.rst | 24 +- doc/guides/testpmd_app_ug/run_app.rst | 12 + drivers/bus/fslmc/fslmc_vfio.c | 7 +- drivers/bus/pci/linux/pci.c | 2 +- drivers/net/mlx4/mlx4_mr.c | 3 + drivers/net/mlx5/mlx5.c | 5 +- drivers/net/mlx5/mlx5_mr.c | 3 + drivers/net/virtio/virtio_user/vhost_kernel.c | 5 +- lib/librte_eal/bsdapp/eal/Makefile | 2 +- lib/librte_eal/bsdapp/eal/eal.c | 3 + lib/librte_eal/bsdapp/eal/eal_memory.c | 9 +- lib/librte_eal/common/eal_common_memory.c | 8 +- lib/librte_eal/common/eal_common_memzone.c | 8 +- .../common/include/rte_eal_memconfig.h | 6 +- lib/librte_eal/common/include/rte_malloc.h | 198 +++++++++ .../common/include/rte_malloc_heap.h | 3 + lib/librte_eal/common/include/rte_memory.h | 9 + lib/librte_eal/common/malloc_elem.c | 10 +- lib/librte_eal/common/malloc_heap.c | 300 +++++++++++-- lib/librte_eal/common/malloc_heap.h | 17 + lib/librte_eal/common/rte_malloc.c | 420 +++++++++++++++++- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 10 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 12 +- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +- lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 +- lib/librte_eal/meson.build | 2 +- lib/librte_eal/rte_eal_version.map | 8 + lib/librte_flow_classify/rte_flow_classify.c | 3 +- lib/librte_mempool/rte_mempool.c | 57 ++- lib/librte_pipeline/rte_pipeline.c | 3 +- lib/librte_sched/rte_sched.c | 2 +- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 ++++++++++++++++ test/test/test_malloc.c | 3 + test/test/test_memzone.c | 3 + 46 files changed, 1897 insertions(+), 137 deletions(-) create mode 100644 test/test/test_external_mem.c
Signature · 1 line
2.17.1
PATCH v4 4/20 mem: do not check for invalid socket ID
We will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
This changes the semantics of what we understand by "socket ID", so document the change in the release notes.
--- doc/guides/rel_notes/release_18_11.rst | 7 +++++++ lib/librte_eal/common/eal_common_memzone.c | 8 +++++--- lib/librte_eal/common/malloc_heap.c | 2 +- lib/librte_eal/common/rte_malloc.c | 4 ---- 4 files changed, 13 insertions(+), 8 deletions(-)
doc/guides/rel_notes/release_18_11.rst +7 -0
@@ -98,6 +98,13 @@ API Changes users of memseg-walk-related functions, as they will now have to skip externally allocated segments in most cases if the intent is to only iterate over internal DPDK memory. + - ``socket_id`` parameter across the entire DPDK has gained additional + meaning, as some socket ID's will now be representing externally allocated + memory. No changes will be required for existing code as backwards + compatibility will be kept, and those who do not use this feature will not + see these extra socket ID's. Any new API's must not check socket ID + parameters themselves, and must instead leave it to the memory subsystem to + decide whether socket ID is a valid one. ABI Changes -----------
lib/librte_eal/common/eal_common_memzone.c +5 -3
@@ -120,13 +120,15 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,return NULL ; } -if ((socket_id !=SOCKET_ID_ANY ) && - (socket_id >=RTE_MAX_NUMA_NODES || socket_id <0 )) { +if ((socket_id !=SOCKET_ID_ANY ) && socket_id <0 ) { rte_errno =EINVAL ;return NULL ; } -if (!rte_eal_has_hugepages ()) +/* only set socket to SOCKET_ID_ANY if we aren't allocating for an +* external heap. +*/ +if (!rte_eal_has_hugepages () && socket_id <RTE_MAX_NUMA_NODES ) socket_id =SOCKET_ID_ANY ; contig = (flags &RTE_MEMZONE_IOVA_CONTIG ) !=0 ;
lib/librte_eal/common/malloc_heap.c +1 -1
@@ -647,7 +647,7 @@ malloc_heap_alloc(const char *type, size_t size, int socket_arg,if (size ==0 || (align && !rte_is_power_of_2 (align)))return NULL ; -if (!rte_eal_has_hugepages ()) +if (!rte_eal_has_hugepages () && socket_arg <RTE_MAX_NUMA_NODES ) socket_arg =SOCKET_ID_ANY ;if (socket_arg ==SOCKET_ID_ANY )
lib/librte_eal/common/rte_malloc.c +0 -4
@@ -47,10 +47,6 @@ rte_malloc_socket(const char *type, size_t size, unsigned int align,if (!rte_eal_has_hugepages ()) socket_arg =SOCKET_ID_ANY ; -/* Check socket parameter */ -if (socket_arg >=RTE_MAX_NUMA_NODES ) -return NULL ; -return malloc_heap_alloc (type, size, socket_arg,0 , align ==0 ?1 : align,0 , false); }
Signature · 1 line
2.17.1
PATCH v4 6/20 pipeline: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_pipeline/rte_pipeline.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_pipeline/rte_pipeline.c +1 -2
@@ -178,8 +178,7 @@ rte_pipeline_check_params(struct rte_pipeline_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_LOG (ERR ,PIPELINE ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH v4 13/20 malloc: allow adding memory to named heaps
In reply to Anatoly BurakovAdd an API to add externally allocated memory to malloc heap. The memory will be stored in memseg lists like regular DPDK memory. Multiple segments are allowed within a heap. If IOVA table is not provided, IOVA addresses are filled in with RTE_BAD_IOVA.
--- lib/librte_eal/common/include/rte_malloc.h | 39 ++++++++++++ lib/librte_eal/common/malloc_heap.c | 74 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 51 +++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 169 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +39 -0
@@ -263,6 +263,45 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Add memory chunk to a heap with specified name. +* +* @note Multiple memory chunks can be added to the same heap +* +* @note Memory must be previously allocated for DPDK to be able to use it as a +* malloc heap. Failing to do so will result in undefined behavior, up to and +* including segmentation faults. +* +* @note Calling this function will erase any contents already present at the +* supplied memory address. +* +* @param heap_name +* Name of the heap to add memory chunk to +* @param va_addr +* Start of virtual area to add to the heap +* @param len +* Length of virtual area to add to the heap +* @param iova_addrs +* Array of page IOVA addresses corresponding to each page in this memory +* area. Can be NULL, in which case page IOVA addresses will be set to +* RTE_BAD_IOVA. +* @param n_pages +* Number of elements in the iova_addrs array. Ignored if ``iova_addrs`` +* is NULL. +* @param page_sz +* Page size of the underlying memory +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to add memory to a reserved heap +* ENOSPC - no more space in internal config to store a new memory chunk +*/ +int __rte_experimental +rte_malloc_heap_memory_add (const char *heap_name,void *va_addr,size_t len, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +74 -0
80 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -39,6 +39,10 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name);int malloc_heap_destroy (struct malloc_heap *heap); +int +malloc_heap_add_external_memory (struct malloc_heap *heap,void *va_addr, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +51 -0
57 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -321,6 +321,7 @@ EXPERIMENTAL { rte_malloc_heap_create; rte_malloc_heap_destroy; rte_malloc_heap_get_socket; + rte_malloc_heap_memory_add; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v4 17/20 test: add unit tests for external memory support
In reply to Anatoly BurakovAdd simple unit tests to test external memory support. The tests are pretty basic and mostly consist of checking if invalid API calls are handled correctly, plus a simple allocation/deallocation test for malloc and memzone.
--- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 ++++++++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 test/test/test_external_mem.c
test/test/Makefile +1 -0
@@ -71,6 +71,7 @@ SRCS-y += test_bitmap.c SRCS-y += test_reciprocal_division.c SRCS-y += test_reciprocal_division_perf.c SRCS-y += test_fbarray.c +SRCS-y += test_external_mem.c SRCS-y += test_ring.c SRCS-y += test_ring_perf.c
test/test/autotest_data.py +10 -4
@@ -477,10 +477,16 @@ "Report": None, }, { - "Name": "Fbarray autotest", - "Command": "fbarray_autotest", - "Func": default_autotest, - "Report": None, + "Name": "Fbarray autotest", + "Command": "fbarray_autotest", + "Func": default_autotest, + "Report": None, + }, + { + "Name": "External memory autotest", + "Command": "external_mem_autotest", + "Func": default_autotest, + "Report": None, }, # #Please always keep all dump tests at the end and together!
test/test/meson.build +1 -0
@@ -155,6 +155,7 @@ test_names = [ 'eventdev_common_autotest', 'eventdev_octeontx_autotest', 'eventdev_sw_autotest', + 'external_mem_autotest', 'func_reentrancy_autotest', 'flow_classify_autotest', 'hash_scaling_autotest',
test/test/test_external_mem.c new file +389 -0
389 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v4 19/20 doc: add external memory feature to the release notes
In reply to Anatoly BurakovDocument the addition of external memory support to DPDK.
--- doc/guides/rel_notes/release_18_11.rst | 5 +++++ 1 file changed, 5 insertions(+)
doc/guides/rel_notes/release_18_11.rst +5 -0
@@ -67,6 +67,11 @@ New Features SR-IOV option in Hyper-V and Azure. This is an alternative to the previous vdev_netvsc, tap, and failsafe drivers combination. +* **Added support for using externally allocated memory in DPDK.** + + DPDK has gained support for creating new ``rte_malloc`` heaps referencing + memory that was created outside of DPDK's own page allocator, and using that + memory natively with any other DPDK library or data structure. API Changes -----------
Signature · 1 line
2.17.1
PATCH v4 1/20 mem: add length to memseg list
In reply to Anatoly BurakovPreviously, to calculate length of memory area covered by a memseg list, we would've needed to multiply page size by length of fbarray backing that memseg list. This is not obvious and unnecessarily low level, so store length in the memseg list itself.
--- drivers/bus/pci/linux/pci.c | 2 +- lib/librte_eal/bsdapp/eal/eal_memory.c | 2 ++ lib/librte_eal/common/eal_common_memory.c | 5 ++--- lib/librte_eal/common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/linuxapp/eal/eal_memalloc.c | 3 ++- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +++- 6 files changed, 11 insertions(+), 6 deletions(-)
drivers/bus/pci/linux/pci.c +1 -1
@@ -119,7 +119,7 @@ rte_pci_unmap_device(struct rte_pci_device *dev)static int find_max_end_va (const struct rte_memseg_list *msl,void *arg) { -size_t sz = msl->memseg_arr.len * msl->page_sz; +size_t sz = msl->len;void *end_va =RTE_PTR_ADD (msl->base_va, sz);void **max_va = arg;
lib/librte_eal/bsdapp/eal/eal_memory.c +2 -0
@@ -79,6 +79,7 @@ rte_eal_hugepage_init(void) } msl->base_va = addr; msl->page_sz = page_sz; + msl->len = internal_config.memory; msl->socket_id =0 ;/* populate memsegs. each memseg is 1 page long */
@@ -370,6 +371,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
lib/librte_eal/common/eal_common_memory.c +2 -3
@@ -171,7 +171,7 @@ virt2memseg(const void *addr, const struct rte_memseg_list *msl)/* a memseg list was specified, check if it's the right one */ start = msl->base_va; - end =RTE_PTR_ADD (start, (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr < start || addr >= end)return NULL ;
@@ -194,8 +194,7 @@ virt2memseg_list(const void *addr) msl = &mcfg->memsegs[msl_idx]; start = msl->base_va; - end =RTE_PTR_ADD (start, - (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr >= start && addr < end)break ; }
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -30,6 +30,7 @@ struct rte_memseg_list {uint64_t addr_64;/**< Makes sure addr is always 64-bits */ }; +size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ volatile uint32_t version;/**< version number for multiprocess sync. */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +2 -1
@@ -986,7 +986,7 @@ free_seg_walk(const struct rte_memseg_list *msl, void *arg)int msl_idx, seg_idx, ret, dir_fd =-1 ; start_addr = (uintptr_t ) msl->base_va; - end_addr = start_addr + msl->memseg_arr.len * (size_t )msl->page_sz; + end_addr = start_addr + msl->len;if ((uintptr_t )wa->ms->addr < start_addr || (uintptr_t )wa->ms->addr >= end_addr)
@@ -1472,6 +1472,7 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,return -1 ; } local_msl->base_va = primary_msl->base_va; + local_msl->len = primary_msl->len;return 0 ; }
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -1
@@ -861,6 +861,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
@@ -1369,6 +1370,7 @@ eal_legacy_hugepage_init(void) msl->base_va = addr; msl->page_sz = page_sz; msl->socket_id =0 ; + msl->len = internal_config.memory;/* populate memsegs. each memseg is one page long */ for (cur_seg =0 ; cur_seg < n_segs; cur_seg++) {
@@ -1615,7 +1617,7 @@ eal_legacy_hugepage_init(void)if (msl->memseg_arr.count >0 )continue ;/* this is an unused list, deallocate it */ - mem_sz = (size_t )msl->page_sz * msl->memseg_arr.len; + mem_sz = msl->len;munmap (msl->base_va, mem_sz); msl->base_va =NULL ;
Signature · 1 line
2.17.1
PATCH v4 14/20 malloc: allow removing memory from named heaps
In reply to Anatoly BurakovAdd an API to remove memory from specified heaps. This will first check if all elements within the region are free, and that the region is the original region that was added to the heap (by comparing its length to length of memory addressed by the underlying memseg list).
--- lib/librte_eal/common/include/rte_malloc.h | 27 +++++++++++ lib/librte_eal/common/malloc_heap.c | 54 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 39 ++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 125 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -302,6 +302,33 @@ int __rte_experimental rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +/** +* Remove memory chunk from heap with specified name. +* +* @note Memory chunk being removed must be the same as one that was added; +* partially removing memory chunks is not supported +* +* @note Memory area must not contain any allocated elements to allow its +* removal from the heap +* +* @param heap_name +* Name of the heap to remove memory from +* @param va_addr +* Virtual address to remove from the heap +* @param len +* Length of virtual area to remove from the heap +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to remove memory from a reserved heap +* ENOENT - heap or memory chunk was not found +* EBUSY - memory chunk still contains data +*/ +int __rte_experimental +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +54 -0
66 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -43,6 +43,10 @@ int malloc_heap_add_external_memory(struct malloc_heap *heap, void *va_addr, rte_iova_t iova_addrs[], unsigned int n_pages, size_t page_sz); +int +malloc_heap_remove_external_memory (struct malloc_heap *heap,void *va_addr, +size_t len); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +39 -0
@@ -379,6 +379,45 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,return ret; } +int +rte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +int ret; + +if (heap_name ==NULL || va_addr ==NULL || len ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (heap_name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_write_lock (&mcfg->memory_hotplug_lock); +/* find our heap */ + heap =find_named_heap (heap_name); +if (heap ==NULL ) { + rte_errno =ENOENT ; + ret =-1 ; + goto unlock; + } +if (heap->socket_id <RTE_MAX_NUMA_NODES ) { +/* cannot remove memory from internal heaps */ + rte_errno =EPERM ; + ret =-1 ; + goto unlock; + } + +rte_spinlock_lock (&heap->lock); + ret =malloc_heap_remove_external_memory (heap, va_addr, len); +rte_spinlock_unlock (&heap->lock); + +unlock: +rte_rwlock_write_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -322,6 +322,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_remove; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v4 11/20 malloc: allow creating malloc heaps
In reply to Anatoly BurakovAdd API to allow creating new malloc heaps. They will be created with socket ID's going above RTE_MAX_NUMA_NODES, to avoid clashing with internal heaps.
--- lib/librte_eal/common/include/rte_malloc.h | 19 ++++++++ lib/librte_eal/common/malloc_heap.c | 30 +++++++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 52 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 105 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +19 -0
@@ -263,6 +263,25 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Creates a new empty malloc heap with a specified name. +* +* @note Heaps created via this call will automatically get assigned a unique +* socket ID, which can be found using ``rte_malloc_heap_get_socket()`` +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on successful creation +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +*/ +int __rte_experimental +rte_malloc_heap_create (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +30 -0
@@ -29,6 +29,10 @@#include "malloc_heap.h" #include "malloc_mp.h" +/* start external socket ID's at a very high number */ +#define CONST_MAX (a, b) (a > b ? a : b)/* RTE_MAX is not a constant */ +#define EXTERNAL_HEAP_MIN_SOCKET_ID (CONST_MAX((1 << 8), RTE_MAX_NUMA_NODES)) +static unsigned check_hugepage_sz (unsigned flags,uint64_t hugepage_sz) {
@@ -1015,6 +1019,32 @@ malloc_heap_dump(struct malloc_heap *heap, FILE *f)rte_spinlock_unlock (&heap->lock); } +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name) +{ +static uint32_t next_socket_id =EXTERNAL_HEAP_MIN_SOCKET_ID ; + +/* prevent overflow. did you really create 2 billion heaps??? */ +if (next_socket_id >INT32_MAX ) { +RTE_LOG (ERR ,EAL ,"Cannot assign new socket ID's\n" ); + rte_errno =ENOSPC ; +return -1 ; + } + +/* initialize empty heap */ + heap->alloc_count =0 ; + heap->first =NULL ; + heap->last =NULL ; +LIST_INIT (heap->free_head); +rte_spinlock_init (&heap->lock); + heap->total_size =0 ; + heap->socket_id = next_socket_id++; + +/* set up name */ +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -33,6 +33,9 @@ void * malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags, size_t align, bool contig); +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +52 -0
61 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -318,6 +318,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_create; rte_malloc_heap_get_socket; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register;
Signature · 1 line
2.17.1
PATCH v4 16/20 malloc: allow detaching from external memory
In reply to Anatoly BurakovAdd API to detach from existing chunk of external memory in a process.
--- lib/librte_eal/common/include/rte_malloc.h | 27 ++++++++++++++++++++++ lib/librte_eal/common/rte_malloc.c | 27 ++++++++++++++++++---- lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 50 insertions(+), 5 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -315,6 +315,9 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,* @note Memory area must not contain any allocated elements to allow its * removal from the heap * +* @note All other processes must detach from the memory chunk prior to it being +* removed from the heap. +* * @param heap_name * Name of the heap to remove memory from * @param va_addr
@@ -357,6 +360,30 @@ rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len); int __rte_experimentalrte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** +* Detach from a chunk of external memory in secondary process. +* +* @note This function must be called in before any attempt is made to remove +* external memory from the heap in another process. This function does *not* +* need to be called if a call to ``rte_malloc_heap_memory_remove`` will be +* called in current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful detach +* -1 on unsuccessful detach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to detach memory from a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/rte_malloc.c +22 -5
@@ -422,10 +422,11 @@ struct sync_mem_walk_arg {void *va_addr;size_t len;int result; +bool attach; };static int -attach_mem_walk (const struct rte_memseg_list *msl,void *arg) +sync_mem_walk (const struct rte_memseg_list *msl,void *arg) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct sync_mem_walk_arg *wa = arg;
@@ -440,7 +441,10 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg) msl_idx = msl - mcfg->memsegs; found_msl = &mcfg->memsegs[msl_idx]; - ret =rte_fbarray_attach (&found_msl->memseg_arr); +if (wa->attach) + ret =rte_fbarray_attach (&found_msl->memseg_arr); +else + ret =rte_fbarray_detach (&found_msl->memseg_arr);if (ret <0 ) wa->result = -rte_errno;
@@ -451,8 +455,8 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg)return 0 ; } -int -rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +static int +sync_memory (const char *heap_name,void *va_addr,size_t len,bool attach) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct malloc_heap *heap =NULL ;
@@ -486,9 +490,10 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len) wa.va_addr = va_addr; wa.len = len; wa.result = -ENOENT ;/* fail unless explicitly told to succeed */ + wa.attach = attach;/* we're already holding a read lock */ -rte_memseg_list_walk_thread_unsafe (attach_mem_walk, &wa); +rte_memseg_list_walk_thread_unsafe (sync_mem_walk, &wa);if (wa.result <0 ) { rte_errno = -wa.result;
@@ -501,6 +506,18 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)return ret; } +int +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, true); +} + +int +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, false); +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -323,6 +323,7 @@ EXPERIMENTAL { rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; rte_malloc_heap_memory_attach; + rte_malloc_heap_memory_detach; rte_malloc_heap_memory_remove; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register;
Signature · 1 line
2.17.1
PATCH v4 2/20 mem: allow memseg lists to be marked as external
In reply to Anatoly BurakovWhen we allocate and use DPDK memory, we need to be able to differentiate between DPDK hugepage segments and segments that were made part of DPDK but are externally allocated. Add such a property to memseg lists.
This breaks the ABI, so bump the EAL library ABI version and document the change in release notes. This also breaks a few internal assumptions about memory contiguousness, so adjust malloc code in a few places.
All current calls for memseg walk functions were adjusted to ignore external segments where it made sense.
Mempools is a special case, because we may be asked to allocate a mempool on a specific socket, and we need to ignore all page sizes on other heaps or other sockets. Previously, this assumption of knowing all page sizes was not a problem, but it will be now, so we have to match socket ID with page size when calculating minimum page size for a mempool.
---
Notes:
v3:
- Add comment to explain the process of picking up minimum page sizes for mempool
v2:
- Add documentation changes and ABI break
v1:
- Adjust all calls to memseg walk functions to ignore external segments where it made sense to do so
doc/guides/rel_notes/deprecation.rst | 15 -------- doc/guides/rel_notes/release_18_11.rst | 12 ++++++- drivers/bus/fslmc/fslmc_vfio.c | 7 ++-- drivers/net/mlx4/mlx4_mr.c | 3 ++ drivers/net/mlx5/mlx5.c | 5 ++- drivers/net/mlx5/mlx5_mr.c | 3 ++ drivers/net/virtio/virtio_user/vhost_kernel.c | 5 ++- lib/librte_eal/bsdapp/eal/Makefile | 2 +- lib/librte_eal/bsdapp/eal/eal.c | 3 ++ lib/librte_eal/bsdapp/eal/eal_memory.c | 7 ++-- lib/librte_eal/common/eal_common_memory.c | 3 ++ .../common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/common/include/rte_memory.h | 9 +++++ lib/librte_eal/common/malloc_elem.c | 10 ++++-- lib/librte_eal/common/malloc_heap.c | 9 +++-- lib/librte_eal/common/rte_malloc.c | 2 +- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 10 +++++- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 9 +++++ lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 ++++++--- lib/librte_eal/meson.build | 2 +- lib/librte_mempool/rte_mempool.c | 35 ++++++++++++++----- test/test/test_malloc.c | 3 ++ test/test/test_memzone.c | 3 ++ 24 files changed, 133 insertions(+), 44 deletions(-)
doc/guides/rel_notes/deprecation.rst +0 -15
@@ -11,21 +11,6 @@ API and ABI deprecation notices are to be posted here. Deprecation Notices ------------------- -* eal: certain structures will change in EAL on account of upcoming external - memory support. Aside from internal changes leading to an ABI break, the - following externally visible changes will also be implemented: - - - ``rte_memseg_list`` will change to include a boolean flag indicating - whether a particular memseg list is externally allocated. This will have - implications for any users of memseg-walk-related functions, as they will - now have to skip externally allocated segments in most cases if the intent - is to only iterate over internal DPDK memory. - - ``socket_id`` parameter across the entire DPDK will gain additional meaning, - as some socket ID's will now be representing externally allocated memory. No - changes will be required for existing code as backwards compatibility will - be kept, and those who do not use this feature will not see these extra - socket ID's. - * eal: both declaring and identifying devices will be streamlined in v18.11. New functions will appear to query a specific port from buses, classes of device and device drivers. Device declaration will be made coherent with the
doc/guides/rel_notes/release_18_11.rst +11 -1
@@ -91,6 +91,13 @@ API Changes flag the MAC can be properly configured in any case. This is particularly important for bonding. +* eal: The following API changes were made in 18.11: + + - ``rte_memseg_list`` structure now has an additional flag indicating whether + the memseg list is externally allocated. This will have implications for any + users of memseg-walk-related functions, as they will now have to skip + externally allocated segments in most cases if the intent is to only iterate + over internal DPDK memory. ABI Changes -----------
@@ -107,6 +114,9 @@ ABI Changes ========================================================= +* eal: EAL library ABI version was changed due to previously announced work on + supporting external memory in DPDK. + Removed Items -------------
@@ -152,7 +162,7 @@ The libraries prepended with a plus sign were incremented in this version. librte_compressdev.so.1 librte_cryptodev.so.5 librte_distributor.so.1 - librte_eal.so.8 + + librte_eal.so.9 librte_ethdev.so.10 librte_eventdev.so.4 librte_flow_classify.so.1
drivers/bus/fslmc/fslmc_vfio.c +5 -2
@@ -317,12 +317,15 @@ fslmc_unmap_dma(uint64_t vaddr, uint64_t iovaddr __rte_unused, size_t len) }static int -fslmc_dmamap_seg(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +fslmc_dmamap_seg (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *n_segs = arg;int ret; +if (msl->external) +return 0 ; + ret =fslmc_map_dma (ms->addr_64, ms->iova, ms->len);if (ret) DPAA2_BUS_ERR("Unable to VFIO map (addr=%p, len=%zu)",
drivers/net/mlx4/mlx4_mr.c +3 -0
@@ -496,6 +496,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/mlx5/mlx5.c +4 -1
@@ -568,11 +568,14 @@ static struct rte_pci_driver mlx5_driver;static void *uar_base;static int -find_lower_va_bound(const struct rte_memseg_list *msl __rte_unused, +find_lower_va_bound (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {void **addr = arg; +if (msl->external) +return 0 ; +if (*addr ==NULL ) *addr = ms->addr;else
drivers/net/mlx5/mlx5_mr.c +3 -0
@@ -486,6 +486,9 @@ mr_find_contig_memsegs_cb(const struct rte_memseg_list *msl, {struct mr_find_contig_memsegs_data *data = arg; +if (msl->external) +return 0 ; +if (data->addr < ms->addr_64 || data->addr >= ms->addr_64 + len)return 0 ;/* Found, save it and stop walking. */
drivers/net/virtio/virtio_user/vhost_kernel.c +4 -1
@@ -75,13 +75,16 @@ struct walk_arg {uint32_t region_nr; };static int -add_memory_region(const struct rte_memseg_list *msl __rte_unused, +add_memory_region (const struct rte_memseg_list *msl,const struct rte_memseg *ms,size_t len,void *arg) {struct walk_arg *wa = arg;struct vhost_memory_region *mr;void *start_addr; +if (msl->external) +return 0 ; +if (wa->region_nr >= max_regions)return -1 ;
lib/librte_eal/bsdapp/eal/Makefile +1 -1
@@ -22,7 +22,7 @@ LDLIBS += -lrte_kvargs EXPORT_MAP := ../../rte_eal_version.map -LIBABIVER := 8 +LIBABIVER := 9 # specific to bsdapp exec-env SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
lib/librte_eal/bsdapp/eal/eal.c +3 -0
@@ -502,6 +502,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +if (msl->socket_id == *socket_id && msl->memseg_arr.count !=0 )return 1 ;
lib/librte_eal/bsdapp/eal/eal_memory.c +5 -2
@@ -236,12 +236,15 @@ struct attach_walk_args {int seg_idx; };static int -attach_segment(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +attach_segment (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {struct attach_walk_args *wa = arg;void *addr; +if (msl->external) +return 0 ; + addr =mmap (ms->addr, ms->len,PROT_READ |PROT_WRITE ,MAP_SHARED |MAP_FIXED , wa->fd_hugepage, wa->seg_idx *EAL_PAGE_SIZE );
lib/librte_eal/common/eal_common_memory.c +3 -0
@@ -272,6 +272,9 @@ physmem_size(const struct rte_memseg_list *msl, void *arg) {uint64_t *total_len = arg; +if (msl->external) +return 0 ; + *total_len += msl->memseg_arr.count * msl->page_sz;return 0 ;
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -33,6 +33,7 @@ struct rte_memseg_list {size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ +unsigned int external;/**< 1 if this list points to external memory */ volatile uint32_t version;/**< version number for multiprocess sync. */ struct rte_fbarray memseg_arr; };
lib/librte_eal/common/include/rte_memory.h +9 -0
@@ -215,6 +215,9 @@ typedef int (*rte_memseg_list_walk_t)(const struct rte_memseg_list *msl,* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -233,6 +236,9 @@ rte_memseg_walk(rte_memseg_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
@@ -251,6 +257,9 @@ rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg);* @note This function read-locks the memory hotplug subsystem, and thus cannot * be used within memory-related callback functions. * +* @note This function will also walk through externally allocated segments. It +* is up to the user to decide whether to skip through these segments. +* * @param func * Iterator function * @param arg
lib/librte_eal/common/malloc_elem.c +7 -3
@@ -39,10 +39,14 @@ malloc_elem_find_max_iova_contig(struct malloc_elem *elem, size_t align) contig_seg_start =RTE_PTR_ALIGN_CEIL (data_start, align);/* if we're in IOVA as VA mode, or if we're in legacy mode with -* hugepages, all elements are IOVA-contiguous. +* hugepages, all elements are IOVA-contiguous. however, we can only +* make these assumptions about internal memory - externally allocated +* segments have to be checked. */ -if (rte_eal_iova_mode () ==RTE_IOVA_VA || - (internal_config.legacy_mem &&rte_eal_has_hugepages ())) +if (!elem->msl->external && + (rte_eal_iova_mode () ==RTE_IOVA_VA || + (internal_config.legacy_mem && +rte_eal_has_hugepages ())))return RTE_PTR_DIFF (data_end, contig_seg_start); cur_page =RTE_PTR_ALIGN_FLOOR (contig_seg_start, page_sz);
lib/librte_eal/common/malloc_heap.c +7 -2
@@ -95,6 +95,9 @@ malloc_add_seg(const struct rte_memseg_list *msl,struct malloc_heap *heap;int msl_idx; +if (msl->external) +return 0 ; + heap = &mcfg->malloc_heaps[msl->socket_id];/* msl is const, so find it */
@@ -754,8 +757,10 @@ malloc_heap_free(struct malloc_elem *elem)/* anything after this is a bonus */ ret =0 ; -/* ...of which we can't avail if we are in legacy mode */ -if (internal_config.legacy_mem) +/* ...of which we can't avail if we are in legacy mode, or if this is an +* externally allocated segment. +*/ +if (internal_config.legacy_mem || msl->external) goto free_unlock;/* check if we can free any memory back to the system */
lib/librte_eal/common/rte_malloc.c +1 -1
@@ -223,7 +223,7 @@ rte_malloc_virt2iova(const void *addr)if (elem ==NULL )return RTE_BAD_IOVA ; -if (rte_eal_iova_mode () ==RTE_IOVA_VA ) +if (!elem->msl->external &&rte_eal_iova_mode () ==RTE_IOVA_VA )return (uintptr_t ) addr; ms =rte_mem_virt2memseg (addr, elem->msl);
lib/librte_eal/linuxapp/eal/Makefile +1 -1
@@ -10,7 +10,7 @@ ARCH_DIR ?= $(RTE_ARCH) EXPORT_MAP := ../../rte_eal_version.map VPATH += $(RTE_SDK)/lib/librte_eal/common/arch/$(ARCH_DIR) -LIBABIVER := 8 +LIBABIVER := 9 VPATH += $(RTE_SDK)/lib/librte_eal/common
lib/librte_eal/linuxapp/eal/eal.c +9 -1
@@ -725,6 +725,9 @@ check_socket(const struct rte_memseg_list *msl, void *arg) {int *socket_id = arg; +if (msl->external) +return 0 ; +return *socket_id == msl->socket_id; }
@@ -1059,7 +1062,12 @@ mark_freeable(const struct rte_memseg_list *msl, const struct rte_memseg *ms, void *arg __rte_unused) {/* ms is const, so find this memseg */ -struct rte_memseg *found =rte_mem_virt2memseg (ms->addr, msl); +struct rte_memseg *found; + +if (msl->external) +return 0 ; + + found =rte_mem_virt2memseg (ms->addr, msl); found->flags &= ~RTE_MEMSEG_FLAG_DO_NOT_FREE ;
lib/librte_eal/linuxapp/eal/eal_memalloc.c +9 -0
@@ -1408,6 +1408,9 @@ sync_walk(const struct rte_memseg_list *msl, void *arg __rte_unused)unsigned int i;int msl_idx; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1456,6 +1459,9 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,char name[PATH_MAX ];int msl_idx, ret; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; primary_msl = &mcfg->memsegs[msl_idx]; local_msl = &local_memsegs[msl_idx];
@@ -1509,6 +1515,9 @@ fd_list_create_walk(const struct rte_memseg_list *msl,unsigned int len;int msl_idx; +if (msl->external) +return 0 ; + msl_idx = msl - mcfg->memsegs; len = msl->memseg_arr.len;
lib/librte_eal/linuxapp/eal/eal_vfio.c +13 -4
@@ -1082,11 +1082,14 @@ rte_vfio_get_group_num(const char *sysfs_base, }static int -type1_map(const struct rte_memseg_list *msl __rte_unused, -const struct rte_memseg *ms,void *arg) +type1_map (const struct rte_memseg_list *msl,const struct rte_memseg *ms, +void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_type1_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1196,11 +1199,14 @@ vfio_spapr_dma_do_map(int vfio_container_fd, uint64_t vaddr, uint64_t iova, }static int -vfio_spapr_map_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_map_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {int *vfio_container_fd = arg; +if (msl->external) +return 0 ; +return vfio_spapr_dma_mem_map (*vfio_container_fd, ms->addr_64, ms->iova, ms->len,1 ); }
@@ -1210,12 +1216,15 @@ struct spapr_walk_param {uint64_t hugepage_sz; };static int -vfio_spapr_window_size_walk(const struct rte_memseg_list *msl __rte_unused, +vfio_spapr_window_size_walk (const struct rte_memseg_list *msl,const struct rte_memseg *ms,void *arg) {struct spapr_walk_param *param = arg;uint64_t max = ms->iova + ms->len; +if (msl->external) +return 0 ; +if (max > param->window_size) { param->hugepage_sz = ms->hugepage_sz; param->window_size = max;
lib/librte_eal/meson.build +1 -1
@@ -21,7 +21,7 @@ else error('unsupported system type "@0@"'.format(host_machine.system())) endif -version = 8 # the version of the EAL API +version = 9 # the version of the EAL API allow_experimental_apis = true deps += 'compat' deps += 'kvargs'
lib/librte_mempool/rte_mempool.c +27 -8
@@ -99,25 +99,44 @@ static unsigned optimize_object_size(unsigned obj_size)return new_obj_size *RTE_MEMPOOL_ALIGN ; } +struct pagesz_walk_arg { +int socket_id; +size_t min; +}; +static int find_min_pagesz (const struct rte_memseg_list *msl,void *arg) { -size_t *min = arg; +struct pagesz_walk_arg *wa = arg; +bool valid; -if (msl->page_sz < *min) - *min = msl->page_sz; +/* +* we need to only look at page sizes available for a particular socket +* ID. so, we either need an exact match on socket ID (can match both +* native and external memory), or, if SOCKET_ID_ANY was specified as a +* socket ID argument, we must only look at native memory and ignore any +* page sizes associated with external memory. +*/ + valid = msl->socket_id == wa->socket_id; + valid |= wa->socket_id ==SOCKET_ID_ANY && msl->external ==0 ; + +if (valid && msl->page_sz < wa->min) + wa->min = msl->page_sz;return 0 ; }static size_t -get_min_page_size (void ) +get_min_page_size (int socket_id) { -size_t min_pagesz =SIZE_MAX ; +struct pagesz_walk_arg wa; -rte_memseg_list_walk (find_min_pagesz, &min_pagesz); + wa.min =SIZE_MAX ; + wa.socket_id = socket_id; -return min_pagesz ==SIZE_MAX ? (size_t )getpagesize () : min_pagesz; +rte_memseg_list_walk (find_min_pagesz, &wa); + +return wa.min ==SIZE_MAX ? (size_t )getpagesize () : wa.min; }
@@ -470,7 +489,7 @@ rte_mempool_populate_default(struct rte_mempool *mp) pg_sz =0 ; pg_shift =0 ; }else if (try_contig) { - pg_sz =get_min_page_size (); + pg_sz =get_min_page_size (mp->socket_id); pg_shift =rte_bsf32 (pg_sz); }else { pg_sz =getpagesize ();
test/test/test_malloc.c +3 -0
@@ -711,6 +711,9 @@ check_socket_mem(const struct rte_memseg_list *msl, void *arg) {int32_t *socket = arg; +if (msl->external) +return 0 ; +return *socket == msl->socket_id; }
test/test/test_memzone.c +3 -0
@@ -115,6 +115,9 @@ find_available_pagesz(const struct rte_memseg_list *msl, void *arg) {struct walk_arg *wa = arg; +if (msl->external) +return 0 ; +if (msl->page_sz ==RTE_PGSIZE_2M ) wa->hugepage_2MB_avail =1 ;if (msl->page_sz ==RTE_PGSIZE_1G )
Signature · 1 line
2.17.1
PATCH v4 5/20 flow_classify: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_flow_classify/rte_flow_classify.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_flow_classify/rte_flow_classify.c +1 -2
@@ -247,8 +247,7 @@ rte_flow_classifier_check_params(struct rte_flow_classifier_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_FLOW_CLASSIFY_LOG (ERR ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH v4 18/20 app/testpmd: add support for external memory
In reply to Anatoly BurakovCurrently, mempools can only be allocated either using native DPDK memory, or anonymous memory. This patch will add two new methods to allocate mempool using external memory (regular or hugepage memory), and add documentation about it to testpmd user guide.
It adds a new flag "--mp-alloc", with four possible values:
native (use regular DPDK allocator), anon (use anonymous mempool), xmem (use externally allocated memory area), and xmemhuge (use externally allocated hugepage memory area). Old flag "--mp-anon" is kept for compatibility.
All external memory is allocated using the same external heap, but each will allocate and add a new memory area.
--- app/test-pmd/config.c | 21 +- app/test-pmd/parameters.c | 23 +- app/test-pmd/testpmd.c | 337 ++++++++++++++++++++++++-- app/test-pmd/testpmd.h | 13 +- doc/guides/testpmd_app_ug/run_app.rst | 12 + 5 files changed, 381 insertions(+), 25 deletions(-)
app/test-pmd/config.c +19 -2
@@ -2413,6 +2413,23 @@ fwd_config_setup(void)simple_fwd_config_setup (); } +static const char * +mp_alloc_to_str (uint8_t mode) +{ +switch (mode) { +case MP_ALLOC_NATIVE : +return "native" ; +case MP_ALLOC_ANON : +return "anon" ; +case MP_ALLOC_XMEM : +return "xmem" ; +case MP_ALLOC_XMEM_HUGE : +return "xmemhuge" ; +default : +return "invalid" ; + } +} +void pkt_fwd_config_display (struct fwd_config *cfg) {
@@ -2421,12 +2438,12 @@ pkt_fwd_config_display(struct fwd_config *cfg)streamid_t sm_id;printf ("%s packet forwarding%s - ports=%d - cores=%d - streams=%d - " -"NUMA support %s, MP over anonymous pages %s\n" , +"NUMA support %s, MP allocation mode: %s\n" , cfg->fwd_eng->fwd_mode_name, retry_enabled ==0 ?"" :" with retry" , cfg->nb_fwd_ports, cfg->nb_fwd_lcores, cfg->nb_fwd_streams, numa_support ==1 ?"enabled" :"disabled" , - mp_anon !=0 ?"enabled" :"disabled" ); +mp_alloc_to_str (mp_alloc_type));if (retry_enabled) printf("TX retry num: %u, delay between TX retries: %uus\n",
app/test-pmd/parameters.c +22 -1
@@ -190,6 +190,11 @@ usage(char* progname)printf (" --vxlan-gpe-port=N: UPD port of tunnel VXLAN-GPE\n" );printf (" --mlockall: lock all memory\n" );printf (" --no-mlockall: do not lock all memory\n" ); +printf (" --mp-alloc <native|anon|xmem|xmemhuge>: mempool allocation method.\n" +" native: use regular DPDK memory to create and populate mempool\n" +" anon: use regular DPDK memory to create and anonymous memory to populate mempool\n" +" xmem: use anonymous memory to create and populate mempool\n" +" xmemhuge: use anonymous hugepage memory to create and populate mempool\n" ); }#ifdef RTE_LIBRTE_CMDLINE
@@ -625,6 +630,7 @@ launch_args_parse(int argc, char** argv) { "vxlan-gpe-port", 1, 0, 0 }, { "mlockall", 0, 0, 0 }, { "no-mlockall", 0, 0, 0 }, + { "mp-alloc", 1, 0, 0 }, { 0, 0, 0, 0 }, };
@@ -743,7 +749,22 @@ launch_args_parse(int argc, char** argv)if (!strcmp (lgopts[opt_idx].name,"numa" )) numa_support =1 ;if (!strcmp (lgopts[opt_idx].name,"mp-anon" )) { - mp_anon =1 ; + mp_alloc_type =MP_ALLOC_ANON ; + } +if (!strcmp (lgopts[opt_idx].name,"mp-alloc" )) { +if (!strcmp (optarg,"native" )) + mp_alloc_type =MP_ALLOC_NATIVE ; +else if (!strcmp (optarg,"anon" )) + mp_alloc_type =MP_ALLOC_ANON ; +else if (!strcmp (optarg,"xmem" )) + mp_alloc_type =MP_ALLOC_XMEM ; +else if (!strcmp (optarg,"xmemhuge" )) + mp_alloc_type =MP_ALLOC_XMEM_HUGE ; +else +rte_exit (EXIT_FAILURE , +"mp-alloc %s invalid - must be: " +"native, anon or xmem\n" , + optarg); }if (!strcmp (lgopts[opt_idx].name,"port-numa-config" )) {if (parse_portnuma_config (optarg))
app/test-pmd/testpmd.c +316 -21
373 lines not on this page · raw message
app/test-pmd/testpmd.h +12 -1
@@ -69,6 +69,16 @@ enum { PORT_TOPOLOGY_LOOP, }; +enum { +MP_ALLOC_NATIVE ,/**< allocate and populate mempool natively */ +MP_ALLOC_ANON , +/**< allocate mempool natively, but populate using anonymous memory */ +MP_ALLOC_XMEM , +/**< allocate and populate mempool using anonymous memory */ +MP_ALLOC_XMEM_HUGE +/**< allocate and populate mempool using anonymous hugepage memory */ +}; +#ifdef RTE_TEST_PMD_RECORD_BURST_STATS /** * The data structure associated with RX and TX packet burst statistics
@@ -304,7 +314,8 @@ extern uint8_t numa_support; /**< set by "--numa" parameter */extern uint16_t port_topology;/**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx;/**<set by "--no-flush-rx" parameter */ extern uint8_t flow_isolate_all;/**< set by "--flow-isolate-all */ -extern uint8_t mp_anon;/**< set by "--mp-anon" parameter */ +extern uint8_t mp_alloc_type; +/**< set by "--mp-anon" or "--mp-alloc" parameter */ extern uint8_t no_link_check;/**<set by "--disable-link-check" parameter */ extern volatile int test_done;/* stop packet forwarding when set to 1. */ extern uint8_t lsc_interrupt;/**< disabled by "--no-lsc-interrupt" parameter */
doc/guides/testpmd_app_ug/run_app.rst +12 -0
@@ -498,3 +498,15 @@ The commandline options are: * ``--no-mlockall`` Disable locking all memory. + +* ``--mp-alloc <native|anon|xmem|xmemhuge>`` + + Select mempool allocation mode: + + * native: create and populate mempool using native DPDK memory + * anon: create mempool using native DPDK memory, but populate using + anonymous memory + * xmem: create and populate mempool using externally and anonymously + allocated area + * xmemhuge: create and populate mempool using externally and anonymously + allocated hugepage area
Signature · 1 line
2.17.1
PATCH v4 3/20 malloc: index heaps using heap ID rather than NUMA node
In reply to Anatoly BurakovSwitch over all parts of EAL to use heap ID instead of NUMA node ID to identify heaps. Heap ID for DPDK-internal heaps is NUMA node's index within the detected NUMA node list. Heap ID for external heaps will be order of their creation.
--- config/common_base | 1 + config/rte_config.h | 1 + .../common/include/rte_eal_memconfig.h | 4 +- .../common/include/rte_malloc_heap.h | 1 + lib/librte_eal/common/malloc_heap.c | 98 +++++++++++++------ lib/librte_eal/common/malloc_heap.h | 3 + lib/librte_eal/common/rte_malloc.c | 41 +++++--- 7 files changed, 106 insertions(+), 43 deletions(-)
config/common_base +1 -0
@@ -61,6 +61,7 @@ CONFIG_RTE_CACHE_LINE_SIZE=64 CONFIG_RTE_LIBRTE_EAL=y CONFIG_RTE_MAX_LCORE=128 CONFIG_RTE_MAX_NUMA_NODES=8 +CONFIG_RTE_MAX_HEAPS=32 CONFIG_RTE_MAX_MEMSEG_LISTS=64 # each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages # or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
config/rte_config.h +1 -0
@@ -24,6 +24,7 @@#define RTE_BUILD_SHARED_LIB /* EAL defines */ +#define RTE_MAX_HEAPS 32#define RTE_MAX_MEMSEG_LISTS 128#define RTE_MAX_MEMSEG_PER_LIST 8192#define RTE_MAX_MEM_MB_PER_LIST 32768
lib/librte_eal/common/include/rte_eal_memconfig.h +2 -2
@@ -72,8 +72,8 @@ struct rte_mem_config {struct rte_tailq_head tailq_head[RTE_MAX_TAILQ ];/**< Tailqs for objects */ -/* Heaps of Malloc per socket */ -struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES ]; +/* Heaps of Malloc */ +struct malloc_heap malloc_heaps[RTE_MAX_HEAPS ];/* address of mem_config in primary process. used to map shared config into * exact same address the primary process maps it.
lib/librte_eal/common/include/rte_malloc_heap.h +1 -0
@@ -27,6 +27,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size; + unsigned int socket_id; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +69 -29
204 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -46,6 +46,9 @@ malloc_heap_get_stats(struct malloc_heap *heap,void malloc_heap_dump (struct malloc_heap *heap,FILE *f); +int +malloc_socket_to_heap_id (unsigned int socket_id); +int rte_eal_malloc_heap_init (void );
lib/librte_eal/common/rte_malloc.c +29 -12
72 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v4 10/20 malloc: add function to check if socket is external
In reply to Anatoly BurakovAn API is needed to check whether a particular socket ID belongs to an internal or external heap. Prime user of this would be mempool allocator, because normal assumptions of IOVA contiguousness in IOVA as VA mode do not hold in case of externally allocated memory.
--- lib/librte_eal/common/include/rte_malloc.h | 15 +++++++++++++ lib/librte_eal/common/rte_malloc.c | 25 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + lib/librte_mempool/rte_mempool.c | 22 ++++++++++++++++--- 4 files changed, 60 insertions(+), 3 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +15 -0
@@ -277,6 +277,21 @@ rte_malloc_get_socket_stats(int socket, int __rte_experimentalrte_malloc_heap_get_socket (const char *name); +/** +* Check if a given socket ID refers to externally allocated memory. +* +* @note Passing SOCKET_ID_ANY will return 0. +* +* @param socket_id +* Socket ID to check +* @return +* 1 if socket ID refers to externally allocated memory +* 0 if socket ID refers to internal DPDK memory +* -1 if socket ID is invalid +*/ +int __rte_experimental +rte_malloc_heap_socket_is_external (int socket_id); +/** * Dump statistics. *
lib/librte_eal/common/rte_malloc.c +25 -0
@@ -220,6 +220,31 @@ rte_malloc_heap_get_socket(const char *name)return ret; } +int +rte_malloc_heap_socket_is_external (int socket_id) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +unsigned int idx; +int ret =-1 ; + +if (socket_id ==SOCKET_ID_ANY ) +return 0 ; + +rte_rwlock_read_lock (&mcfg->memory_hotplug_lock); +for (idx =0 ; idx <RTE_MAX_HEAPS ; idx++) { +struct malloc_heap *tmp = &mcfg->malloc_heaps[idx]; + +if ((int )tmp->socket_id == socket_id) { +/* external memory always has large socket ID's */ + ret = tmp->socket_id >=RTE_MAX_NUMA_NODES ; +break ; + } + } +rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +/* * Print stats on memory type. If type is NULL, info on all types is printed */
lib/librte_eal/rte_eal_version.map +1 -0
@@ -319,6 +319,7 @@ EXPERIMENTAL { rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; rte_malloc_heap_get_socket; + rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
lib/librte_mempool/rte_mempool.c +19 -3
@@ -428,12 +428,18 @@ rte_mempool_populate_default(struct rte_mempool *mp)rte_iova_t iova;unsigned mz_id, n;int ret; -bool no_contig, try_contig, no_pageshift; +bool no_contig, try_contig, no_pageshift, external; ret =mempool_ops_alloc_once (mp);if (ret !=0 )return ret; +/* check if we can retrieve a valid socket ID */ + ret =rte_malloc_heap_socket_is_external (mp->socket_id); +if (ret <0 ) +return -EINVAL ; + external = ret; +/* mempool must not be populated */ if (mp->nb_mem_chunks !=0 )return -EEXIST ;
@@ -481,9 +487,19 @@ rte_mempool_populate_default(struct rte_mempool *mp)* in one contiguous chunk as well (otherwise we might end up wasting a * 1G page on a 10MB memzone). If we fail to get enough contiguous * memory, then we'll go and reserve space page-by-page. +* +* We also have to take into account the fact that memory that we're +* going to allocate from can belong to an externally allocated memory +* area, in which case the assumption of IOVA as VA mode being +* synonymous with IOVA contiguousness will not hold. We should also try +* to go for contiguous memory even if we're in no-huge mode, because +* external memory may in fact be IOVA-contiguous. */ - no_pageshift = no_contig ||rte_eal_iova_mode () ==RTE_IOVA_VA ; - try_contig = !no_contig && !no_pageshift &&rte_eal_has_hugepages (); + external =rte_malloc_heap_socket_is_external (mp->socket_id) ==1 ; + no_pageshift = no_contig || + (!external &&rte_eal_iova_mode () ==RTE_IOVA_VA ); + try_contig = !no_contig && !no_pageshift && + (rte_eal_has_hugepages () || external);if (no_pageshift) { pg_sz =0 ;
Signature · 1 line
2.17.1
PATCH v4 12/20 malloc: allow destroying heaps
In reply to Anatoly BurakovAdd an API to destroy specified heap.
--- lib/librte_eal/common/include/rte_malloc.h | 23 +++++++++ lib/librte_eal/common/malloc_heap.c | 22 ++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 58 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 107 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +23 -0
@@ -282,6 +282,29 @@ rte_malloc_get_socket_stats(int socket, int __rte_experimentalrte_malloc_heap_create (const char *heap_name); +/** +* Destroys a previously created malloc heap with specified name. +* +* @note This function will return a failure result if not all memory allocated +* from the heap has been freed back to the heap +* +* @note This function will return a failure result if not all memory segments +* were removed from the heap prior to its destruction +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* ENOENT - heap by the name of ``heap_name`` was not found +* EPERM - attempting to destroy reserved heap +* EBUSY - heap still contains data +*/ +int __rte_experimental +rte_malloc_heap_destroy (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +22 -0
@@ -1045,6 +1045,28 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name)return 0 ; } +int +malloc_heap_destroy (struct malloc_heap *heap) +{ +if (heap->alloc_count !=0 ) { +RTE_LOG (ERR ,EAL ,"Heap is still in use\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->first !=NULL || heap->last !=NULL ) { +RTE_LOG (ERR ,EAL ,"Heap still contains memory segments\n" ); + rte_errno =EBUSY ; +return -1 ; + } +if (heap->total_size !=0 ) +RTE_LOG (ERR ,EAL ,"Total size not zero, heap is likely corrupt\n" ); + +/* after this, the lock will be dropped */ +memset (heap,0 ,sizeof (*heap)); + +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -36,6 +36,9 @@ malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags,int malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int +malloc_heap_destroy (struct malloc_heap *heap); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +58 -0
67 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -319,6 +319,7 @@ EXPERIMENTAL { rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; rte_malloc_heap_create; + rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register;
Signature · 1 line
2.17.1
PATCH v4 7/20 sched: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_sched/rte_sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/librte_sched/rte_sched.c +1 -1
@@ -329,7 +329,7 @@ rte_sched_port_check_params(struct rte_sched_port_params *params)return -1 ;/* socket */ -if ((params->socket <0 ) || (params->socket >=RTE_MAX_NUMA_NODES )) +if (params->socket <0 )return -3 ;/* rate */
Signature · 1 line
2.17.1
PATCH v4 15/20 malloc: allow attaching to external memory chunks
In reply to Anatoly BurakovIn order to use external memory in multiple processes, we need to attach to primary process's memseg lists, so add a new API to do that. It is the responsibility of the user to ensure that memory is accessible and that it has been previously added to the malloc heap by another process.
--- lib/librte_eal/common/include/rte_malloc.h | 48 +++++++++-- lib/librte_eal/common/rte_malloc.c | 93 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 135 insertions(+), 7 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +41 -7
@@ -268,6 +268,10 @@ rte_malloc_get_socket_stats(int socket,* * @note Multiple memory chunks can be added to the same heap * +* @note Before accessing this memory in other processes, it needs to be +* attached in each of those processes by calling +* ``rte_malloc_heap_memory_attach`` in each other process. +* * @note Memory must be previously allocated for DPDK to be able to use it as a * malloc heap. Failing to do so will result in undefined behavior, up to and * including segmentation faults.
@@ -329,21 +333,48 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len, int __rte_experimentalrte_malloc_heap_memory_remove (const char *heap_name,void *va_addr,size_t len); +/** +* Attach to an already existing chunk of external memory in another process. +* +* @note This function must be called before any attempt is made to use an +* already existing external memory chunk. This function does *not* need to +* be called if a call to ``rte_malloc_heap_memory_add`` was made in the +* current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful attach +* -1 on unsuccessful attach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to attach memory to a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. * * @note Heaps created via this call will automatically get assigned a unique * socket ID, which can be found using ``rte_malloc_heap_get_socket()`` * +* @note This function can only be called in primary process. +* * @param heap_name * Name of the heap to create. * * @return * - 0 on successful creation * - -1 in case of error, with rte_errno set to one of the following: -* EINVAL - ``heap_name`` was NULL, empty or too long -* EEXIST - heap by name of ``heap_name`` already exists -* ENOSPC - no more space in internal config to store a new heap +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +* E_RTE_SECONDARY - attempted to create a heap in secondary process */ int __rte_experimentalrte_malloc_heap_create (const char *heap_name);
@@ -357,16 +388,19 @@ rte_malloc_heap_create(const char *heap_name);* @note This function will return a failure result if not all memory segments * were removed from the heap prior to its destruction * +* @note This function can only be called in primary process. +* * @param heap_name * Name of the heap to create. * * @return * - 0 on success * - -1 in case of error, with rte_errno set to one of the following: -* EINVAL - ``heap_name`` was NULL, empty or too long -* ENOENT - heap by the name of ``heap_name`` was not found -* EPERM - attempting to destroy reserved heap -* EBUSY - heap still contains data +* EINVAL - ``heap_name`` was NULL, empty or too long +* ENOENT - heap by the name of ``heap_name`` was not found +* EPERM - attempting to destroy reserved heap +* EBUSY - heap still contains data +* E_RTE_SECONDARY - attempted to destroy a heap in secondary process */ int __rte_experimentalrte_malloc_heap_destroy (const char *heap_name);
lib/librte_eal/common/rte_malloc.c +93 -0
111 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -322,6 +322,7 @@ EXPERIMENTAL { rte_malloc_heap_destroy; rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; + rte_malloc_heap_memory_attach; rte_malloc_heap_memory_remove; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register;
Signature · 1 line
2.17.1
PATCH v4 8/20 malloc: add name to malloc heaps
In reply to Anatoly BurakovWe will need to refer to external heaps in some way. While we use heap ID's internally, for external API use it has to be something more user-friendly. So, we will be using a string to uniquely identify a heap.
--- lib/librte_eal/common/include/rte_malloc_heap.h | 2 ++ lib/librte_eal/common/malloc_heap.c | 15 ++++++++++++++- lib/librte_eal/common/rte_malloc.c | 1 + 3 files changed, 17 insertions(+), 1 deletion(-)
lib/librte_eal/common/include/rte_malloc_heap.h +2 -0
@@ -12,6 +12,7 @@/* Number of free lists per heap, grouped by size. */ #define RTE_HEAP_NUM_FREELISTS 13 +#define RTE_HEAP_NAME_MAX_LEN 32/* dummy definition, for pointers */ struct malloc_elem ;
@@ -28,6 +29,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size;unsigned int socket_id; + char name[RTE_HEAP_NAME_MAX_LEN]; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +14 -1
@@ -127,7 +127,6 @@ malloc_add_seg(const struct rte_memseg_list *msl,malloc_heap_add_memory (heap, found_msl, ms->addr, len); heap->total_size += len; - heap->socket_id = msl->socket_id;RTE_LOG (DEBUG ,EAL ,"Added %zuM to heap on socket %i\n" , len >>20 , msl->socket_id);
@@ -1020,6 +1019,20 @@ intrte_eal_malloc_heap_init (void) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +unsigned int i; + +/* assign names to default DPDK heaps */ +for (i =0 ; i <rte_socket_count (); i++) { +struct malloc_heap *heap = &mcfg->malloc_heaps[i]; +char heap_name[RTE_HEAP_NAME_MAX_LEN ]; +int socket_id =rte_socket_id_by_idx (i); + +snprintf (heap_name,sizeof (heap_name) -1 , +"socket_%i" , socket_id); +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); + heap->socket_id = socket_id; + } +if (register_mp_requests ()) {RTE_LOG (ERR ,EAL ,"Couldn't register malloc multiprocess actions\n" );
lib/librte_eal/common/rte_malloc.c +1 -0
@@ -202,6 +202,7 @@ rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)malloc_heap_get_stats (heap, &sock_stats);fprintf (f,"Heap id:%u\n" , heap_id); +fprintf (f,"\tHeap name:%s\n" , heap->name);fprintf (f,"\tHeap_size:%zu,\n" , sock_stats.heap_totalsz_bytes);fprintf (f,"\tFree_size:%zu,\n" , sock_stats.heap_freesz_bytes);fprintf (f,"\tAlloc_size:%zu,\n" , sock_stats.heap_allocsz_bytes);
Signature · 1 line
2.17.1
PATCH v4 9/20 malloc: add function to query socket ID of named heap
In reply to Anatoly BurakovWhen we will be creating external heaps, they will have their own "fake" socket ID, so add a function that will map the heap name to its socket ID.
--- lib/librte_eal/common/include/rte_malloc.h | 14 ++++++++ lib/librte_eal/common/rte_malloc.c | 37 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 52 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +14 -0
@@ -263,6 +263,20 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Find socket ID corresponding to a named heap. +* +* @param name +* Heap name to find socket ID for +* @return +* Socket ID in case of success (a non-negative number) +* -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``name`` was NULL +* ENOENT - heap identified by the name ``name`` was not found +*/ +int __rte_experimental +rte_malloc_heap_get_socket (const char *name); +/** * Dump statistics. *
lib/librte_eal/common/rte_malloc.c +37 -0
@@ -8,6 +8,7 @@#include <string.h> #include <sys/queue.h> +#include <rte_errno.h> #include <rte_memcpy.h> #include <rte_memory.h> #include <rte_eal.h>
@@ -183,6 +184,42 @@ rte_malloc_dump_heaps(FILE *f)rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); } +int +rte_malloc_heap_get_socket (const char *name) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +unsigned int idx; +int ret; + +if (name ==NULL || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_read_lock (&mcfg->memory_hotplug_lock); +for (idx =0 ; idx <RTE_MAX_HEAPS ; idx++) { +struct malloc_heap *tmp = &mcfg->malloc_heaps[idx]; + +if (!strncmp (name, tmp->name,RTE_HEAP_NAME_MAX_LEN )) { + heap = tmp; +break ; + } + } + +if (heap !=NULL ) { + ret = heap->socket_id; + }else { + rte_errno =ENOENT ; + ret =-1 ; + } +rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +/* * Print stats on memory type. If type is NULL, info on all types is printed */
lib/librte_eal/rte_eal_version.map +1 -0
@@ -318,6 +318,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v4 20/20 doc: add external memory feature to programmer's guide
In reply to Anatoly BurakovAdd a short chapter on usage of external memory in DPDK to the Programmer's Guide.
--- .../prog_guide/env_abstraction_layer.rst | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+)
doc/guides/prog_guide/env_abstraction_layer.rst +38 -0
@@ -213,6 +213,44 @@ Normally, these options do not need to be changed. can later be mapped into that preallocated VA space (if dynamic memory mode is enabled), and can optionally be mapped into it at startup. +Support for Externally Allocated Memory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to use externally allocated memory in DPDK, using a set of malloc +heap API's. Support for externally allocated memory is implemented through +overloading the socket ID - externally allocated heaps will have socket ID's +that would be considered invalid under normal circumstances. Requesting an +allocation to take place from a specified externally allocated memory is a +matter of supplying the correct socket ID to DPDK allocator, either directly +(e.g. through a call to ``rte_malloc``) or indirectly (through data +structure-specific allocation API's such as ``rte_ring_create``). + +Since there is no way DPDK can verify whether memory are is available or valid, +this responsibility falls on the shoulders of the user. All multiprocess +synchronization is also user's responsibility, as well as ensuring that all +calls to add/attach/detach/remove memory are done in the correct order. It is +not required to attach to a memory area in all processes - only attach to memory +areas as needed. + +The expected workflow is as follows: + +* Get a pointer to memory area +* Create a named heap +* Add memory area(s) to the heap + * If IOVA table is not specified, IOVA addresses will be assumed to be + unavailable + * Any DMA mappings for the external area are responsibility of the user + * Other processes must attach to the memory area before they can use it +* Get socket ID used for the heap +* Use normal DPDK allocation procedures, using supplied socket ID +* If memory area is no longer needed, it can be removed from the heap + * Other processes must detach from this memory area before it can be removed +* If heap is no longer needed, remove it + * Socket ID will become invalid and will not be reused + +For more information, please refer to ``rte_malloc`` API documentation, +specifically the ``rte_malloc_heap_*`` family of function calls. + PCI Access ~~~~~~~~~~
Signature · 1 line
2.17.1
Hi Anatoly,
21/09/2018 18:13, Anatoly Burakov:
Quoted 2 lines
This is a proposal to enable using externally allocated memory in DPDK.
About this change and previous ones, I think we may miss some documentation about the usage and the internal design of the DPDK memory allocation.
You already updated some doc recently:
http://git.dpdk.org/dpdk/commit/?id=b31739328
This is what we have currently:
http://doc.dpdk.org/guides/prog_guide/env_abstraction_layer.html#memory-segments-and-memory-zones-memzone
http://doc.dpdk.org/guides/prog_guide/env_abstraction_layer.html#malloc
http://doc.dpdk.org/guides/prog_guide/mempool_lib.html
This is probably a good time to check this doc again. Do you think it deserves more explanations, or maybe some figures?
Quoted 12 lines · Thomas Monjalon
Hi Anatoly,
21/09/2018 18:13, Anatoly Burakov:
Quoted 2 lines
This is a proposal to enable using externally allocated memory in DPDK.
About this change and previous ones, I think we may miss some documentation about the usage and the internal design of the DPDK memory allocation.
You already updated some doc recently:http://git.dpdk.org/dpdk/commit/?id=b31739328
Quoted 8 lines
This is what we have currently:
http://doc.dpdk.org/guides/prog_guide/env_abstraction_layer.html#memory-segments-and-memory-zones-memzone
http://doc.dpdk.org/guides/prog_guide/env_abstraction_layer.html#malloc
http://doc.dpdk.org/guides/prog_guide/mempool_lib.htmlThis is probably a good time to check this doc again. Do you think it deserves more explanations, or maybe some figures?
Maybe this could be split into two sections - explanation of user-facing API, and explanation of its inner workings. However, I don't want for DPDK documentation to become my personal soapbox, so i'm open to suggestions on what is missing and how to organize the memory docs better :)
Signature · 2 lines
Thanks, Anatoly
PATCH v5 1/21 mem: add length to memseg list
In reply to Anatoly BurakovPreviously, to calculate length of memory area covered by a memseg list, we would've needed to multiply page size by length of fbarray backing that memseg list. This is not obvious and unnecessarily low level, so store length in the memseg list itself.
--- drivers/bus/pci/linux/pci.c | 2 +- lib/librte_eal/bsdapp/eal/eal_memory.c | 2 ++ lib/librte_eal/common/eal_common_memory.c | 5 ++--- lib/librte_eal/common/include/rte_eal_memconfig.h | 1 + lib/librte_eal/linuxapp/eal/eal_memalloc.c | 3 ++- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +++- 6 files changed, 11 insertions(+), 6 deletions(-)
drivers/bus/pci/linux/pci.c +1 -1
@@ -119,7 +119,7 @@ rte_pci_unmap_device(struct rte_pci_device *dev)static int find_max_end_va (const struct rte_memseg_list *msl,void *arg) { -size_t sz = msl->memseg_arr.len * msl->page_sz; +size_t sz = msl->len;void *end_va =RTE_PTR_ADD (msl->base_va, sz);void **max_va = arg;
lib/librte_eal/bsdapp/eal/eal_memory.c +2 -0
@@ -79,6 +79,7 @@ rte_eal_hugepage_init(void) } msl->base_va = addr; msl->page_sz = page_sz; + msl->len = internal_config.memory; msl->socket_id =0 ;/* populate memsegs. each memseg is 1 page long */
@@ -370,6 +371,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
lib/librte_eal/common/eal_common_memory.c +2 -3
@@ -171,7 +171,7 @@ virt2memseg(const void *addr, const struct rte_memseg_list *msl)/* a memseg list was specified, check if it's the right one */ start = msl->base_va; - end =RTE_PTR_ADD (start, (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr < start || addr >= end)return NULL ;
@@ -194,8 +194,7 @@ virt2memseg_list(const void *addr) msl = &mcfg->memsegs[msl_idx]; start = msl->base_va; - end =RTE_PTR_ADD (start, - (size_t )msl->page_sz * msl->memseg_arr.len); + end =RTE_PTR_ADD (start, msl->len);if (addr >= start && addr < end)break ; }
lib/librte_eal/common/include/rte_eal_memconfig.h +1 -0
@@ -30,6 +30,7 @@ struct rte_memseg_list {uint64_t addr_64;/**< Makes sure addr is always 64-bits */ }; +size_t len;/**< Length of memory area covered by this memseg list. */ int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ volatile uint32_t version;/**< version number for multiprocess sync. */
lib/librte_eal/linuxapp/eal/eal_memalloc.c +2 -1
@@ -986,7 +986,7 @@ free_seg_walk(const struct rte_memseg_list *msl, void *arg)int msl_idx, seg_idx, ret, dir_fd =-1 ; start_addr = (uintptr_t ) msl->base_va; - end_addr = start_addr + msl->memseg_arr.len * (size_t )msl->page_sz; + end_addr = start_addr + msl->len;if ((uintptr_t )wa->ms->addr < start_addr || (uintptr_t )wa->ms->addr >= end_addr)
@@ -1472,6 +1472,7 @@ secondary_msl_create_walk(const struct rte_memseg_list *msl,return -1 ; } local_msl->base_va = primary_msl->base_va; + local_msl->len = primary_msl->len;return 0 ; }
lib/librte_eal/linuxapp/eal/eal_memory.c +3 -1
@@ -861,6 +861,7 @@ alloc_va_space(struct rte_memseg_list *msl)return -1 ; } msl->base_va = addr; + msl->len = mem_sz;return 0 ; }
@@ -1369,6 +1370,7 @@ eal_legacy_hugepage_init(void) msl->base_va = addr; msl->page_sz = page_sz; msl->socket_id =0 ; + msl->len = internal_config.memory;/* populate memsegs. each memseg is one page long */ for (cur_seg =0 ; cur_seg < n_segs; cur_seg++) {
@@ -1615,7 +1617,7 @@ eal_legacy_hugepage_init(void)if (msl->memseg_arr.count >0 )continue ;/* this is an unused list, deallocate it */ - mem_sz = (size_t )msl->page_sz * msl->memseg_arr.len; + mem_sz = msl->len;munmap (msl->base_va, mem_sz); msl->base_va =NULL ;
Signature · 1 line
2.17.1
PATCH v5 5/21 flow_classify: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_flow_classify/rte_flow_classify.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
lib/librte_flow_classify/rte_flow_classify.c +1 -2
@@ -247,8 +247,7 @@ rte_flow_classifier_check_params(struct rte_flow_classifier_params *params) }/* socket */ -if ((params->socket_id <0 ) || - (params->socket_id >=RTE_MAX_NUMA_NODES )) { +if (params->socket_id <0 ) {RTE_FLOW_CLASSIFY_LOG (ERR ,"%s: Incorrect value for parameter socket_id\n" , __func__);
Signature · 1 line
2.17.1
PATCH v5 8/21 malloc: add name to malloc heaps
In reply to Anatoly BurakovWe will need to refer to external heaps in some way. While we use heap ID's internally, for external API use it has to be something more user-friendly. So, we will be using a string to uniquely identify a heap.
--- doc/guides/rel_notes/release_18_11.rst | 1 + lib/librte_eal/common/include/rte_malloc_heap.h | 2 ++ lib/librte_eal/common/malloc_heap.c | 17 ++++++++++++++++- lib/librte_eal/common/rte_malloc.c | 1 + 4 files changed, 20 insertions(+), 1 deletion(-)
doc/guides/rel_notes/release_18_11.rst +1 -0
@@ -124,6 +124,7 @@ ABI Changes * eal: EAL library ABI version was changed due to previously announced work on supporting external memory in DPDK. Structure ``rte_memseg_list`` now has a new flag indicating whether the memseg list refers to external memory. + Structure ``rte_malloc_heap`` now has a ``heap_name`` string member. Removed Items -------------
lib/librte_eal/common/include/rte_malloc_heap.h +2 -0
@@ -12,6 +12,7 @@/* Number of free lists per heap, grouped by size. */ #define RTE_HEAP_NUM_FREELISTS 13 +#define RTE_HEAP_NAME_MAX_LEN 32/* dummy definition, for pointers */ struct malloc_elem ;
@@ -28,6 +29,7 @@ struct malloc_heap {unsigned alloc_count;size_t total_size;unsigned int socket_id; + char name[RTE_HEAP_NAME_MAX_LEN]; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +16 -1
@@ -127,7 +127,6 @@ malloc_add_seg(const struct rte_memseg_list *msl,malloc_heap_add_memory (heap, found_msl, ms->addr, len); heap->total_size += len; - heap->socket_id = msl->socket_id;RTE_LOG (DEBUG ,EAL ,"Added %zuM to heap on socket %i\n" , len >>20 , msl->socket_id);
@@ -1020,6 +1019,22 @@ intrte_eal_malloc_heap_init (void) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +unsigned int i; + +if (rte_eal_process_type () ==RTE_PROC_PRIMARY ) { +/* assign names to default DPDK heaps */ +for (i =0 ; i <rte_socket_count (); i++) { +struct malloc_heap *heap = &mcfg->malloc_heaps[i]; +char heap_name[RTE_HEAP_NAME_MAX_LEN ]; +int socket_id =rte_socket_id_by_idx (i); + +snprintf (heap_name,sizeof (heap_name) -1 , +"socket_%i" , socket_id); +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); + heap->socket_id = socket_id; + } + } +if (register_mp_requests ()) {RTE_LOG (ERR ,EAL ,"Couldn't register malloc multiprocess actions\n" );
lib/librte_eal/common/rte_malloc.c +1 -0
@@ -202,6 +202,7 @@ rte_malloc_dump_stats(FILE *f, __rte_unused const char *type)malloc_heap_get_stats (heap, &sock_stats);fprintf (f,"Heap id:%u\n" , heap_id); +fprintf (f,"\tHeap name:%s\n" , heap->name);fprintf (f,"\tHeap_size:%zu,\n" , sock_stats.heap_totalsz_bytes);fprintf (f,"\tFree_size:%zu,\n" , sock_stats.heap_freesz_bytes);fprintf (f,"\tAlloc_size:%zu,\n" , sock_stats.heap_allocsz_bytes);
Signature · 1 line
2.17.1
v5 cover Support externally allocated memory in DPDK
In reply to Anatoly BurakovThis is a proposal to enable using externally allocated memory in DPDK.
In a nutshell, here is what is being done here:
- Index internal malloc heaps by NUMA node index, rather than NUMA node itself (external heaps will have ID's in order of creation)
- Add identifier string to malloc heap, to uniquely identify it
- Each new heap will receive a unique socket ID that will be used by allocator to decide from which heap (internal or external) to allocate requested amount of memory
- Allow creating named heaps and add/remove memory to/from those heaps
- Allocate memseg lists at runtime, to keep track of IOVA addresses of externally allocated memory
- If IOVA addresses aren't provided, use RTE_BAD_IOVA
- Allow malloc and memzones to allocate from external heaps
- Allow other data structures to allocate from externall heaps
The responsibility to ensure memory is accessible before using it is on the shoulders of the user - there is no checking done with regards to validity of the memory (nor could there be...).
The general approach is to create heap and add memory into it. For any other process wishing to use the same memory, said memory must first be attached (otherwise some things will not work).
A design decision was made to make multiprocess synchronization a manual process. Due to underlying issues with attaching to fbarrays in secondary processes, this design was deemed to be better because we don't want to fail to create external heap in the primary because something in the secondary has failed when in fact we may not eve have wanted this memory to be accessible in the secondary in the first place.
Using external memory in multiprocess is *hard*, because not only memory space needs to be preallocated, but it also needs to be attached in each process to allow other processes to access the page table. The attach API call may or may not succeed, depending on memory layout, for reasons similar to other multiprocess failures. This is treated as a "known issue" for this release.
v5 -> v4 changes:
- All processes are now able to create and destroy malloc heaps
- Memory is automatically mapped for DMA on adding it to heap
- Mem event callbacks are triggered on adding/removing memory
- Fixed compile issues on FreeBSD
- Better documentation on API/ABI changes
v4 -> v3 changes:
- Dropped sample application in favor of new testpmd flag
- Added new flag to testpmd, with four options of mempool allocation
- Added new API to check if a socket ID belongs to an external heap
- Adjusted malloc and mempool code to not make any assumptions about IOVA-contiguousness when dealing with externally allocated memory
v3 -> v2 changes:
- Rebase on top of latest master
- Clarifications added to mempool code as per Andrew Rynchenko's comments
v2 -> v1 changes:
- Fixed NULL dereference on heap socket ID lookup
- Fixed memseg offset calculation on adding memory to heap
- Improved unit test to test for above bugfixes
- Restricted heap creation to primary processes only
- Added sample application
- Added documentation
RFC -> v1 changes:
- Removed the "named heaps" API, allocate using fake socket ID instead
- Added multiprocess support
- Everything is now thread-safe
- Numerous bugfixes and API improvements
Anatoly Burakov (21):
mem: add length to memseg list
mem: allow memseg lists to be marked as external
malloc: index heaps using heap ID rather than NUMA node
mem: do not check for invalid socket ID
flow_classify: do not check for invalid socket ID
pipeline: do not check for invalid socket ID
sched: do not check for invalid socket ID
malloc: add name to malloc heaps
malloc: add function to query socket ID of named heap
malloc: add function to check if socket is external
malloc: allow creating malloc heaps
malloc: allow destroying heaps
malloc: allow adding memory to named heaps
malloc: allow removing memory from named heaps
malloc: allow attaching to external memory chunks
malloc: allow detaching from external memory
malloc: enable event callbacks for external memory
test: add unit tests for external memory support app/testpmd: add support for external memory
doc: add external memory feature to the release notes
doc: add external memory feature to programmer's guide
app/test-pmd/config.c | 21 +- app/test-pmd/parameters.c | 23 +- app/test-pmd/testpmd.c | 305 ++++++++++++- app/test-pmd/testpmd.h | 13 +- config/common_base | 1 + config/rte_config.h | 1 + .../prog_guide/env_abstraction_layer.rst | 37 ++ doc/guides/rel_notes/deprecation.rst | 15 - doc/guides/rel_notes/release_18_11.rst | 28 +- doc/guides/testpmd_app_ug/run_app.rst | 12 + drivers/bus/fslmc/fslmc_vfio.c | 14 +- drivers/bus/pci/linux/pci.c | 2 +- drivers/net/mlx4/mlx4_mr.c | 3 + drivers/net/mlx5/mlx5.c | 5 +- drivers/net/mlx5/mlx5_mr.c | 3 + drivers/net/virtio/virtio_user/vhost_kernel.c | 5 +- .../net/virtio/virtio_user/virtio_user_dev.c | 8 + lib/librte_eal/bsdapp/eal/Makefile | 2 +- lib/librte_eal/bsdapp/eal/eal.c | 3 + lib/librte_eal/bsdapp/eal/eal_memory.c | 9 +- lib/librte_eal/common/eal_common_memory.c | 8 +- lib/librte_eal/common/eal_common_memzone.c | 8 +- .../common/include/rte_eal_memconfig.h | 9 +- lib/librte_eal/common/include/rte_malloc.h | 192 ++++++++ .../common/include/rte_malloc_heap.h | 3 + lib/librte_eal/common/include/rte_memory.h | 9 + lib/librte_eal/common/malloc_elem.c | 10 +- lib/librte_eal/common/malloc_heap.c | 316 +++++++++++-- lib/librte_eal/common/malloc_heap.h | 17 + lib/librte_eal/common/rte_malloc.c | 429 +++++++++++++++++- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 10 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 12 +- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +- lib/librte_eal/linuxapp/eal/eal_vfio.c | 27 +- lib/librte_eal/meson.build | 2 +- lib/librte_eal/rte_eal_version.map | 8 + lib/librte_flow_classify/rte_flow_classify.c | 3 +- lib/librte_mempool/rte_mempool.c | 57 ++- lib/librte_pipeline/rte_pipeline.c | 3 +- lib/librte_sched/rte_sched.c | 2 +- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 ++++++++++++++++ test/test/test_malloc.c | 3 + test/test/test_memzone.c | 3 + 47 files changed, 1913 insertions(+), 139 deletions(-) create mode 100644 test/test/test_external_mem.c
Signature · 1 line
2.17.1
PATCH v5 7/21 sched: do not check for invalid socket ID
We will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
--- lib/librte_sched/rte_sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/librte_sched/rte_sched.c +1 -1
@@ -329,7 +329,7 @@ rte_sched_port_check_params(struct rte_sched_port_params *params)return -1 ;/* socket */ -if ((params->socket <0 ) || (params->socket >=RTE_MAX_NUMA_NODES )) +if (params->socket <0 )return -3 ;/* rate */
Signature · 1 line
2.17.1
PATCH v5 9/21 malloc: add function to query socket ID of named heap
In reply to Anatoly BurakovWhen we will be creating external heaps, they will have their own "fake" socket ID, so add a function that will map the heap name to its socket ID.
--- lib/librte_eal/common/include/rte_malloc.h | 14 ++++++++ lib/librte_eal/common/rte_malloc.c | 37 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 52 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +14 -0
@@ -263,6 +263,20 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Find socket ID corresponding to a named heap. +* +* @param name +* Heap name to find socket ID for +* @return +* Socket ID in case of success (a non-negative number) +* -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``name`` was NULL +* ENOENT - heap identified by the name ``name`` was not found +*/ +int __rte_experimental +rte_malloc_heap_get_socket (const char *name); +/** * Dump statistics. *
lib/librte_eal/common/rte_malloc.c +37 -0
@@ -8,6 +8,7 @@#include <string.h> #include <sys/queue.h> +#include <rte_errno.h> #include <rte_memcpy.h> #include <rte_memory.h> #include <rte_eal.h>
@@ -183,6 +184,42 @@ rte_malloc_dump_heaps(FILE *f)rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); } +int +rte_malloc_heap_get_socket (const char *name) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +struct malloc_heap *heap =NULL ; +unsigned int idx; +int ret; + +if (name ==NULL || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) ==0 || +strnlen (name,RTE_HEAP_NAME_MAX_LEN ) == +RTE_HEAP_NAME_MAX_LEN ) { + rte_errno =EINVAL ; +return -1 ; + } +rte_rwlock_read_lock (&mcfg->memory_hotplug_lock); +for (idx =0 ; idx <RTE_MAX_HEAPS ; idx++) { +struct malloc_heap *tmp = &mcfg->malloc_heaps[idx]; + +if (!strncmp (name, tmp->name,RTE_HEAP_NAME_MAX_LEN )) { + heap = tmp; +break ; + } + } + +if (heap !=NULL ) { + ret = heap->socket_id; + }else { + rte_errno =ENOENT ; + ret =-1 ; + } +rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +/* * Print stats on memory type. If type is NULL, info on all types is printed */
lib/librte_eal/rte_eal_version.map +1 -0
@@ -318,6 +318,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_get_socket; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
Signature · 1 line
2.17.1
PATCH v5 11/21 malloc: allow creating malloc heaps
In reply to Anatoly BurakovAdd API to allow creating new malloc heaps. They will be created with socket ID's going above RTE_MAX_NUMA_NODES, to avoid clashing with internal heaps.
--- doc/guides/rel_notes/release_18_11.rst | 2 + .../common/include/rte_eal_memconfig.h | 3 ++ lib/librte_eal/common/include/rte_malloc.h | 19 +++++++ lib/librte_eal/common/malloc_heap.c | 37 +++++++++++++ lib/librte_eal/common/malloc_heap.h | 3 ++ lib/librte_eal/common/rte_malloc.c | 52 +++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 7 files changed, 117 insertions(+)
doc/guides/rel_notes/release_18_11.rst +2 -0
@@ -125,6 +125,8 @@ ABI Changes supporting external memory in DPDK. Structure ``rte_memseg_list`` now has a new flag indicating whether the memseg list refers to external memory. Structure ``rte_malloc_heap`` now has a ``heap_name`` string member. + Structure ``rte_eal_memconfig`` has been extended to contain next socket + ID for externally allocated memory segments. Removed Items -------------
lib/librte_eal/common/include/rte_eal_memconfig.h +3 -0
@@ -75,6 +75,9 @@ struct rte_mem_config {/* Heaps of Malloc */ struct malloc_heap malloc_heaps[RTE_MAX_HEAPS ]; +/* next socket ID for external malloc heap */ +int next_socket_id; +/* address of mem_config in primary process. used to map shared config into * exact same address the primary process maps it. */
lib/librte_eal/common/include/rte_malloc.h +19 -0
@@ -263,6 +263,25 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Creates a new empty malloc heap with a specified name. +* +* @note Heaps created via this call will automatically get assigned a unique +* socket ID, which can be found using ``rte_malloc_heap_get_socket()`` +* +* @param heap_name +* Name of the heap to create. +* +* @return +* - 0 on successful creation +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - ``heap_name`` was NULL, empty or too long +* EEXIST - heap by name of ``heap_name`` already exists +* ENOSPC - no more space in internal config to store a new heap +*/ +int __rte_experimental +rte_malloc_heap_create (const char *heap_name); +/** * Find socket ID corresponding to a named heap. *
lib/librte_eal/common/malloc_heap.c +37 -0
@@ -29,6 +29,10 @@#include "malloc_heap.h" #include "malloc_mp.h" +/* start external socket ID's at a very high number */ +#define CONST_MAX (a, b) (a > b ? a : b)/* RTE_MAX is not a constant */ +#define EXTERNAL_HEAP_MIN_SOCKET_ID (CONST_MAX((1 << 8), RTE_MAX_NUMA_NODES)) +static unsigned check_hugepage_sz (unsigned flags,uint64_t hugepage_sz) {
@@ -1015,6 +1019,36 @@ malloc_heap_dump(struct malloc_heap *heap, FILE *f)rte_spinlock_unlock (&heap->lock); } +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +uint32_t next_socket_id = mcfg->next_socket_id; + +/* prevent overflow. did you really create 2 billion heaps??? */ +if (next_socket_id >INT32_MAX ) { +RTE_LOG (ERR ,EAL ,"Cannot assign new socket ID's\n" ); + rte_errno =ENOSPC ; +return -1 ; + } + +/* initialize empty heap */ + heap->alloc_count =0 ; + heap->first =NULL ; + heap->last =NULL ; +LIST_INIT (heap->free_head); +rte_spinlock_init (&heap->lock); + heap->total_size =0 ; + heap->socket_id = next_socket_id; + +/* we hold a global mem hotplug writelock, so it's safe to increment */ + mcfg->next_socket_id++; + +/* set up name */ +strlcpy (heap->name, heap_name,RTE_HEAP_NAME_MAX_LEN ); +return 0 ; +} +int rte_eal_malloc_heap_init (void ) {
@@ -1022,6 +1056,9 @@ rte_eal_malloc_heap_init(void)unsigned int i;if (rte_eal_process_type () ==RTE_PROC_PRIMARY ) { +/* assign min socket ID to external heaps */ + mcfg->next_socket_id =EXTERNAL_HEAP_MIN_SOCKET_ID ; +/* assign names to default DPDK heaps */ for (i =0 ; i <rte_socket_count (); i++) {struct malloc_heap *heap = &mcfg->malloc_heaps[i];
lib/librte_eal/common/malloc_heap.h +3 -0
@@ -33,6 +33,9 @@ void * malloc_heap_alloc_biggest(const char *type, int socket, unsigned int flags, size_t align, bool contig); +int +malloc_heap_create (struct malloc_heap *heap,const char *heap_name); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +52 -0
61 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -318,6 +318,7 @@ EXPERIMENTAL { rte_fbarray_set_used; rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; + rte_malloc_heap_create; rte_malloc_heap_get_socket; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register;
Signature · 1 line
2.17.1
PATCH v5 13/21 malloc: allow adding memory to named heaps
In reply to Anatoly BurakovAdd an API to add externally allocated memory to malloc heap. The memory will be stored in memseg lists like regular DPDK memory. Multiple segments are allowed within a heap. If IOVA table is not provided, IOVA addresses are filled in with RTE_BAD_IOVA.
--- lib/librte_eal/common/include/rte_malloc.h | 39 ++++++++++++ lib/librte_eal/common/malloc_heap.c | 74 ++++++++++++++++++++++ lib/librte_eal/common/malloc_heap.h | 4 ++ lib/librte_eal/common/rte_malloc.c | 51 +++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 169 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +39 -0
@@ -263,6 +263,45 @@ int rte_malloc_get_socket_stats(int socket, struct rte_malloc_socket_stats *socket_stats); +/** +* Add memory chunk to a heap with specified name. +* +* @note Multiple memory chunks can be added to the same heap +* +* @note Memory must be previously allocated for DPDK to be able to use it as a +* malloc heap. Failing to do so will result in undefined behavior, up to and +* including segmentation faults. +* +* @note Calling this function will erase any contents already present at the +* supplied memory address. +* +* @param heap_name +* Name of the heap to add memory chunk to +* @param va_addr +* Start of virtual area to add to the heap +* @param len +* Length of virtual area to add to the heap +* @param iova_addrs +* Array of page IOVA addresses corresponding to each page in this memory +* area. Can be NULL, in which case page IOVA addresses will be set to +* RTE_BAD_IOVA. +* @param n_pages +* Number of elements in the iova_addrs array. Ignored if ``iova_addrs`` +* is NULL. +* @param page_sz +* Page size of the underlying memory +* +* @return +* - 0 on success +* - -1 in case of error, with rte_errno set to one of the following: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to add memory to a reserved heap +* ENOSPC - no more space in internal config to store a new memory chunk +*/ +int __rte_experimental +rte_malloc_heap_memory_add (const char *heap_name,void *va_addr,size_t len, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/malloc_heap.c +74 -0
80 lines not on this page · raw message
lib/librte_eal/common/malloc_heap.h +4 -0
@@ -39,6 +39,10 @@ malloc_heap_create(struct malloc_heap *heap, const char *heap_name);int malloc_heap_destroy (struct malloc_heap *heap); +int +malloc_heap_add_external_memory (struct malloc_heap *heap,void *va_addr, +rte_iova_t iova_addrs[],unsigned int n_pages,size_t page_sz); +int malloc_heap_free (struct malloc_elem *elem);
lib/librte_eal/common/rte_malloc.c +51 -0
57 lines not on this page · raw message
lib/librte_eal/rte_eal_version.map +1 -0
@@ -321,6 +321,7 @@ EXPERIMENTAL { rte_malloc_heap_create; rte_malloc_heap_destroy; rte_malloc_heap_get_socket; + rte_malloc_heap_memory_add; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister;
Signature · 1 line
2.17.1
PATCH v5 16/21 malloc: allow detaching from external memory
In reply to Anatoly BurakovAdd API to detach from existing chunk of external memory in a process.
--- lib/librte_eal/common/include/rte_malloc.h | 27 +++++++++++++++++++ lib/librte_eal/common/rte_malloc.c | 31 +++++++++++++++++----- lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 52 insertions(+), 7 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +27 -0
@@ -315,6 +315,9 @@ rte_malloc_heap_memory_add(const char *heap_name, void *va_addr, size_t len,* @note Memory area must not contain any allocated elements to allow its * removal from the heap * +* @note All other processes must detach from the memory chunk prior to it being +* removed from the heap. +* * @param heap_name * Name of the heap to remove memory from * @param va_addr
@@ -357,6 +360,30 @@ rte_malloc_heap_memory_remove(const char *heap_name, void *va_addr, size_t len); int __rte_experimentalrte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len); +/** +* Detach from a chunk of external memory in secondary process. +* +* @note This function must be called in before any attempt is made to remove +* external memory from the heap in another process. This function does *not* +* need to be called if a call to ``rte_malloc_heap_memory_remove`` will be +* called in current process. +* +* @param heap_name +* Heap name to which this chunk of memory belongs +* @param va_addr +* Start address of memory chunk to attach to +* @param len +* Length of memory chunk to attach to +* @return +* 0 on successful detach +* -1 on unsuccessful detach, with rte_errno set to indicate cause for error: +* EINVAL - one of the parameters was invalid +* EPERM - attempted to detach memory from a reserved heap +* ENOENT - heap or memory chunk was not found +*/ +int __rte_experimental +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len); +/** * Creates a new empty malloc heap with a specified name. *
lib/librte_eal/common/rte_malloc.c +24 -7
@@ -422,10 +422,11 @@ struct sync_mem_walk_arg {void *va_addr;size_t len;int result; +bool attach; };static int -attach_mem_walk (const struct rte_memseg_list *msl,void *arg) +sync_mem_walk (const struct rte_memseg_list *msl,void *arg) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct sync_mem_walk_arg *wa = arg;
@@ -440,7 +441,10 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg) msl_idx = msl - mcfg->memsegs; found_msl = &mcfg->memsegs[msl_idx]; - ret =rte_fbarray_attach (&found_msl->memseg_arr); +if (wa->attach) + ret =rte_fbarray_attach (&found_msl->memseg_arr); +else + ret =rte_fbarray_detach (&found_msl->memseg_arr);if (ret <0 ) wa->result = -rte_errno;
@@ -451,8 +455,8 @@ attach_mem_walk(const struct rte_memseg_list *msl, void *arg)return 0 ; } -int -rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +static int +sync_memory (const char *heap_name,void *va_addr,size_t len,bool attach) {struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config;struct malloc_heap *heap =NULL ;
@@ -475,20 +479,21 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len) ret =-1 ; goto unlock; } -/* we shouldn't be able to attach to internal heaps */ +/* we shouldn't be able to sync to internal heaps */ if (heap->socket_id <RTE_MAX_NUMA_NODES ) { rte_errno =EPERM ; ret =-1 ; goto unlock; } -/* find corresponding memseg list to attach to */ +/* find corresponding memseg list to sync to */ wa.va_addr = va_addr; wa.len = len; wa.result = -ENOENT ;/* fail unless explicitly told to succeed */ + wa.attach = attach;/* we're already holding a read lock */ -rte_memseg_list_walk_thread_unsafe (attach_mem_walk, &wa); +rte_memseg_list_walk_thread_unsafe (sync_mem_walk, &wa);if (wa.result <0 ) { rte_errno = -wa.result;
@@ -501,6 +506,18 @@ rte_malloc_heap_memory_attach(const char *heap_name, void *va_addr, size_t len)return ret; } +int +rte_malloc_heap_memory_attach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, true); +} + +int +rte_malloc_heap_memory_detach (const char *heap_name,void *va_addr,size_t len) +{ +return sync_memory (heap_name, va_addr, len, false); +} +int rte_malloc_heap_create (const char *heap_name) {
lib/librte_eal/rte_eal_version.map +1 -0
@@ -323,6 +323,7 @@ EXPERIMENTAL { rte_malloc_heap_get_socket; rte_malloc_heap_memory_add; rte_malloc_heap_memory_attach; + rte_malloc_heap_memory_detach; rte_malloc_heap_memory_remove; rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register;
Signature · 1 line
2.17.1
PATCH v5 4/21 mem: do not check for invalid socket ID
In reply to Anatoly BurakovWe will be assigning "invalid" socket ID's to external heap, and malloc will now be able to verify if a supplied socket ID is in fact a valid one, rendering parameter checks for sockets obsolete.
This changes the semantics of what we understand by "socket ID", so document the change in the release notes.
--- doc/guides/rel_notes/release_18_11.rst | 7 +++++++ lib/librte_eal/common/eal_common_memzone.c | 8 +++++--- lib/librte_eal/common/malloc_heap.c | 2 +- lib/librte_eal/common/rte_malloc.c | 4 ---- 4 files changed, 13 insertions(+), 8 deletions(-)
doc/guides/rel_notes/release_18_11.rst +7 -0
@@ -98,6 +98,13 @@ API Changes users of memseg-walk-related functions, as they will now have to skip externally allocated segments in most cases if the intent is to only iterate over internal DPDK memory. + - ``socket_id`` parameter across the entire DPDK has gained additional + meaning, as some socket ID's will now be representing externally allocated + memory. No changes will be required for existing code as backwards + compatibility will be kept, and those who do not use this feature will not + see these extra socket ID's. Any new API's must not check socket ID + parameters themselves, and must instead leave it to the memory subsystem to + decide whether socket ID is a valid one. ABI Changes -----------
lib/librte_eal/common/eal_common_memzone.c +5 -3
@@ -120,13 +120,15 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,return NULL ; } -if ((socket_id !=SOCKET_ID_ANY ) && - (socket_id >=RTE_MAX_NUMA_NODES || socket_id <0 )) { +if ((socket_id !=SOCKET_ID_ANY ) && socket_id <0 ) { rte_errno =EINVAL ;return NULL ; } -if (!rte_eal_has_hugepages ()) +/* only set socket to SOCKET_ID_ANY if we aren't allocating for an +* external heap. +*/ +if (!rte_eal_has_hugepages () && socket_id <RTE_MAX_NUMA_NODES ) socket_id =SOCKET_ID_ANY ; contig = (flags &RTE_MEMZONE_IOVA_CONTIG ) !=0 ;
lib/librte_eal/common/malloc_heap.c +1 -1
@@ -647,7 +647,7 @@ malloc_heap_alloc(const char *type, size_t size, int socket_arg,if (size ==0 || (align && !rte_is_power_of_2 (align)))return NULL ; -if (!rte_eal_has_hugepages ()) +if (!rte_eal_has_hugepages () && socket_arg <RTE_MAX_NUMA_NODES ) socket_arg =SOCKET_ID_ANY ;if (socket_arg ==SOCKET_ID_ANY )
lib/librte_eal/common/rte_malloc.c +0 -4
@@ -47,10 +47,6 @@ rte_malloc_socket(const char *type, size_t size, unsigned int align,if (!rte_eal_has_hugepages ()) socket_arg =SOCKET_ID_ANY ; -/* Check socket parameter */ -if (socket_arg >=RTE_MAX_NUMA_NODES ) -return NULL ; -return malloc_heap_alloc (type, size, socket_arg,0 , align ==0 ?1 : align,0 , false); }
Signature · 1 line
2.17.1
PATCH v5 10/21 malloc: add function to check if socket is external
In reply to Anatoly BurakovAn API is needed to check whether a particular socket ID belongs to an internal or external heap. Prime user of this would be mempool allocator, because normal assumptions of IOVA contiguousness in IOVA as VA mode do not hold in case of externally allocated memory.
--- lib/librte_eal/common/include/rte_malloc.h | 15 +++++++++++++ lib/librte_eal/common/rte_malloc.c | 25 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + lib/librte_mempool/rte_mempool.c | 22 ++++++++++++++++--- 4 files changed, 60 insertions(+), 3 deletions(-)
lib/librte_eal/common/include/rte_malloc.h +15 -0
@@ -277,6 +277,21 @@ rte_malloc_get_socket_stats(int socket, int __rte_experimentalrte_malloc_heap_get_socket (const char *name); +/** +* Check if a given socket ID refers to externally allocated memory. +* +* @note Passing SOCKET_ID_ANY will return 0. +* +* @param socket_id +* Socket ID to check +* @return +* 1 if socket ID refers to externally allocated memory +* 0 if socket ID refers to internal DPDK memory +* -1 if socket ID is invalid +*/ +int __rte_experimental +rte_malloc_heap_socket_is_external (int socket_id); +/** * Dump statistics. *
lib/librte_eal/common/rte_malloc.c +25 -0
@@ -220,6 +220,31 @@ rte_malloc_heap_get_socket(const char *name)return ret; } +int +rte_malloc_heap_socket_is_external (int socket_id) +{ +struct rte_mem_config *mcfg =rte_eal_get_configuration ()->mem_config; +unsigned int idx; +int ret =-1 ; + +if (socket_id ==SOCKET_ID_ANY ) +return 0 ; + +rte_rwlock_read_lock (&mcfg->memory_hotplug_lock); +for (idx =0 ; idx <RTE_MAX_HEAPS ; idx++) { +struct malloc_heap *tmp = &mcfg->malloc_heaps[idx]; + +if ((int )tmp->socket_id == socket_id) { +/* external memory always has large socket ID's */ + ret = tmp->socket_id >=RTE_MAX_NUMA_NODES ; +break ; + } + } +rte_rwlock_read_unlock (&mcfg->memory_hotplug_lock); + +return ret; +} +/* * Print stats on memory type. If type is NULL, info on all types is printed */
lib/librte_eal/rte_eal_version.map +1 -0
@@ -319,6 +319,7 @@ EXPERIMENTAL { rte_log_register_type_and_pick_level; rte_malloc_dump_heaps; rte_malloc_heap_get_socket; + rte_malloc_heap_socket_is_external; rte_mem_alloc_validator_register; rte_mem_alloc_validator_unregister; rte_mem_event_callback_register;
lib/librte_mempool/rte_mempool.c +19 -3
@@ -428,12 +428,18 @@ rte_mempool_populate_default(struct rte_mempool *mp)rte_iova_t iova;unsigned mz_id, n;int ret; -bool no_contig, try_contig, no_pageshift; +bool no_contig, try_contig, no_pageshift, external; ret =mempool_ops_alloc_once (mp);if (ret !=0 )return ret; +/* check if we can retrieve a valid socket ID */ + ret =rte_malloc_heap_socket_is_external (mp->socket_id); +if (ret <0 ) +return -EINVAL ; + external = ret; +/* mempool must not be populated */ if (mp->nb_mem_chunks !=0 )return -EEXIST ;
@@ -481,9 +487,19 @@ rte_mempool_populate_default(struct rte_mempool *mp)* in one contiguous chunk as well (otherwise we might end up wasting a * 1G page on a 10MB memzone). If we fail to get enough contiguous * memory, then we'll go and reserve space page-by-page. +* +* We also have to take into account the fact that memory that we're +* going to allocate from can belong to an externally allocated memory +* area, in which case the assumption of IOVA as VA mode being +* synonymous with IOVA contiguousness will not hold. We should also try +* to go for contiguous memory even if we're in no-huge mode, because +* external memory may in fact be IOVA-contiguous. */ - no_pageshift = no_contig ||rte_eal_iova_mode () ==RTE_IOVA_VA ; - try_contig = !no_contig && !no_pageshift &&rte_eal_has_hugepages (); + external =rte_malloc_heap_socket_is_external (mp->socket_id) ==1 ; + no_pageshift = no_contig || + (!external &&rte_eal_iova_mode () ==RTE_IOVA_VA ); + try_contig = !no_contig && !no_pageshift && + (rte_eal_has_hugepages () || external);if (no_pageshift) { pg_sz =0 ;
Signature · 1 line
2.17.1
PATCH v5 18/21 test: add unit tests for external memory support
In reply to Anatoly BurakovAdd simple unit tests to test external memory support. The tests are pretty basic and mostly consist of checking if invalid API calls are handled correctly, plus a simple allocation/deallocation test for malloc and memzone.
--- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 389 ++++++++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 test/test/test_external_mem.c
test/test/Makefile +1 -0
@@ -71,6 +71,7 @@ SRCS-y += test_bitmap.c SRCS-y += test_reciprocal_division.c SRCS-y += test_reciprocal_division_perf.c SRCS-y += test_fbarray.c +SRCS-y += test_external_mem.c SRCS-y += test_ring.c SRCS-y += test_ring_perf.c
test/test/autotest_data.py +10 -4
@@ -477,10 +477,16 @@ "Report": None, }, { - "Name": "Fbarray autotest", - "Command": "fbarray_autotest", - "Func": default_autotest, - "Report": None, + "Name": "Fbarray autotest", + "Command": "fbarray_autotest", + "Func": default_autotest, + "Report": None, + }, + { + "Name": "External memory autotest", + "Command": "external_mem_autotest", + "Func": default_autotest, + "Report": None, }, # #Please always keep all dump tests at the end and together!
test/test/meson.build +1 -0
@@ -155,6 +155,7 @@ test_names = [ 'eventdev_common_autotest', 'eventdev_octeontx_autotest', 'eventdev_sw_autotest', + 'external_mem_autotest', 'func_reentrancy_autotest', 'flow_classify_autotest', 'hash_scaling_autotest',
test/test/test_external_mem.c new file +389 -0
389 lines not on this page · raw message
Signature · 1 line
2.17.1