From 0f7fa159d3ddeced67cfea1b05b722ac32ee5599 Mon Sep 17 00:00:00 2001 From: Lukasz Dorau Date: Tue, 25 Feb 2025 09:40:36 +0100 Subject: [PATCH 1/3] Stack the same values in the tracker Signed-off-by: Lukasz Dorau --- src/provider/provider_tracking.c | 131 ++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 27 deletions(-) diff --git a/src/provider/provider_tracking.c b/src/provider/provider_tracking.c index 62145d5d7..292f53202 100644 --- a/src/provider/provider_tracking.c +++ b/src/provider/provider_tracking.c @@ -37,68 +37,143 @@ struct umf_memory_tracker_t { typedef struct tracker_alloc_info_t { umf_memory_pool_handle_t pool; size_t size; + // list of previous entries with the same address (LIFO) + struct tracker_alloc_info_t *prev; } tracker_alloc_info_t; -static umf_result_t umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker, - umf_memory_pool_handle_t pool, - const void *ptr, size_t size) { +static umf_result_t +umfMemoryTrackerAddValue(umf_memory_tracker_handle_t hTracker, const void *ptr, + tracker_alloc_info_t *new_value) { assert(ptr); + assert(new_value); - tracker_alloc_info_t *value = umf_ba_alloc(hTracker->alloc_info_allocator); - if (value == NULL) { - LOG_ERR("failed to allocate tracker value, ptr=%p, size=%zu", ptr, - size); - return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; - } - - value->pool = pool; - value->size = size; - - int ret = - critnib_insert(hTracker->alloc_segments_map, (uintptr_t)ptr, value, 0); + umf_result_t umf_result = UMF_RESULT_ERROR_UNKNOWN; + int ret = critnib_insert(hTracker->alloc_segments_map, (uintptr_t)ptr, + new_value, 0); if (ret == 0) { LOG_DEBUG( - "memory region is added, tracker=%p, ptr=%p, pool=%p, size=%zu", - (void *)hTracker, ptr, (void *)pool, size); + "memory region added to the tracker=%p, ptr=%p, pool=%p, size=%zu", + (void *)hTracker, ptr, (void *)new_value->pool, new_value->size); return UMF_RESULT_SUCCESS; } - LOG_ERR("failed to insert tracker value, ret=%d, ptr=%p, pool=%p, size=%zu", - ret, ptr, (void *)pool, size); + // failed to insert to the tracker a new value - umf_ba_free(hTracker->alloc_info_allocator, value); + if (ret != EEXIST) { + if (ret == ENOMEM) { + umf_result = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + goto err_message; + } + + // there already is an entry with the same address in the tracker + + ret = utils_mutex_lock(&hTracker->splitMergeMutex); + if (ret) { + goto err_message; + } + + tracker_alloc_info_t *prev_value = (tracker_alloc_info_t *)critnib_get( + hTracker->alloc_segments_map, (uintptr_t)ptr); + if (!prev_value) { + LOG_ERR("the previous entry not found in the tracker"); + goto err_unlock; + } + if (new_value->pool == prev_value->pool) { + LOG_ERR("cannot add the next entry with the same address for the same " + "pool"); + goto err_unlock; + } + + new_value->prev = prev_value; + + ret = critnib_insert(hTracker->alloc_segments_map, (uintptr_t)ptr, + new_value, 1); // update existing entry + if (ret) { + goto err_unlock; + } + + utils_mutex_unlock(&hTracker->splitMergeMutex); + + LOG_DEBUG( + "memory region added to the tracker=%p, ptr=%p, pool=%p, size=%zu", + (void *)hTracker, ptr, (void *)new_value->pool, new_value->size); - if (ret == ENOMEM) { + return UMF_RESULT_SUCCESS; + +err_unlock: + utils_mutex_unlock(&hTracker->splitMergeMutex); + +err_message: + LOG_ERR("failed to insert a new value to the tracker, ret=%d, ptr=%p, " + "pool=%p, size=%zu", + ret, ptr, (void *)new_value->pool, new_value->size); + + return umf_result; +} + +static umf_result_t umfMemoryTrackerAdd(umf_memory_tracker_handle_t hTracker, + umf_memory_pool_handle_t pool, + const void *ptr, size_t size) { + assert(ptr); + + tracker_alloc_info_t *new_value = + umf_ba_alloc(hTracker->alloc_info_allocator); + if (new_value == NULL) { + LOG_ERR("failed to allocate a tracker value, ptr=%p, size=%zu", ptr, + size); return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; } - return UMF_RESULT_ERROR_UNKNOWN; + new_value->pool = pool; + new_value->size = size; + new_value->prev = NULL; + + umf_result_t umf_result = + umfMemoryTrackerAddValue(hTracker, ptr, new_value); + if (umf_result != UMF_RESULT_SUCCESS) { + umf_ba_free(hTracker->alloc_info_allocator, new_value); + return umf_result; + } + + return UMF_RESULT_SUCCESS; } static umf_result_t umfMemoryTrackerRemove(umf_memory_tracker_handle_t hTracker, const void *ptr) { assert(ptr); + umf_result_t umf_result = UMF_RESULT_SUCCESS; + // TODO: there is no support for removing partial ranges (or multiple entries // in a single remove call) yet. // Every umfMemoryTrackerAdd(..., ptr, ...) should have a corresponding // umfMemoryTrackerRemove call with the same ptr value. - void *value = critnib_remove(hTracker->alloc_segments_map, (uintptr_t)ptr); + tracker_alloc_info_t *value = + critnib_remove(hTracker->alloc_segments_map, (uintptr_t)ptr); if (!value) { LOG_ERR("pointer %p not found in the alloc_segments_map", ptr); return UMF_RESULT_ERROR_UNKNOWN; } - tracker_alloc_info_t *v = value; - LOG_DEBUG("memory region removed: tracker=%p, ptr=%p, size=%zu", - (void *)hTracker, ptr, v->size); + (void *)hTracker, ptr, value->size); + + if (value->prev) { + tracker_alloc_info_t *prev_value = value->prev; + umf_result = umfMemoryTrackerAddValue(hTracker, ptr, prev_value); + if (umf_result != UMF_RESULT_SUCCESS) { + LOG_ERR("failed to add the previous entry to the tracker, ptr = " + "%p, size = %zu, umf_result = %d", + ptr, prev_value->size, umf_result); + } + } umf_ba_free(hTracker->alloc_info_allocator, value); - return UMF_RESULT_SUCCESS; + return umf_result; } umf_memory_pool_handle_t umfMemoryTrackerGetPool(const void *ptr) { @@ -247,6 +322,7 @@ static umf_result_t trackingAllocationSplit(void *hProvider, void *ptr, goto err; } + splitValue->prev = value->prev; int cret = critnib_insert(provider->hTracker->alloc_segments_map, (uintptr_t)ptr, (void *)splitValue, 1 /* update */); @@ -322,6 +398,7 @@ static umf_result_t trackingAllocationMerge(void *hProvider, void *lowPtr, // We'll have a duplicate entry for the range [highPtr, highValue->size] but this is fine, // the value is the same anyway and we forbid removing that range concurrently + mergedValue->prev = lowValue->prev; int cret = critnib_insert(provider->hTracker->alloc_segments_map, (uintptr_t)lowPtr, (void *)mergedValue, 1 /* update */); From 3bb33904a8b40eebd88f04d1850a7834da14213b Mon Sep 17 00:00:00 2001 From: Lukasz Dorau Date: Tue, 25 Feb 2025 13:54:44 +0100 Subject: [PATCH 2/3] Add tests for pool from pointer Signed-off-by: Lukasz Dorau --- test/provider_fixed_memory.cpp | 112 ++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/test/provider_fixed_memory.cpp b/test/provider_fixed_memory.cpp index 7f976a1f5..1760ca4f7 100644 --- a/test/provider_fixed_memory.cpp +++ b/test/provider_fixed_memory.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception @@ -11,10 +11,12 @@ #endif #include +#include #include using umf_test::test; +#define FIXED_BUFFER_SIZE (10 * utils_get_page_size()) #define INVALID_PTR ((void *)0x01) typedef enum purge_t { @@ -59,7 +61,7 @@ struct FixedProviderTest test::SetUp(); // Allocate a memory buffer to use with the fixed memory provider - memory_size = utils_get_page_size() * 10; // Allocate 10 pages + memory_size = FIXED_BUFFER_SIZE; // Allocate 10 pages memory_buffer = malloc(memory_size); ASSERT_NE(memory_buffer, nullptr); @@ -391,3 +393,109 @@ TEST_P(FixedProviderTest, split) { umf_result = umfMemoryProviderFree(provider.get(), ptr2, size); ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); } + +TEST_P(FixedProviderTest, pool_from_ptr_whole_size_success) { + umf_result_t umf_result; + size_t size_of_first_alloc; + size_t size_of_pool_from_ptr; + void *ptr_for_pool = nullptr; + void *ptr = nullptr; + + umf_memory_pool_handle_t proxyFixedPool = nullptr; + umf_result = umfPoolCreate(umfProxyPoolOps(), provider.get(), nullptr, 0, + &proxyFixedPool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + size_of_first_alloc = FIXED_BUFFER_SIZE - (2 * page_size); + ptr_for_pool = umfPoolMalloc(proxyFixedPool, size_of_first_alloc); + ASSERT_NE(ptr_for_pool, nullptr); + + // Create provider parameters + size_of_pool_from_ptr = size_of_first_alloc; // whole size + umf_fixed_memory_provider_params_handle_t params = nullptr; + umf_result = umfFixedMemoryProviderParamsCreate(¶ms, ptr_for_pool, + size_of_pool_from_ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(params, nullptr); + + umf_memory_provider_handle_t providerFromPtr = nullptr; + umf_result = umfMemoryProviderCreate(umfFixedMemoryProviderOps(), params, + &providerFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(providerFromPtr, nullptr); + + umf_memory_pool_handle_t poolFromPtr = nullptr; + umf_result = umfPoolCreate(umfProxyPoolOps(), providerFromPtr, nullptr, 0, + &poolFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + ptr = umfPoolMalloc(poolFromPtr, size_of_pool_from_ptr); + ASSERT_NE(ptr, nullptr); + + memset(ptr, 0xFF, size_of_pool_from_ptr); + + umf_result = umfPoolFree(poolFromPtr, ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umfPoolDestroy(poolFromPtr); + umfMemoryProviderDestroy(providerFromPtr); + umfFixedMemoryProviderParamsDestroy(params); + + umf_result = umfPoolFree(proxyFixedPool, ptr_for_pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umfPoolDestroy(proxyFixedPool); +} + +TEST_P(FixedProviderTest, pool_from_ptr_half_size_success) { + umf_result_t umf_result; + size_t size_of_first_alloc; + size_t size_of_pool_from_ptr; + void *ptr_for_pool = nullptr; + void *ptr = nullptr; + + umf_memory_pool_handle_t proxyFixedPool = nullptr; + umf_result = umfPoolCreate(umfProxyPoolOps(), provider.get(), nullptr, 0, + &proxyFixedPool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + size_of_first_alloc = FIXED_BUFFER_SIZE - (2 * page_size); + ptr_for_pool = umfPoolMalloc(proxyFixedPool, size_of_first_alloc); + ASSERT_NE(ptr_for_pool, nullptr); + + // Create provider parameters + size_of_pool_from_ptr = size_of_first_alloc / 2; // half size + umf_fixed_memory_provider_params_handle_t params = nullptr; + umf_result = umfFixedMemoryProviderParamsCreate(¶ms, ptr_for_pool, + size_of_pool_from_ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(params, nullptr); + + umf_memory_provider_handle_t providerFromPtr = nullptr; + umf_result = umfMemoryProviderCreate(umfFixedMemoryProviderOps(), params, + &providerFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(providerFromPtr, nullptr); + + umf_memory_pool_handle_t poolFromPtr = nullptr; + umf_result = umfPoolCreate(umfProxyPoolOps(), providerFromPtr, nullptr, 0, + &poolFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + ptr = umfPoolMalloc(poolFromPtr, size_of_pool_from_ptr); + ASSERT_NE(ptr, nullptr); + + memset(ptr, 0xFF, size_of_pool_from_ptr); + + umf_result = umfPoolFree(poolFromPtr, ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umfPoolDestroy(poolFromPtr); + umfMemoryProviderDestroy(providerFromPtr); + umfFixedMemoryProviderParamsDestroy(params); + + umf_result = umfPoolFree(proxyFixedPool, ptr_for_pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umfPoolDestroy(proxyFixedPool); +} From 44b393de0ddc84b4561e08fcdaa8b5e0e9f6046f Mon Sep 17 00:00:00 2001 From: Lukasz Dorau Date: Tue, 25 Feb 2025 17:38:11 +0100 Subject: [PATCH 3/3] Add tests for pool from pointer to poolFixtures.hpp Signed-off-by: Lukasz Dorau --- test/poolFixtures.hpp | 156 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 151 insertions(+), 5 deletions(-) diff --git a/test/poolFixtures.hpp b/test/poolFixtures.hpp index d9a5410c0..a840660ee 100644 --- a/test/poolFixtures.hpp +++ b/test/poolFixtures.hpp @@ -5,11 +5,6 @@ #ifndef UMF_TEST_POOL_FIXTURES_HPP #define UMF_TEST_POOL_FIXTURES_HPP 1 -#include "pool.hpp" -#include "provider.hpp" -#include "umf/providers/provider_devdax_memory.h" -#include "utils/utils_sanitizers.h" - #include #include #include @@ -17,7 +12,14 @@ #include #include +#include +#include +#include + #include "../malloc_compliance_tests.hpp" +#include "pool.hpp" +#include "provider.hpp" +#include "utils/utils_sanitizers.h" typedef void *(*pfnPoolParamsCreate)(); typedef umf_result_t (*pfnPoolParamsDestroy)(void *); @@ -474,4 +476,148 @@ TEST_P(umfPoolTest, mallocUsableSize) { #endif } +TEST_P(umfPoolTest, umfPoolAlignedMalloc) { +#ifdef _WIN32 + // umfPoolAlignedMalloc() is not supported on Windows + // TODO: implement support for windows + GTEST_SKIP(); +#else /* !_WIN32 */ + umf_result_t umf_result; + size_t size_of_first_alloc; + void *ptr_for_pool = nullptr; + + umf_memory_pool_handle_t pool_get = pool.get(); + size_of_first_alloc = 2 * 1024 * 1024; // 2MB + + if (!umf_test::isAlignedAllocSupported(pool_get)) { + GTEST_SKIP(); + } + + ptr_for_pool = umfPoolAlignedMalloc(pool_get, size_of_first_alloc, + utils_get_page_size()); + ASSERT_NE(ptr_for_pool, nullptr); + + umf_result = umfPoolFree(pool_get, ptr_for_pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +#endif /* !_WIN32 */ +} + +TEST_P(umfPoolTest, pool_from_ptr_whole_size_success) { +#ifdef _WIN32 + // umfPoolAlignedMalloc() is not supported on Windows + // TODO: implement support for windows + GTEST_SKIP(); +#else /* !_WIN32 */ + umf_result_t umf_result; + size_t size_of_first_alloc; + size_t size_of_pool_from_ptr; + void *ptr_for_pool = nullptr; + void *ptr = nullptr; + + umf_memory_pool_handle_t pool_get = pool.get(); + size_of_first_alloc = 2 * 1024 * 1024; // 2MB + + if (!umf_test::isAlignedAllocSupported(pool_get)) { + GTEST_SKIP(); + } + + ptr_for_pool = umfPoolAlignedMalloc(pool_get, size_of_first_alloc, + utils_get_page_size()); + ASSERT_NE(ptr_for_pool, nullptr); + + // Create provider parameters + size_of_pool_from_ptr = size_of_first_alloc; // whole size + umf_fixed_memory_provider_params_handle_t params = nullptr; + umf_result = umfFixedMemoryProviderParamsCreate(¶ms, ptr_for_pool, + size_of_pool_from_ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(params, nullptr); + + umf_memory_provider_handle_t providerFromPtr = nullptr; + umf_result = umfMemoryProviderCreate(umfFixedMemoryProviderOps(), params, + &providerFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(providerFromPtr, nullptr); + + umf_memory_pool_handle_t poolFromPtr = nullptr; + umf_result = umfPoolCreate(umfProxyPoolOps(), providerFromPtr, nullptr, 0, + &poolFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + ptr = umfPoolMalloc(poolFromPtr, size_of_pool_from_ptr); + ASSERT_NE(ptr, nullptr); + + memset(ptr, 0xFF, size_of_pool_from_ptr); + + umf_result = umfPoolFree(poolFromPtr, ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umfPoolDestroy(poolFromPtr); + umfMemoryProviderDestroy(providerFromPtr); + umfFixedMemoryProviderParamsDestroy(params); + + umf_result = umfPoolFree(pool_get, ptr_for_pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +#endif /* !_WIN32 */ +} + +TEST_P(umfPoolTest, pool_from_ptr_half_size_success) { +#ifdef _WIN32 + // umfPoolAlignedMalloc() is not supported on Windows + // TODO: implement support for windows + GTEST_SKIP(); +#else /* !_WIN32 */ + umf_result_t umf_result; + size_t size_of_first_alloc; + size_t size_of_pool_from_ptr; + void *ptr_for_pool = nullptr; + void *ptr = nullptr; + + umf_memory_pool_handle_t pool_get = pool.get(); + size_of_first_alloc = 2 * 1024 * 1024; // 2MB + + if (!umf_test::isAlignedAllocSupported(pool_get)) { + GTEST_SKIP(); + } + + ptr_for_pool = umfPoolAlignedMalloc(pool_get, size_of_first_alloc, + utils_get_page_size()); + ASSERT_NE(ptr_for_pool, nullptr); + + // Create provider parameters + size_of_pool_from_ptr = size_of_first_alloc / 2; // half size + umf_fixed_memory_provider_params_handle_t params = nullptr; + umf_result = umfFixedMemoryProviderParamsCreate(¶ms, ptr_for_pool, + size_of_pool_from_ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(params, nullptr); + + umf_memory_provider_handle_t providerFromPtr = nullptr; + umf_result = umfMemoryProviderCreate(umfFixedMemoryProviderOps(), params, + &providerFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(providerFromPtr, nullptr); + + umf_memory_pool_handle_t poolFromPtr = nullptr; + umf_result = umfPoolCreate(umfProxyPoolOps(), providerFromPtr, nullptr, 0, + &poolFromPtr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + ptr = umfPoolMalloc(poolFromPtr, size_of_pool_from_ptr); + ASSERT_NE(ptr, nullptr); + + memset(ptr, 0xFF, size_of_pool_from_ptr); + + umf_result = umfPoolFree(poolFromPtr, ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umfPoolDestroy(poolFromPtr); + umfMemoryProviderDestroy(providerFromPtr); + umfFixedMemoryProviderParamsDestroy(params); + + umf_result = umfPoolFree(pool_get, ptr_for_pool); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +#endif /* !_WIN32 */ +} + #endif /* UMF_TEST_POOL_FIXTURES_HPP */