Support externally allocated memory in DPDK
Subsystems (2)
PATCH v8 21/21 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 | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+)
doc/guides/prog_guide/env_abstraction_layer.rst +37 -0
@@ -213,6 +213,43 @@ 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, and DMA mappings will not be performed + - 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
v9 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.
v9 -> v8 changes:
- Rebase on latest master
- Minor cosmetic testpmd changes as per Bernard's feedback
- Pack structures better (Stephen's suggestion)
- Touch pages before finding their IOVA address
v8 -> v7 changes:
- Rebase on latest master
- More documentation on ABI changes
v7 -> v6 changes:
- Fixed missing IOVA address setup in testpmd
- Fixed MLX drivers as per Yongseok's comments
- Added a check for invalid heap idx on adding memory to heap
v6 -> v5 changes:
- Fixed documentation formatting as per Marko's comments
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 | 325 ++++++++++++- 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 | 36 +- doc/guides/testpmd_app_ug/run_app.rst | 12 + drivers/bus/fslmc/fslmc_vfio.c | 13 +- drivers/bus/pci/linux/pci.c | 2 +- drivers/net/mlx5/mlx5.c | 4 +- drivers/net/virtio/virtio_user/vhost_kernel.c | 3 + .../net/virtio/virtio_user/virtio_user_dev.c | 6 + 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 | 11 +- 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 | 320 +++++++++++-- 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 + 45 files changed, 1936 insertions(+), 138 deletions(-) create mode 100644 test/test/test_external_mem.c
Signature · 1 line
2.17.1
PATCH v9 1/21 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.
This breaks ABI, so bump the EAL ABI version and document the change. Also, while we're breaking ABI, pack the members a little better.
--- doc/guides/rel_notes/release_18_11.rst | 8 +++++++- drivers/bus/pci/linux/pci.c | 2 +- lib/librte_eal/bsdapp/eal/Makefile | 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 | 3 ++- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 3 ++- lib/librte_eal/linuxapp/eal/eal_memory.c | 4 +++- lib/librte_eal/meson.build | 2 +- 10 files changed, 22 insertions(+), 11 deletions(-)
doc/guides/rel_notes/release_18_11.rst +7 -1
@@ -153,6 +153,12 @@ 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 field indicating length + of memory addressed by the segment list + + Removed Items -------------
@@ -198,7 +204,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.6 librte_flow_classify.so.1
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/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_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 +2 -1
@@ -30,9 +30,10 @@ struct rte_memseg_list {uint64_t addr_64;/**< Makes sure addr is always 64-bits */ }; -int socket_id;/**< Socket ID for all memsegs in this list. */ uint64_t page_sz;/**< Page size for all memsegs in this list. */ +int socket_id;/**< Socket ID for all memsegs in this list. */ volatile uint32_t version;/**< version number for multiprocess sync. */ +size_t len;/**< Length of memory area covered by this memseg list. */ struct rte_fbarray memseg_arr; };
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_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 ;
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'
Signature · 1 line
2.17.1
PATCH v9 2/21 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 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 | 8 +++++ drivers/bus/fslmc/fslmc_vfio.c | 6 +++- drivers/net/mlx5/mlx5.c | 4 ++- drivers/net/virtio/virtio_user/vhost_kernel.c | 3 ++ 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/eal.c | 10 +++++- lib/librte_eal/linuxapp/eal/eal_memalloc.c | 9 +++++ lib/librte_eal/linuxapp/eal/eal_vfio.c | 17 ++++++--- lib/librte_mempool/rte_mempool.c | 35 ++++++++++++++----- test/test/test_malloc.c | 3 ++ test/test/test_memzone.c | 3 ++ 19 files changed, 119 insertions(+), 38 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 +8 -0
@@ -118,6 +118,12 @@ API Changes Also, make sure to start the actual text at the margin. ========================================================= +* eal: ``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. + * mbuf: The ``__rte_mbuf_raw_free()`` and ``__rte_pktmbuf_prefree_seg()`` functions were deprecated since 17.05 and are replaced by ``rte_mbuf_raw_free()`` and ``rte_pktmbuf_prefree_seg()``.
@@ -157,6 +163,8 @@ ABI Changes supporting external memory in DPDK: - structure ``rte_memseg_list`` now has a new field indicating length of memory addressed by the segment list + - structure ``rte_memseg_list`` now has a new flag indicating whether + the memseg list refers to external memory Removed Items
drivers/bus/fslmc/fslmc_vfio.c +5 -1
@@ -318,11 +318,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) +const struct rte_memseg *ms,void *arg) {int *n_segs = arg;int ret; +/* if IOVA address is invalid, skip */ +if (ms->iova ==RTE_BAD_IOVA ) +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/mlx5/mlx5.c +3 -1
@@ -568,11 +568,13 @@ 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/virtio/virtio_user/vhost_kernel.c +3 -0
@@ -78,6 +78,9 @@ add_memseg_list(const struct rte_memseg_list *msl, void *arg)void *start_addr;uint64_t len; +if (msl->external) +return 0 ; +if (vm->nregions >= 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 +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
@@ -34,6 +34,7 @@ struct rte_memseg_list {int socket_id;/**< Socket ID for all memsegs in this list. */ volatile uint32_t version;/**< version number for multiprocess sync. */ size_t len;/**< Length of memory area covered by this memseg list. */ +unsigned int external;/**< 1 if this list points to external memory */ 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/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_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 v9 3/21 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.
This breaks the ABI, so document the changes.
--- config/common_base | 1 + config/rte_config.h | 1 + doc/guides/rel_notes/release_18_11.rst | 5 +- .../common/include/rte_eal_memconfig.h | 4 +- .../common/include/rte_malloc_heap.h | 1 + lib/librte_eal/common/malloc_heap.c | 102 +++++++++++++----- lib/librte_eal/common/malloc_heap.h | 3 + lib/librte_eal/common/rte_malloc.c | 41 ++++--- 8 files changed, 114 insertions(+), 44 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
doc/guides/rel_notes/release_18_11.rst +4 -1
@@ -165,7 +165,10 @@ ABI Changes of memory addressed by the segment list - 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 new field indicating socket + ID the malloc heap belongs to + - structure ``rte_mem_config`` has had its ``malloc_heaps`` array + resized from ``RTE_MAX_NUMA_NODES`` to ``RTE_MAX_HEAPS`` value Removed Items -------------
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
@@ -26,6 +26,7 @@ struct malloc_heap {struct malloc_elem *volatile last;unsigned alloc_count; +unsigned int socket_id;size_t total_size; } __rte_cache_aligned;
lib/librte_eal/common/malloc_heap.c +73 -29
208 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 v9 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
@@ -123,6 +123,13 @@ API Changes 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 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. * mbuf: The ``__rte_mbuf_raw_free()`` and ``__rte_pktmbuf_prefree_seg()`` functions were deprecated since 17.05 and are replaced by
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
@@ -651,7 +651,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 v9 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 v9 6/21 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 v9 7/21 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 v9 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.
This breaks the ABI, so document the change.
--- doc/guides/rel_notes/release_18_11.rst | 2 ++ 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, 21 insertions(+), 1 deletion(-)
doc/guides/rel_notes/release_18_11.rst +2 -0
@@ -176,6 +176,8 @@ ABI Changes ID the malloc heap belongs to - structure ``rte_mem_config`` has had its ``malloc_heaps`` array resized from ``RTE_MAX_NUMA_NODES`` to ``RTE_MAX_HEAPS`` value + - structure ``rte_malloc_heap`` now has a ``heap_name`` 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;unsigned int socket_id;size_t total_size; + char name[RTE_HEAP_NAME_MAX_LEN]; } __rte_cache_aligned;#endif /* _RTE_MALLOC_HEAP_H_ */
lib/librte_eal/common/malloc_heap.c +16 -1
@@ -131,7 +131,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);
@@ -1024,6 +1023,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
PATCH v9 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 v9 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 v9 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.
This breaks the ABI, so document the change.
--- 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
@@ -177,6 +177,8 @@ ABI Changes - structure ``rte_mem_config`` has had its ``malloc_heaps`` array resized from ``RTE_MAX_NUMA_NODES`` to ``RTE_MAX_HEAPS`` value - structure ``rte_malloc_heap`` now has a ``heap_name`` member + - structure ``rte_eal_memconfig`` has been extended to contain next + socket ID for externally allocated 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) {
@@ -1019,6 +1023,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 ) {
@@ -1026,6 +1060,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 v9 12/21 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
@@ -1053,6 +1053,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 v9 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 v9 14/21 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 v9 15/21 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 | 28 ++++++++ lib/librte_eal/common/rte_malloc.c | 83 ++++++++++++++++++++++ lib/librte_eal/rte_eal_version.map | 1 + 3 files changed, 112 insertions(+)
lib/librte_eal/common/include/rte_malloc.h +28 -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,6 +333,30 @@ 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. *
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
@@ -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 v9 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 v9 17/21 malloc: enable event callbacks for external memory
In reply to Anatoly BurakovWhen adding or removing external memory from the memory map, there may be actions that need to be taken on account of this memory (e.g. DMA mapping). Add support for triggering callbacks when adding, removing, attaching or detaching external memory.
Some memory event callback handlers will need additional logic to handle external memory regions. For example, virtio callback has to completely ignore externally allocated memory, because there is no way to find file descriptors backing the memory address in a generic fashion. All other callbacks have also been adjusted to handle RTE_BAD_IOVA as IOVA address, as this is one of the expected use cases for external memory support.
--- drivers/bus/fslmc/fslmc_vfio.c | 7 +++++ .../net/virtio/virtio_user/virtio_user_dev.c | 6 +++++ lib/librte_eal/common/malloc_heap.c | 7 +++++ lib/librte_eal/common/rte_malloc.c | 27 ++++++++++++++++--- lib/librte_eal/linuxapp/eal/eal_vfio.c | 10 +++++-- 5 files changed, 51 insertions(+), 6 deletions(-)
drivers/bus/fslmc/fslmc_vfio.c +7 -0
@@ -221,6 +221,13 @@ fslmc_memevent_cb(enum rte_mem_event type, const void *addr, size_t len, "alloc" : "dealloc", va, virt_addr, iova_addr, map_len); +/* iova_addr may be set to RTE_BAD_IOVA */ +if (iova_addr ==RTE_BAD_IOVA ) { +DPAA2_BUS_DEBUG ("Segment has invalid iova, skipping\n" ); + cur_len += map_len; +continue ; + } +if (type ==RTE_MEM_EVENT_ALLOC ) ret =fslmc_map_dma (virt_addr, iova_addr, map_len);else
drivers/net/virtio/virtio_user/virtio_user_dev.c +6 -0
@@ -301,8 +301,14 @@ virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused, void *arg) {struct virtio_user_dev *dev = arg; +struct rte_memseg_list *msl;uint16_t i; +/* ignore externally allocated memory */ + msl =rte_mem_virt2memseg_list (addr); +if (msl->external) +return ; +pthread_mutex_lock (&dev->mutex);if (dev->started == false)
lib/librte_eal/common/malloc_heap.c +7 -0
@@ -1031,6 +1031,9 @@ destroy_seg(struct malloc_elem *elem, size_t len) msl = elem->msl; +/* notify all subscribers that a memory area is going to be removed */ +eal_memalloc_mem_event_notify (RTE_MEM_EVENT_FREE , elem, len); +/* this element can be removed */ malloc_elem_free_list_remove (elem);malloc_elem_hide_region (elem, elem, len);
@@ -1120,6 +1123,10 @@ malloc_heap_add_external_memory(struct malloc_heap *heap, void *va_addr,RTE_LOG (DEBUG ,EAL ,"Added segment for heap %s starting at %p\n" , heap->name, va_addr); +/* notify all subscribers that a new memory area has been added */ +eal_memalloc_mem_event_notify (RTE_MEM_EVENT_ALLOC , + va_addr, seg_len); +return 0 ; }
lib/librte_eal/common/rte_malloc.c +23 -4
@@ -25,6 +25,7 @@#include <rte_malloc.h> #include "malloc_elem.h" #include "malloc_heap.h" +#include "eal_memalloc.h" /* Free the memory space back to heap */
@@ -441,15 +442,29 @@ sync_mem_walk(const struct rte_memseg_list *msl, void *arg) msl_idx = msl - mcfg->memsegs; found_msl = &mcfg->memsegs[msl_idx]; -if (wa->attach) +if (wa->attach) { ret =rte_fbarray_attach (&found_msl->memseg_arr); -else + }else { +/* notify all subscribers that a memory area is about to +* be removed +*/ +eal_memalloc_mem_event_notify (RTE_MEM_EVENT_FREE , + msl->base_va, msl->len); ret =rte_fbarray_detach (&found_msl->memseg_arr); + } -if (ret <0 ) +if (ret <0 ) { wa->result = -rte_errno; -else + }else { +/* notify all subscribers that a new memory area was +* added +*/ +if (wa->attach) +eal_memalloc_mem_event_notify ( +RTE_MEM_EVENT_ALLOC , + msl->base_va, msl->len); wa->result =0 ; + }return 1 ; }return 0 ;
@@ -499,6 +514,10 @@ sync_memory(const char *heap_name, void *va_addr, size_t len, bool attach) rte_errno = -wa.result; ret =-1 ; }else { +/* notify all subscribers that a new memory area was added */ +if (attach) +eal_memalloc_mem_event_notify (RTE_MEM_EVENT_ALLOC , + va_addr, len); ret =0 ; } unlock:
lib/librte_eal/linuxapp/eal/eal_vfio.c +8 -2
@@ -509,7 +509,7 @@ vfio_mem_event_callback(enum rte_mem_event type, const void *addr, size_t len, msl =rte_mem_virt2memseg_list (addr);/* for IOVA as VA mode, no need to care for IOVA addresses */ -if (rte_eal_iova_mode () ==RTE_IOVA_VA ) { +if (rte_eal_iova_mode () ==RTE_IOVA_VA && msl->external ==0 ) {uint64_t vfio_va = (uint64_t )(uintptr_t )addr;if (type ==RTE_MEM_EVENT_ALLOC ) vfio_dma_mem_map(default_vfio_cfg, vfio_va, vfio_va,
@@ -523,13 +523,19 @@ vfio_mem_event_callback(enum rte_mem_event type, const void *addr, size_t len,/* memsegs are contiguous in memory */ ms =rte_mem_virt2memseg (addr, msl);while (cur_len < len) { +/* some memory segments may have invalid IOVA */ +if (ms->iova ==RTE_BAD_IOVA ) { +RTE_LOG (DEBUG ,EAL ,"Memory segment at %p has bad IOVA, skipping\n" , + ms->addr); + goto next; + }if (type ==RTE_MEM_EVENT_ALLOC )vfio_dma_mem_map (default_vfio_cfg, ms->addr_64, ms->iova, ms->len,1 );else vfio_dma_mem_map (default_vfio_cfg, ms->addr_64, ms->iova, ms->len,0 ); - +next: cur_len += ms->len; ++ms; }
Signature · 1 line
2.17.1
PATCH v9 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
@@ -164,6 +164,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 v9 19/21 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 | 325 ++++++++++++++++++++++++-- app/test-pmd/testpmd.h | 13 +- doc/guides/testpmd_app_ug/run_app.rst | 12 + 5 files changed, 369 insertions(+), 25 deletions(-)
app/test-pmd/config.c +19 -2
@@ -2423,6 +2423,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) {
@@ -2431,12 +2448,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, xmem or xmemhuge\n" , + optarg); }if (!strcmp (lgopts[opt_idx].name,"port-numa-config" )) {if (parse_portnuma_config (optarg))
app/test-pmd/testpmd.c +304 -21
358 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
Re: [PATCH v9 19/21] app/testpmd: add support for external memory
Quoted 28 lines
-----Original Message-----
From: Burakov, Anatoly
Sent: Tuesday, October 2, 2018 2:35 PM
To: [email protected]
Cc: Lu, Wenzhuo <[email protected]>; Wu, Jingjing <[email protected]>; Iremonger, Bernard <[email protected]>; Mcnamara, John <[email protected]>; Kovacevic, Marko <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Wiles, Keith <[email protected]>; Richardson, Bruce <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [PATCH v9 19/21] app/testpmd: add support for external memoryCurrently, 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.
Quoted 5 lines
All external memory is allocated using the same external heap, but each will allocate and add a new memory area.
Signed-off-by Anatoly Burakov <[email protected]>Suggested-by Konstantin Ananyev <[email protected]>
PATCH v9 20/21 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 | 6 ++++++ 1 file changed, 6 insertions(+)
doc/guides/rel_notes/release_18_11.rst +6 -0
@@ -54,6 +54,12 @@ 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. + * **Add support to offload more flow match and actions for CXGBE PMD** Flow API support has been enhanced for CXGBE Poll Mode Driver to offload:
Signature · 1 line
2.17.1
PATCH v9 21/21 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 | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+)
doc/guides/prog_guide/env_abstraction_layer.rst +37 -0
@@ -213,6 +213,43 @@ 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, and DMA mappings will not be performed + - 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
Thomas Monjalon
AppliedQuoted 14 lines
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
Quoted 8 lines
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
last 2 patches merged together
Applied, thanks