07dcbfe010
This enables multiprocess synchronization for memory hotplug requests at runtime (as opposed to initialization). Basic workflow is the following. Primary process always does initial mapping and unmapping, and secondary processes always follow primary page map. Only one allocation request can be active at any one time. When primary allocates memory, it ensures that all other processes have allocated the same set of hugepages successfully, otherwise any allocations made are being rolled back, and heap is freed back. Heap is locked throughout the process, and there is also a global memory hotplug lock, so no race conditions can happen. When primary frees memory, it frees the heap, deallocates affected pages, and notifies other processes of deallocations. Since heap is freed from that memory chunk, the area basically becomes invisible to other processes even if they happen to fail to unmap that specific set of pages, so it's completely safe to ignore results of sync requests. When secondary allocates memory, it does not do so by itself. Instead, it sends a request to primary process to try and allocate pages of specified size and on specified socket, such that a specified heap allocation request could complete. Primary process then sends all secondaries (including the requestor) a separate notification of allocated pages, and expects all secondary processes to report success before considering pages as "allocated". Only after primary process ensures that all memory has been successfully allocated in all secondary process, it will respond positively to the initial request, and let secondary proceed with the allocation. Since the heap now has memory that can satisfy allocation request, and it was locked all this time (so no other allocations could take place), secondary process will be able to allocate memory from the heap. When secondary frees memory, it hides pages to be deallocated from the heap. Then, it sends a deallocation request to primary process, so that it deallocates pages itself, and then sends a separate sync request to all other processes (including the requestor) to unmap the same pages. This way, even if secondary fails to notify other processes of this deallocation, that memory will become invisible to other processes, and will not be allocated from again. So, to summarize: address space will only become part of the heap if primary process can ensure that all other processes have allocated this memory successfully. If anything goes wrong, the worst thing that could happen is that a page will "leak" and will not be available to neither DPDK nor the system, as some process will still hold onto it. It's not an actual leak, as we can account for the page - it's just that none of the processes will be able to use this page for anything useful, until it gets allocated from by the primary. Due to underlying DPDK IPC implementation being single-threaded, some asynchronous magic had to be done, as we need to complete several requests before we can definitively allow secondary process to use allocated memory (namely, it has to be present in all other secondary processes before it can be used). Additionally, only one allocation request is allowed to be submitted at once. Memory allocation requests are only allowed when there are no secondary processes currently initializing. To enforce that, a shared rwlock is used, that is set to read lock on init (so that several secondaries could initialize concurrently), and write lock on making allocation requests (so that either secondary init will have to wait, or allocation request will have to wait until all processes have initialized). Any other function that wishes to iterate over memory or prevent allocations should be using memory hotplug lock. Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com> Tested-by: Santosh Shukla <santosh.shukla@caviumnetworks.com> Tested-by: Hemant Agrawal <hemant.agrawal@nxp.com> Tested-by: Gowrishankar Muthukrishnan <gowrishankar.m@linux.vnet.ibm.com>
97 lines
2.5 KiB
Meson
97 lines
2.5 KiB
Meson
# SPDX-License-Identifier: BSD-3-Clause
|
|
# Copyright(c) 2017 Intel Corporation
|
|
|
|
eal_inc += include_directories('.', 'include',
|
|
join_paths('include/arch', arch_subdir))
|
|
|
|
common_objs = []
|
|
common_sources = files(
|
|
'eal_common_bus.c',
|
|
'eal_common_cpuflags.c',
|
|
'eal_common_devargs.c',
|
|
'eal_common_dev.c',
|
|
'eal_common_errno.c',
|
|
'eal_common_fbarray.c',
|
|
'eal_common_hexdump.c',
|
|
'eal_common_launch.c',
|
|
'eal_common_lcore.c',
|
|
'eal_common_log.c',
|
|
'eal_common_memalloc.c',
|
|
'eal_common_memory.c',
|
|
'eal_common_memzone.c',
|
|
'eal_common_options.c',
|
|
'eal_common_proc.c',
|
|
'eal_common_string_fns.c',
|
|
'eal_common_tailqs.c',
|
|
'eal_common_thread.c',
|
|
'eal_common_timer.c',
|
|
'malloc_elem.c',
|
|
'malloc_heap.c',
|
|
'malloc_mp.c',
|
|
'rte_keepalive.c',
|
|
'rte_malloc.c',
|
|
'rte_reciprocal.c',
|
|
'rte_service.c'
|
|
)
|
|
|
|
# get architecture specific sources and objs
|
|
eal_common_arch_sources = []
|
|
eal_common_arch_objs = []
|
|
subdir(join_paths('arch', arch_subdir))
|
|
common_sources += eal_common_arch_sources
|
|
common_objs += eal_common_arch_objs
|
|
|
|
common_headers = files(
|
|
'include/rte_alarm.h',
|
|
'include/rte_branch_prediction.h',
|
|
'include/rte_bus.h',
|
|
'include/rte_bitmap.h',
|
|
'include/rte_common.h',
|
|
'include/rte_debug.h',
|
|
'include/rte_devargs.h',
|
|
'include/rte_dev.h',
|
|
'include/rte_eal.h',
|
|
'include/rte_eal_memconfig.h',
|
|
'include/rte_eal_interrupts.h',
|
|
'include/rte_errno.h',
|
|
'include/rte_fbarray.h',
|
|
'include/rte_hexdump.h',
|
|
'include/rte_interrupts.h',
|
|
'include/rte_keepalive.h',
|
|
'include/rte_launch.h',
|
|
'include/rte_lcore.h',
|
|
'include/rte_log.h',
|
|
'include/rte_malloc.h',
|
|
'include/rte_malloc_heap.h',
|
|
'include/rte_memory.h',
|
|
'include/rte_memzone.h',
|
|
'include/rte_pci_dev_feature_defs.h',
|
|
'include/rte_pci_dev_features.h',
|
|
'include/rte_per_lcore.h',
|
|
'include/rte_random.h',
|
|
'include/rte_reciprocal.h',
|
|
'include/rte_service.h',
|
|
'include/rte_service_component.h',
|
|
'include/rte_string_fns.h',
|
|
'include/rte_tailq.h',
|
|
'include/rte_time.h',
|
|
'include/rte_version.h')
|
|
|
|
# special case install the generic headers, since they go in a subdir
|
|
generic_headers = files(
|
|
'include/generic/rte_atomic.h',
|
|
'include/generic/rte_byteorder.h',
|
|
'include/generic/rte_cpuflags.h',
|
|
'include/generic/rte_cycles.h',
|
|
'include/generic/rte_io.h',
|
|
'include/generic/rte_memcpy.h',
|
|
'include/generic/rte_pause.h',
|
|
'include/generic/rte_prefetch.h',
|
|
'include/generic/rte_rwlock.h',
|
|
'include/generic/rte_spinlock.h',
|
|
'include/generic/rte_vect.h')
|
|
install_headers(generic_headers, subdir: 'generic')
|
|
|
|
# get and install the architecture specific headers
|
|
subdir(join_paths('include/arch', arch_subdir))
|