diff --git a/include/umf/providers/provider_fixed_memory.h b/include/umf/providers/provider_fixed_memory.h new file mode 100644 index 000000000..873d14389 --- /dev/null +++ b/include/umf/providers/provider_fixed_memory.h @@ -0,0 +1,54 @@ +/* + * 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 +*/ + +#ifndef UMF_FIXED_MEMORY_PROVIDER_H +#define UMF_FIXED_MEMORY_PROVIDER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @cond +#define UMF_FIXED_RESULTS_START_FROM 4000 +/// @endcond + +struct umf_fixed_memory_provider_params_t; + +typedef struct umf_fixed_memory_provider_params_t + *umf_fixed_memory_provider_params_handle_t; + +/// @brief Create a struct to store parameters of the Fixed Memory Provider. +/// @param hParams [out] handle to the newly created parameters struct. +/// @param ptr [in] pointer to the memory region. +/// @param size [in] size of the memory region in bytes. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfFixedMemoryProviderParamsCreate( + umf_fixed_memory_provider_params_handle_t *hParams, void *ptr, size_t size); + +/// @brief Destroy parameters struct. +/// @param hParams [in] handle to the parameters of the Fixed Memory Provider. +/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure. +umf_result_t umfFixedMemoryProviderParamsDestroy( + umf_fixed_memory_provider_params_handle_t hParams); + +/// @brief Retrieve the operations structure for the Fixed Memory Provider. +/// @return Pointer to the umf_memory_provider_ops_t structure. +umf_memory_provider_ops_t *umfFixedMemoryProviderOps(void); + +/// @brief Fixed Memory Provider operation results +typedef enum umf_fixed_memory_provider_native_error { + UMF_FIXED_RESULT_SUCCESS = UMF_FIXED_RESULTS_START_FROM, ///< Success + UMF_FIXED_RESULT_ERROR_PURGE_FORCE_FAILED, ///< Force purging failed +} umf_fixed_memory_provider_native_error_t; + +#ifdef __cplusplus +} +#endif + +#endif /* UMF_FIXED_MEMORY_PROVIDER_H */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fb32b6d2e..2a27dce46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,6 +62,7 @@ set(UMF_SOURCES provider/provider_cuda.c provider/provider_devdax_memory.c provider/provider_file_memory.c + provider/provider_fixed_memory.c provider/provider_level_zero.c provider/provider_os_memory.c provider/provider_tracking.c diff --git a/src/coarse/coarse.c b/src/coarse/coarse.c index 729480154..a575fc2c9 100644 --- a/src/coarse/coarse.c +++ b/src/coarse/coarse.c @@ -1030,6 +1030,11 @@ umf_result_t coarse_alloc(coarse_t *coarse, size_t size, size_t alignment, alignment = ALIGN_UP(alignment, coarse->page_size); } + if (size + alignment < size) { + LOG_ERR("size + alignment overflow"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + if (utils_mutex_lock(&coarse->lock) != 0) { LOG_ERR("locking the lock failed"); return UMF_RESULT_ERROR_UNKNOWN; diff --git a/src/libumf.def b/src/libumf.def index a81492af3..3ebde023b 100644 --- a/src/libumf.def +++ b/src/libumf.def @@ -20,18 +20,23 @@ EXPORTS umfCUDAMemoryProviderParamsSetContext umfCUDAMemoryProviderParamsSetDevice umfCUDAMemoryProviderParamsSetMemoryType + umfCoarseMemoryProviderGetStats + umfCoarseMemoryProviderOps umfDevDaxMemoryProviderOps umfDevDaxMemoryProviderParamsCreate umfDevDaxMemoryProviderParamsDestroy umfDevDaxMemoryProviderParamsSetDeviceDax umfDevDaxMemoryProviderParamsSetProtection - umfFree umfFileMemoryProviderOps umfFileMemoryProviderParamsCreate umfFileMemoryProviderParamsDestroy umfFileMemoryProviderParamsSetPath umfFileMemoryProviderParamsSetProtection umfFileMemoryProviderParamsSetVisibility + umfFixedMemoryProviderOps + umfFixedMemoryProviderParamsCreate + umfFixedMemoryProviderParamsDestroy + umfFree umfGetIPCHandle umfGetLastFailedMemoryProvider umfJemallocPoolOps diff --git a/src/libumf.map b/src/libumf.map index 4c60b87c3..77d89a7e6 100644 --- a/src/libumf.map +++ b/src/libumf.map @@ -14,18 +14,23 @@ UMF_1.0 { umfCUDAMemoryProviderParamsSetContext; umfCUDAMemoryProviderParamsSetDevice; umfCUDAMemoryProviderParamsSetMemoryType; + umfCoarseMemoryProviderGetStats; + umfCoarseMemoryProviderOps; umfDevDaxMemoryProviderOps; umfDevDaxMemoryProviderParamsCreate; umfDevDaxMemoryProviderParamsDestroy; umfDevDaxMemoryProviderParamsSetDeviceDax; umfDevDaxMemoryProviderParamsSetProtection; - umfFree; umfFileMemoryProviderOps; umfFileMemoryProviderParamsCreate; umfFileMemoryProviderParamsDestroy; umfFileMemoryProviderParamsSetPath; umfFileMemoryProviderParamsSetProtection; umfFileMemoryProviderParamsSetVisibility; + umfFixedMemoryProviderOps; + umfFixedMemoryProviderParamsCreate; + umfFixedMemoryProviderParamsDestroy; + umfFree; umfGetIPCHandle; umfGetLastFailedMemoryProvider; umfJemallocPoolOps; @@ -81,13 +86,13 @@ UMF_1.0 { umfOsMemoryProviderOps; umfOsMemoryProviderParamsCreate; umfOsMemoryProviderParamsDestroy; - umfOsMemoryProviderParamsSetProtection; - umfOsMemoryProviderParamsSetVisibility; - umfOsMemoryProviderParamsSetShmName; umfOsMemoryProviderParamsSetNumaList; umfOsMemoryProviderParamsSetNumaMode; umfOsMemoryProviderParamsSetPartSize; umfOsMemoryProviderParamsSetPartitions; + umfOsMemoryProviderParamsSetProtection; + umfOsMemoryProviderParamsSetShmName; + umfOsMemoryProviderParamsSetVisibility; umfPoolAlignedMalloc; umfPoolByPtr; umfPoolCalloc; diff --git a/src/provider/provider_fixed_memory.c b/src/provider/provider_fixed_memory.c new file mode 100644 index 000000000..6e7f42989 --- /dev/null +++ b/src/provider/provider_fixed_memory.c @@ -0,0 +1,335 @@ +/* + * 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 "base_alloc_global.h" +#include "coarse.h" +#include "libumf.h" +#include "utils_common.h" +#include "utils_concurrency.h" +#include "utils_log.h" +#include +#include +#include + +#define FIXED_PAGE_SIZE_2MB ((size_t)(2 * 1024 * 1024)) // == 2 MB + +#define TLS_MSG_BUF_LEN 1024 + +typedef struct fixed_memory_provider_t { + void *base; // base address of memory + size_t size; // size of the memory region + coarse_t *coarse; // coarse library handle +} fixed_memory_provider_t; + +// Fixed Memory provider settings struct +typedef struct umf_fixed_memory_provider_params_t { + void *ptr; + size_t size; +} umf_fixed_memory_provider_params_t; + +typedef struct fixed_last_native_error_t { + int32_t native_error; + int errno_value; + char msg_buff[TLS_MSG_BUF_LEN]; +} fixed_last_native_error_t; + +static __TLS fixed_last_native_error_t TLS_last_native_error; + +// helper values used only in the Native_error_str array +#define _UMF_FIXED_RESULT_SUCCESS \ + (UMF_FIXED_RESULT_SUCCESS - UMF_FIXED_RESULT_SUCCESS) +#define _UMF_FIXED_RESULT_ERROR_PURGE_FORCE_FAILED \ + (UMF_FIXED_RESULT_ERROR_PURGE_FORCE_FAILED - UMF_FIXED_RESULT_SUCCESS) + +static const char *Native_error_str[] = { + [_UMF_FIXED_RESULT_SUCCESS] = "success", + [_UMF_FIXED_RESULT_ERROR_PURGE_FORCE_FAILED] = "force purging failed"}; + +static void fixed_store_last_native_error(int32_t native_error, + int errno_value) { + TLS_last_native_error.native_error = native_error; + TLS_last_native_error.errno_value = errno_value; +} + +static umf_result_t fixed_allocation_split_cb(void *provider, void *ptr, + size_t totalSize, + size_t firstSize) { + (void)provider; + (void)ptr; + (void)totalSize; + (void)firstSize; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t fixed_allocation_merge_cb(void *provider, void *lowPtr, + void *highPtr, size_t totalSize) { + (void)provider; + (void)lowPtr; + (void)highPtr; + (void)totalSize; + return UMF_RESULT_SUCCESS; +} + +static umf_result_t fixed_initialize(void *params, void **provider) { + umf_result_t ret; + + if (params == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + umf_fixed_memory_provider_params_t *in_params = + (umf_fixed_memory_provider_params_t *)params; + + if (in_params->ptr == NULL) { + LOG_ERR("memory pointer is missing"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (in_params->size == 0) { + LOG_ERR("memory size is 0"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + fixed_memory_provider_t *fixed_provider = + umf_ba_global_alloc(sizeof(*fixed_provider)); + if (!fixed_provider) { + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + + memset(fixed_provider, 0, sizeof(*fixed_provider)); + + coarse_params_t coarse_params = {0}; + coarse_params.provider = fixed_provider; + coarse_params.page_size = utils_get_page_size(); + // The alloc callback is not available in case of the fixed provider + // because it is a fixed-size memory provider + // and the entire memory is added as a single block + // to the coarse library. + coarse_params.cb.alloc = NULL; + coarse_params.cb.free = NULL; // not available for the fixed provider + coarse_params.cb.split = fixed_allocation_split_cb; + coarse_params.cb.merge = fixed_allocation_merge_cb; + + coarse_t *coarse = NULL; + ret = coarse_new(&coarse_params, &coarse); + if (ret != UMF_RESULT_SUCCESS) { + LOG_ERR("coarse_new() failed"); + goto err_free_fixed_provider; + } + + fixed_provider->coarse = coarse; + + fixed_provider->base = in_params->ptr; + fixed_provider->size = in_params->size; + + // add the entire memory as a single block + ret = coarse_add_memory_fixed(coarse, fixed_provider->base, + fixed_provider->size); + if (ret != UMF_RESULT_SUCCESS) { + LOG_ERR("adding memory block failed"); + goto err_coarse_delete; + } + + *provider = fixed_provider; + + return UMF_RESULT_SUCCESS; + +err_coarse_delete: + coarse_delete(fixed_provider->coarse); +err_free_fixed_provider: + umf_ba_global_free(fixed_provider); + return ret; +} + +static void fixed_finalize(void *provider) { + fixed_memory_provider_t *fixed_provider = provider; + coarse_delete(fixed_provider->coarse); + umf_ba_global_free(fixed_provider); +} + +static umf_result_t fixed_alloc(void *provider, size_t size, size_t alignment, + void **resultPtr) { + fixed_memory_provider_t *fixed_provider = + (fixed_memory_provider_t *)provider; + size_t page_size = utils_get_page_size(); + if (alignment < page_size) { + alignment = page_size; + } + return coarse_alloc(fixed_provider->coarse, size, alignment, resultPtr); +} + +static void fixed_get_last_native_error(void *provider, const char **ppMessage, + int32_t *pError) { + (void)provider; // unused + + if (ppMessage == NULL || pError == NULL) { + assert(0); + return; + } + + *pError = TLS_last_native_error.native_error; + if (TLS_last_native_error.errno_value == 0) { + *ppMessage = Native_error_str[*pError - UMF_FIXED_RESULT_SUCCESS]; + return; + } + + const char *msg; + size_t len; + size_t pos = 0; + + msg = Native_error_str[*pError - UMF_FIXED_RESULT_SUCCESS]; + len = strlen(msg); + memcpy(TLS_last_native_error.msg_buff + pos, msg, len + 1); + pos += len; + + msg = ": "; + len = strlen(msg); + memcpy(TLS_last_native_error.msg_buff + pos, msg, len + 1); + pos += len; + + utils_strerror(TLS_last_native_error.errno_value, + TLS_last_native_error.msg_buff + pos, TLS_MSG_BUF_LEN - pos); + + *ppMessage = TLS_last_native_error.msg_buff; +} + +static umf_result_t fixed_get_recommended_page_size(void *provider, size_t size, + size_t *page_size) { + (void)provider; // unused + (void)size; // unused + + *page_size = utils_get_page_size(); + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t fixed_get_min_page_size(void *provider, void *ptr, + size_t *page_size) { + (void)ptr; // unused + + return fixed_get_recommended_page_size(provider, 0, page_size); +} + +static umf_result_t fixed_purge_lazy(void *provider, void *ptr, size_t size) { + (void)provider; // unused + (void)ptr; // unused + (void)size; // unused + // purge_lazy is unsupported in case of the fixed memory provider + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + +static umf_result_t fixed_purge_force(void *provider, void *ptr, size_t size) { + (void)provider; // unused + errno = 0; + if (utils_purge(ptr, size, UMF_PURGE_FORCE)) { + fixed_store_last_native_error(UMF_FIXED_RESULT_ERROR_PURGE_FORCE_FAILED, + errno); + LOG_PERR("force purging failed"); + return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; + } + return UMF_RESULT_SUCCESS; +} + +static const char *fixed_get_name(void *provider) { + (void)provider; // unused + return "FIXED"; +} + +static umf_result_t fixed_allocation_split(void *provider, void *ptr, + size_t totalSize, size_t firstSize) { + fixed_memory_provider_t *fixed_provider = + (fixed_memory_provider_t *)provider; + return coarse_split(fixed_provider->coarse, ptr, totalSize, firstSize); +} + +static umf_result_t fixed_allocation_merge(void *provider, void *lowPtr, + void *highPtr, size_t totalSize) { + fixed_memory_provider_t *fixed_provider = + (fixed_memory_provider_t *)provider; + return coarse_merge(fixed_provider->coarse, lowPtr, highPtr, totalSize); +} + +static umf_result_t fixed_free(void *provider, void *ptr, size_t size) { + fixed_memory_provider_t *fixed_provider = + (fixed_memory_provider_t *)provider; + return coarse_free(fixed_provider->coarse, ptr, size); +} + +static umf_memory_provider_ops_t UMF_FIXED_MEMORY_PROVIDER_OPS = { + .version = UMF_VERSION_CURRENT, + .initialize = fixed_initialize, + .finalize = fixed_finalize, + .alloc = fixed_alloc, + .free = fixed_free, + .get_last_native_error = fixed_get_last_native_error, + .get_recommended_page_size = fixed_get_recommended_page_size, + .get_min_page_size = fixed_get_min_page_size, + .get_name = fixed_get_name, + .ext.purge_lazy = fixed_purge_lazy, + .ext.purge_force = fixed_purge_force, + .ext.allocation_merge = fixed_allocation_merge, + .ext.allocation_split = fixed_allocation_split, + .ipc.get_ipc_handle_size = NULL, + .ipc.get_ipc_handle = NULL, + .ipc.put_ipc_handle = NULL, + .ipc.open_ipc_handle = NULL, + .ipc.close_ipc_handle = NULL}; + +umf_memory_provider_ops_t *umfFixedMemoryProviderOps(void) { + return &UMF_FIXED_MEMORY_PROVIDER_OPS; +} + +umf_result_t umfFixedMemoryProviderParamsCreate( + umf_fixed_memory_provider_params_handle_t *hParams, void *ptr, + size_t size) { + libumfInit(); + if (hParams == NULL) { + LOG_ERR("Memory Provider params handle is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (ptr == NULL) { + LOG_ERR("Memory pointer is NULL"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + if (size == 0) { + LOG_ERR("Size must be greater then 0"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + umf_fixed_memory_provider_params_handle_t params = + umf_ba_global_alloc(sizeof(*params)); + if (params == NULL) { + LOG_ERR("Allocating memory for the Memory Provider params failed"); + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + + params->ptr = ptr; + params->size = size; + + *hParams = params; + + return UMF_RESULT_SUCCESS; +} + +umf_result_t umfFixedMemoryProviderParamsDestroy( + umf_fixed_memory_provider_params_handle_t hParams) { + if (hParams != NULL) { + umf_ba_global_free(hParams); + } + + return UMF_RESULT_SUCCESS; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d5a07bfbb..bb353a889 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -317,6 +317,10 @@ if(LINUX AND (NOT UMF_DISABLE_HWLOC)) # OS-specific functions are implemented NAME provider_file_memory_ipc SRCS provider_file_memory_ipc.cpp ${BA_SOURCES_FOR_TEST} LIBS ${UMF_UTILS_FOR_TEST}) + add_umf_test( + NAME provider_fixed_memory + SRCS provider_fixed_memory.cpp + LIBS ${UMF_UTILS_FOR_TEST}) # This test requires Linux-only file memory provider if(UMF_POOL_JEMALLOC_ENABLED) diff --git a/test/provider_fixed_memory.cpp b/test/provider_fixed_memory.cpp new file mode 100644 index 000000000..383ecc15c --- /dev/null +++ b/test/provider_fixed_memory.cpp @@ -0,0 +1,330 @@ +#include "base.hpp" + +#include "cpp_helpers.hpp" +#include "test_helpers.h" +#ifndef _WIN32 +#include "test_helpers_linux.h" +#endif + +#include +#include + +using umf_test::test; + +#define INVALID_PTR ((void *)0x01) + +typedef enum purge_t { + PURGE_NONE = 0, + PURGE_LAZY = 1, + PURGE_FORCE = 2, +} purge_t; + +static const char *Native_error_str[] = { + "success", // UMF_FIXED_RESULT_SUCCESS + "force purging failed", // UMF_FIXED_RESULT_ERROR_PURGE_FORCE_FAILED +}; + +// Test helpers + +static int compare_native_error_str(const char *message, int error) { + const char *error_str = Native_error_str[error - UMF_FIXED_RESULT_SUCCESS]; + size_t len = strlen(error_str); + return strncmp(message, error_str, len); +} + +using providerCreateExtParams = std::tuple; + +static void providerCreateExt(providerCreateExtParams params, + umf::provider_unique_handle_t *handle) { + umf_memory_provider_handle_t hProvider = nullptr; + auto [provider_ops, provider_params] = params; + + auto ret = + umfMemoryProviderCreate(provider_ops, provider_params, &hProvider); + ASSERT_EQ(ret, UMF_RESULT_SUCCESS); + ASSERT_NE(hProvider, nullptr); + + *handle = + umf::provider_unique_handle_t(hProvider, &umfMemoryProviderDestroy); +} + +struct FixedProviderTest + : umf_test::test, + ::testing::WithParamInterface { + void SetUp() override { + test::SetUp(); + + // Allocate a memory buffer to use with the fixed memory provider + memory_size = utils_get_page_size() * 10; // Allocate 10 pages + memory_buffer = malloc(memory_size); + ASSERT_NE(memory_buffer, nullptr); + + // Create provider parameters + umf_fixed_memory_provider_params_handle_t params = nullptr; + umf_result_t res = umfFixedMemoryProviderParamsCreate( + ¶ms, memory_buffer, memory_size); + ASSERT_EQ(res, UMF_RESULT_SUCCESS); + ASSERT_NE(params, nullptr); + + providerCreateExt(std::make_tuple(umfFixedMemoryProviderOps(), params), + &provider); + + umfFixedMemoryProviderParamsDestroy(params); + umf_result_t umf_result = + umfMemoryProviderGetMinPageSize(provider.get(), NULL, &page_size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + page_plus_64 = page_size + 64; + } + + void TearDown() override { + if (memory_buffer) { + free(memory_buffer); + memory_buffer = nullptr; + } + test::TearDown(); + } + + umf::provider_unique_handle_t provider; + size_t page_size; + size_t page_plus_64; + void *memory_buffer = nullptr; + size_t memory_size = 0; +}; + +static void test_alloc_free_success(umf_memory_provider_handle_t provider, + size_t size, size_t alignment, + purge_t purge) { + void *ptr = nullptr; + + umf_result_t umf_result = + umfMemoryProviderAlloc(provider, size, alignment, &ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr, nullptr); + + memset(ptr, 0xFF, size); + + if (purge == PURGE_LAZY) { + umf_result = umfMemoryProviderPurgeLazy(provider, ptr, size); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); + } else if (purge == PURGE_FORCE) { + umf_result = umfMemoryProviderPurgeForce(provider, ptr, size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + } + + umf_result = umfMemoryProviderFree(provider, ptr, size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +} + +static void verify_last_native_error(umf_memory_provider_handle_t provider, + int32_t err) { + const char *message; + int32_t error; + umfMemoryProviderGetLastNativeError(provider, &message, &error); + ASSERT_EQ(error, err); + ASSERT_EQ(compare_native_error_str(message, error), 0); +} + +static void test_alloc_failure(umf_memory_provider_handle_t provider, + size_t size, size_t alignment, + umf_result_t result, int32_t err) { + void *ptr = nullptr; + umf_result_t umf_result = + umfMemoryProviderAlloc(provider, size, alignment, &ptr); + ASSERT_EQ(umf_result, result); + ASSERT_EQ(ptr, nullptr); + + if (umf_result == UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC) { + verify_last_native_error(provider, err); + } +} + +// TESTS + +// Positive tests using test_alloc_free_success + +INSTANTIATE_TEST_SUITE_P(fixedProviderTest, FixedProviderTest, + ::testing::Values(providerCreateExtParams{ + umfFixedMemoryProviderOps(), nullptr})); + +TEST_P(FixedProviderTest, create_destroy) { + // Creation and destruction are handled in SetUp and TearDown +} + +TEST_P(FixedProviderTest, two_allocations) { + umf_result_t umf_result; + void *ptr1 = nullptr; + void *ptr2 = nullptr; + size_t size = page_plus_64; + size_t alignment = page_size; + + umf_result = umfMemoryProviderAlloc(provider.get(), size, alignment, &ptr1); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr1, nullptr); + + umf_result = umfMemoryProviderAlloc(provider.get(), size, alignment, &ptr2); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr2, nullptr); + + ASSERT_NE(ptr1, ptr2); + if ((uintptr_t)ptr1 > (uintptr_t)ptr2) { + ASSERT_GT((uintptr_t)ptr1 - (uintptr_t)ptr2, size); + } else { + ASSERT_GT((uintptr_t)ptr2 - (uintptr_t)ptr1, size); + } + + memset(ptr1, 0x11, size); + memset(ptr2, 0x22, size); + + umf_result = umfMemoryProviderFree(provider.get(), ptr1, size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umf_result = umfMemoryProviderFree(provider.get(), ptr2, size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +} + +TEST_P(FixedProviderTest, alloc_page64_align_0) { + test_alloc_free_success(provider.get(), page_plus_64, 0, PURGE_NONE); +} + +TEST_P(FixedProviderTest, alloc_page64_align_page_div_2) { + test_alloc_free_success(provider.get(), page_plus_64, page_size / 2, + PURGE_NONE); +} + +TEST_P(FixedProviderTest, purge_lazy) { + test_alloc_free_success(provider.get(), page_size, 0, PURGE_LAZY); +} + +TEST_P(FixedProviderTest, purge_force) { + test_alloc_free_success(provider.get(), page_size, 0, PURGE_FORCE); +} + +// Negative tests using test_alloc_failure + +TEST_P(FixedProviderTest, alloc_WRONG_SIZE) { + test_alloc_failure(provider.get(), (size_t)-1, 0, + UMF_RESULT_ERROR_INVALID_ARGUMENT, 0); +} + +TEST_P(FixedProviderTest, alloc_page64_WRONG_ALIGNMENT_3_pages) { + test_alloc_failure(provider.get(), page_plus_64, 3 * page_size, + UMF_RESULT_ERROR_INVALID_ALIGNMENT, 0); +} + +TEST_P(FixedProviderTest, alloc_3pages_WRONG_ALIGNMENT_3pages) { + test_alloc_failure(provider.get(), 3 * page_size, 3 * page_size, + UMF_RESULT_ERROR_INVALID_ALIGNMENT, 0); +} + +TEST_P(FixedProviderTest, alloc_page64_align_page_plus_1_WRONG_ALIGNMENT_1) { + test_alloc_failure(provider.get(), page_plus_64, page_size + 1, + UMF_RESULT_ERROR_INVALID_ALIGNMENT, 0); +} + +TEST_P(FixedProviderTest, alloc_page64_align_one_half_pages_WRONG_ALIGNMENT_2) { + test_alloc_failure(provider.get(), page_plus_64, + page_size + (page_size / 2), + UMF_RESULT_ERROR_INVALID_ALIGNMENT, 0); +} + +// Other positive tests + +TEST_P(FixedProviderTest, get_min_page_size) { + size_t min_page_size; + umf_result_t umf_result = umfMemoryProviderGetMinPageSize( + provider.get(), nullptr, &min_page_size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_LE(min_page_size, page_size); +} + +TEST_P(FixedProviderTest, get_recommended_page_size) { + size_t min_page_size; + umf_result_t umf_result = umfMemoryProviderGetMinPageSize( + provider.get(), nullptr, &min_page_size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_LE(min_page_size, page_size); + + size_t recommended_page_size; + umf_result = umfMemoryProviderGetRecommendedPageSize( + provider.get(), 0, &recommended_page_size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_GE(recommended_page_size, min_page_size); +} + +TEST_P(FixedProviderTest, get_name) { + const char *name = umfMemoryProviderGetName(provider.get()); + ASSERT_STREQ(name, "FIXED"); +} + +TEST_P(FixedProviderTest, free_size_0_ptr_not_null) { + umf_result_t umf_result = + umfMemoryProviderFree(provider.get(), INVALID_PTR, 0); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); +} + +TEST_P(FixedProviderTest, free_NULL) { + umf_result_t umf_result = umfMemoryProviderFree(provider.get(), nullptr, 0); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +} + +// Other negative tests + +TEST_P(FixedProviderTest, free_INVALID_POINTER_SIZE_GT_0) { + umf_result_t umf_result = + umfMemoryProviderFree(provider.get(), INVALID_PTR, page_plus_64); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); +} + +TEST_P(FixedProviderTest, purge_lazy_INVALID_POINTER) { + umf_result_t umf_result = + umfMemoryProviderPurgeLazy(provider.get(), INVALID_PTR, 1); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); +} + +TEST_P(FixedProviderTest, purge_force_INVALID_POINTER) { + umf_result_t umf_result = + umfMemoryProviderPurgeForce(provider.get(), INVALID_PTR, 1); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC); + + verify_last_native_error(provider.get(), + UMF_FIXED_RESULT_ERROR_PURGE_FORCE_FAILED); +} + +// Params tests + +TEST_F(test, params_null_handle) { + constexpr size_t memory_size = 100; + char memory_buffer[memory_size]; + umf_result_t umf_result = + umfFixedMemoryProviderParamsCreate(nullptr, memory_buffer, memory_size); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); + + umf_result = umfFixedMemoryProviderParamsDestroy(nullptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); +} + +TEST_F(test, create_with_null_ptr) { + constexpr size_t memory_size = 100; + umf_fixed_memory_provider_params_handle_t wrong_params = nullptr; + umf_result_t umf_result = + umfFixedMemoryProviderParamsCreate(&wrong_params, nullptr, memory_size); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); + ASSERT_EQ(wrong_params, nullptr); +} + +TEST_F(test, create_with_zero_size) { + constexpr size_t memory_size = 100; + char memory_buffer[memory_size]; + umf_fixed_memory_provider_params_handle_t wrong_params = nullptr; + umf_result_t umf_result = + umfFixedMemoryProviderParamsCreate(&wrong_params, memory_buffer, 0); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); + ASSERT_EQ(wrong_params, nullptr); +} + +TEST_P(FixedProviderTest, alloc_size_exceeds_buffer) { + size_t size = memory_size + page_size; + test_alloc_failure(provider.get(), size, 0, + UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY, 0); +}