diff --git a/.github/workflows/reusable_basic.yml b/.github/workflows/reusable_basic.yml index 3b5fdbbf7..807fd14f0 100644 --- a/.github/workflows/reusable_basic.yml +++ b/.github/workflows/reusable_basic.yml @@ -210,7 +210,7 @@ jobs: --build-type ${{matrix.build_type}} --disjoint-pool --jemalloc-pool - ${{ matrix.install_tbb == 'ON' && matrix.disable_hwloc != 'ON' && matrix.link_hwloc_statically != 'ON' && '--proxy' || '' }} + ${{ matrix.install_tbb == 'ON' && matrix.disable_hwloc != 'ON' && matrix.shared_library == 'ON' && '--proxy' || '' }} --umf-version ${{env.UMF_VERSION}} ${{ matrix.shared_library == 'ON' && '--shared-library' || '' }} @@ -300,7 +300,7 @@ jobs: --build-type ${{matrix.build_type}} --disjoint-pool --jemalloc-pool - --proxy + ${{matrix.shared_library == 'ON' && '--proxy' || '' }} --umf-version ${{env.UMF_VERSION}} ${{ matrix.shared_library == 'ON' && '--shared-library' || ''}} @@ -310,7 +310,7 @@ jobs: shell: pwsh - name: check /DEPENDENTLOADFLAG in umf_proxy.dll - if: ${{matrix.compiler.cxx == 'cl'}} + if: ${{matrix.shared_library == 'ON' && matrix.compiler.cxx == 'cl'}} run: ${{github.workspace}}/.github/scripts/check_dll_flags.ps1 ${{env.BUILD_DIR}}/src/proxy_lib/${{matrix.build_type}}/umf_proxy.dll shell: pwsh diff --git a/CMakeLists.txt b/CMakeLists.txt index b0ab6ac64..aef1ee16b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,8 +432,17 @@ if(WINDOWS) ) endif() endif() + # set UMF_PROXY_LIB_ENABLED -if(UMF_PROXY_LIB_BASED_ON_POOL STREQUAL SCALABLE) +if(UMF_DISABLE_HWLOC) + message(STATUS "Disabling the proxy library, because HWLOC is disabled") +elseif(NOT UMF_BUILD_SHARED_LIBRARY) + # TODO enable this scenario + message( + STATUS + "Disabling the proxy library, because UMF is built as static library" + ) +elseif(UMF_PROXY_LIB_BASED_ON_POOL STREQUAL SCALABLE) if(UMF_POOL_SCALABLE_ENABLED) set(UMF_PROXY_LIB_ENABLED ON) set(PROXY_LIB_USES_SCALABLE_POOL ON) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9fabe2a08..7078d629f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -195,8 +195,6 @@ install(TARGETS umf EXPORT ${PROJECT_NAME}-targets) add_subdirectory(pool) -if(UMF_PROXY_LIB_ENABLED - AND NOT UMF_LINK_HWLOC_STATICALLY - AND NOT UMF_DISABLE_HWLOC) +if(UMF_PROXY_LIB_ENABLED) add_subdirectory(proxy_lib) endif() diff --git a/src/provider/provider_devdax_memory.c b/src/provider/provider_devdax_memory.c index 7751eb463..bae968a1d 100644 --- a/src/provider/provider_devdax_memory.c +++ b/src/provider/provider_devdax_memory.c @@ -360,13 +360,9 @@ static umf_result_t devdax_allocation_split(void *provider, void *ptr, static umf_result_t devdax_allocation_merge(void *provider, void *lowPtr, void *highPtr, size_t totalSize) { - (void)provider; - - if ((uintptr_t)highPtr <= (uintptr_t)lowPtr) { - return UMF_RESULT_ERROR_INVALID_ARGUMENT; - } - - if ((uintptr_t)highPtr - (uintptr_t)lowPtr <= totalSize) { + if (provider == NULL || lowPtr == NULL || highPtr == NULL || + ((uintptr_t)highPtr <= (uintptr_t)lowPtr) || + ((uintptr_t)highPtr - (uintptr_t)lowPtr >= totalSize)) { return UMF_RESULT_ERROR_INVALID_ARGUMENT; } diff --git a/src/provider/provider_tracking.c b/src/provider/provider_tracking.c index 70f63f937..d058af271 100644 --- a/src/provider/provider_tracking.c +++ b/src/provider/provider_tracking.c @@ -48,13 +48,14 @@ static umf_result_t umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker, int ret = critnib_insert(hTracker->map, (uintptr_t)ptr, value, 0); if (ret == 0) { - LOG_DEBUG("memory region is added, tracker=%p, ptr=%p, size=%zu", - (void *)hTracker, ptr, size); + LOG_DEBUG( + "memory region is added, tracker=%p, ptr=%p, pool=%p, size=%zu", + (void *)hTracker, ptr, (void *)pool, size); return UMF_RESULT_SUCCESS; } - LOG_ERR("failed to insert tracker value, ret=%d, ptr=%p, size=%zu", ret, - ptr, size); + LOG_ERR("failed to insert tracker value, ret=%d, ptr=%p, pool=%p, size=%zu", + ret, ptr, (void *)pool, size); umf_ba_free(hTracker->tracker_allocator, value); @@ -303,8 +304,8 @@ static umf_result_t trackingAllocationMerge(void *hProvider, void *lowPtr, ret = umfMemoryProviderAllocationMerge(provider->hUpstream, lowPtr, highPtr, totalSize); if (ret != UMF_RESULT_SUCCESS) { - LOG_ERR("upstream provider failed to merge regions"); - goto err; + LOG_WARN("upstream provider failed to merge regions"); + goto not_merged; } // We'll have a duplicate entry for the range [highPtr, highValue->size] but this is fine, @@ -329,7 +330,11 @@ static umf_result_t trackingAllocationMerge(void *hProvider, void *lowPtr, return UMF_RESULT_SUCCESS; err: + assert(0); + +not_merged: utils_mutex_unlock(&provider->hTracker->splitMergeMutex); + err_lock: umf_ba_free(provider->hTracker->tracker_allocator, mergedValue); return ret; diff --git a/src/proxy_lib/proxy_lib.c b/src/proxy_lib/proxy_lib.c index ca8d69315..2730b9f17 100644 --- a/src/proxy_lib/proxy_lib.c +++ b/src/proxy_lib/proxy_lib.c @@ -124,8 +124,6 @@ void proxy_lib_create_common(void) { } else if (utils_env_var_has_str("UMF_PROXY", "page.disposition=shared-shm")) { - LOG_DEBUG("proxy_lib: using the MAP_SHARED visibility mode with the " - "named shared memory"); os_params.visibility = UMF_MEM_MAP_SHARED; memset(shm_name, 0, NAME_MAX); @@ -145,9 +143,8 @@ void proxy_lib_create_common(void) { exit(-1); } - umf_result = - umfPoolCreate(umfPoolManagerOps(), OS_memory_provider, NULL, - UMF_POOL_CREATE_FLAG_DISABLE_TRACKING, &Proxy_pool); + umf_result = umfPoolCreate(umfPoolManagerOps(), OS_memory_provider, NULL, 0, + &Proxy_pool); if (umf_result != UMF_RESULT_SUCCESS) { LOG_ERR("creating UMF pool manager failed"); exit(-1); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 99f963827..df17d9b2c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -408,10 +408,7 @@ add_umf_test( LIBS ${UMF_UTILS_FOR_TEST}) # tests for the proxy library -if(UMF_PROXY_LIB_ENABLED - AND UMF_BUILD_SHARED_LIBRARY - AND NOT UMF_DISABLE_HWLOC - AND NOT UMF_LINK_HWLOC_STATICALLY) +if(UMF_PROXY_LIB_ENABLED AND UMF_BUILD_SHARED_LIBRARY) add_umf_test( NAME proxy_lib_basic SRCS ${BA_SOURCES_FOR_TEST} test_proxy_lib.cpp @@ -486,6 +483,18 @@ if(LINUX) add_umf_ipc_test(TEST ipc_os_prov_anon_fd) add_umf_ipc_test(TEST ipc_os_prov_shm) + if(UMF_PROXY_LIB_ENABLED AND UMF_BUILD_SHARED_LIBRARY) + build_umf_test( + NAME + ipc_os_prov_proxy + SRCS + ipc_os_prov_proxy.c + common/ipc_common.c + LIBS + ${UMF_UTILS_FOR_TEST}) + add_umf_ipc_test(TEST ipc_os_prov_proxy) + endif() + build_umf_test( NAME ipc_devdax_prov_consumer diff --git a/test/ipc_os_prov_proxy.c b/test/ipc_os_prov_proxy.c new file mode 100644 index 000000000..a17518658 --- /dev/null +++ b/test/ipc_os_prov_proxy.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2024 Intel Corporation + * + * Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ipc_common.h" +#include "utils_load_library.h" + +umf_result_t (*pfnGetIPCHandle)(const void *ptr, umf_ipc_handle_t *umfIPCHandle, + size_t *size); +umf_result_t (*pfnPutIPCHandle)(umf_ipc_handle_t umfIPCHandle); + +// This is a test for a scenario where a user process is started using the +// LD_PRELOAD with the UMF Proxy Lib and this process uses UMF by loading +// libumf.so at runtime. +// In this test, we expect that all allocations made by the process will be +// handled by UMF in the Proxy Lib and added to the UMF tracker so that they +// can be used later in the UMF IPC API. +int main(int argc, char *argv[]) { + int ret = 0; + umf_result_t umf_result = UMF_RESULT_ERROR_UNKNOWN; + int producer_socket = -1; + const size_t MSG_SIZE = 2048; + char consumer_message[MSG_SIZE]; + + if (argc < 2) { + fprintf(stderr, "usage: %s [shm_name]\n", argv[0]); + return -1; + } + + int port = atoi(argv[1]); + + int fd = open("/proc/self/maps", O_RDONLY); + if (fd == -1) { + return -1; + } + + // read the "/proc/self/maps" file until the "libumf_proxy.so" of the maps + // is found or EOF is reached. + const size_t SIZE_BUF = 8192; + char buf[SIZE_BUF]; + ssize_t nbytes = 1; + char *found = NULL; + while (nbytes > 0 && found == NULL) { + memset(buf, 0, SIZE_BUF); // erase previous data + nbytes = read(fd, buf, SIZE_BUF); + if (nbytes <= 0) { + break; + } + found = strstr(buf, "libumf_proxy.so"); + } + (void)close(fd); + + if (found == NULL) { + fprintf( + stderr, + "test binary not run under LD_PRELOAD with \"libumf_proxy.so\"\n"); + return -1; + } + + // open the UMF library and get umfGetIPCHandle() function + const char *umf_lib_name = "libumf.so"; + void *umf_lib_handle = utils_open_library(umf_lib_name, 0); + if (umf_lib_handle == NULL) { + fprintf(stderr, "utils_open_library: UMF library not found (%s)\n", + umf_lib_name); + return -1; + } + + *(void **)&pfnGetIPCHandle = + utils_get_symbol_addr(umf_lib_handle, "umfGetIPCHandle", umf_lib_name); + if (pfnGetIPCHandle == NULL) { + ret = -1; + goto err_close_lib; + } + + *(void **)&pfnPutIPCHandle = + utils_get_symbol_addr(umf_lib_handle, "umfPutIPCHandle", umf_lib_name); + if (pfnPutIPCHandle == NULL) { + ret = -1; + goto err_close_lib; + } + + // create simple allocation - it should be added to the UMF tracker if the + // process was launched under UMF Proxy Lib + size_t size = 2137; + void *ptr = malloc(size); + if (ptr == NULL) { + fprintf(stderr, "malloc() failed!\n"); + ret = -1; + goto err_close_lib; + } + + fprintf(stderr, "Allocated memory - %zu\n", size); + size_t val = 144; + size_t expected_val = val / 2; + *(size_t *)ptr = val; + + // get IPC handle of the allocation + umf_ipc_handle_t ipc_handle = NULL; + size_t ipc_handle_size = 0; + umf_result_t res = pfnGetIPCHandle(ptr, &ipc_handle, &ipc_handle_size); + if (res != UMF_RESULT_SUCCESS) { + fprintf(stderr, "pfnGetIPCHandle() failed!\n"); + ret = -1; + goto err_free_mem; + } + + // check if we got valid data + if (ipc_handle == NULL || ipc_handle_size == 0) { + fprintf(stderr, "pfnGetIPCHandle() couldn't find the handle data!\n"); + ret = -1; + goto err_free_mem; + } + + fprintf(stderr, "Got IPCHandle for memory - %p | size - %zu\n", + (void *)ipc_handle, ipc_handle_size); + + producer_socket = producer_connect(port); + if (producer_socket < 0) { + goto err_PutIPCHandle; + } + + // send the ipc_handle_size to the consumer + ssize_t len = + send(producer_socket, &ipc_handle_size, sizeof(ipc_handle_size), 0); + if (len < 0) { + fprintf(stderr, "[producer] ERROR: unable to send the ipc_handle_size " + "to the consumer\n"); + goto err_close_producer_socket; + } + + fprintf(stderr, + "[producer] Sent the size of the IPC handle (%zu) to the consumer " + "(sent %zu bytes)\n", + ipc_handle_size, len); + + // zero the consumer_message buffer + memset(consumer_message, 0, sizeof(consumer_message)); + + // receive the consumer's confirmation - IPC handle size + len = recv(producer_socket, consumer_message, sizeof(consumer_message), 0); + if (len < 0) { + fprintf(stderr, "[producer] ERROR: error while receiving the " + "confirmation from the consumer\n"); + goto err_close_producer_socket; + } + + size_t conf_IPC_handle_size = *(size_t *)consumer_message; + if (conf_IPC_handle_size == ipc_handle_size) { + fprintf(stderr, + "[producer] Received the correct confirmation (%zu) from the " + "consumer (%zu bytes)\n", + conf_IPC_handle_size, len); + } else { + fprintf(stderr, + "[producer] Received an INCORRECT confirmation (%zu) from the " + "consumer (%zu bytes)\n", + conf_IPC_handle_size, len); + goto err_close_producer_socket; + } + + // send the ipc_handle of ipc_handle_size to the consumer + if (send(producer_socket, ipc_handle, ipc_handle_size, 0) < 0) { + fprintf(stderr, "[producer] ERROR: unable to send the ipc_handle to " + "the consumer\n"); + goto err_close_producer_socket; + } + + fprintf(stderr, + "[producer] Sent the IPC handle to the consumer (%zu bytes)\n", + ipc_handle_size); + + // zero the consumer_message buffer + memset(consumer_message, 0, sizeof(consumer_message)); + + // receive the consumer's response + if (recv(producer_socket, consumer_message, sizeof(consumer_message) - 1, + 0) < 0) { + fprintf( + stderr, + "[producer] ERROR: error while receiving the consumer's message\n"); + goto err_close_producer_socket; + } + + fprintf(stderr, "[producer] Received the consumer's response: \"%s\"\n", + consumer_message); + + if (strncmp(consumer_message, "SKIP", 5 /* length of "SKIP" + 1 */) == 0) { + fprintf(stderr, "[producer] SKIP: received the 'SKIP' response from " + "consumer, skipping ...\n"); + ret = 1; + goto err_close_producer_socket; + } + + // read a new value - the expected correct value val / 2 + volatile unsigned long long new_val = *(unsigned long long *)ptr; + if (new_val == expected_val) { + ret = 0; // got the correct value - success! + fprintf( + stderr, + "[producer] The consumer wrote the correct value (the old one / 2) " + "to my shared memory: %llu\n", + new_val); + } else { + fprintf( + stderr, + "[producer] ERROR: The consumer did NOT write the correct value " + "(the old one / 2 = %llu) to my shared memory: %llu\n", + expected_val, new_val); + } + +err_close_producer_socket: + close(producer_socket); + +err_PutIPCHandle: + umf_result = pfnPutIPCHandle(ipc_handle); + if (umf_result != UMF_RESULT_SUCCESS) { + fprintf(stderr, "[producer] ERROR: putting the IPC handle failed\n"); + } + + fprintf(stderr, "[producer] Put the IPC handle\n"); + + if (ret == 0) { + fprintf(stderr, "[producer] Shutting down (status OK) ...\n"); + } else if (ret == 1) { + fprintf(stderr, "[producer] Shutting down (status SKIP) ...\n"); + ret = 0; + } else { + fprintf(stderr, "[producer] Shutting down (status ERROR) ...\n"); + } + + return ret; + +err_free_mem: + free(ptr); + +err_close_lib: + utils_close_library(umf_lib_handle); + + return ret; +} diff --git a/test/ipc_os_prov_proxy.sh b/test/ipc_os_prov_proxy.sh new file mode 100755 index 000000000..86b95a235 --- /dev/null +++ b/test/ipc_os_prov_proxy.sh @@ -0,0 +1,26 @@ +# +# Copyright (C) 2024 Intel Corporation +# +# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +set -e + +UMF_LOG_VAL="level:debug;flush:debug;output:stderr;pid:yes" +UMF_PROXY_VAL="page.disposition=shared-shm" +LD_PRELOAD_VAL="../lib/libumf_proxy.so" + +# port should be a number from the range <1024, 65535> +PORT=$(( 1024 + ( $$ % ( 65535 - 1024 )))) + +echo "Starting CONSUMER on port $PORT ..." +UMF_LOG=$UMF_LOG_VAL ./umf_test-ipc_os_prov_consumer $PORT & + +echo "Waiting 1 sec ..." +sleep 1 + +echo "Starting ipc_os_prov_proxy PRODUCER on port $PORT ..." +LD_PRELOAD=$LD_PRELOAD_VAL UMF_LOG=$UMF_LOG_VAL UMF_PROXY=$UMF_PROXY_VAL ./umf_test-ipc_os_prov_proxy $PORT