diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ceb75368..e54d1146e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -421,6 +421,7 @@ if(NOT UMF_DISABLE_HWLOC) add_optional_symbol(umfOsMemoryProviderOps) if(LINUX) add_optional_symbol(umfDevDaxMemoryProviderOps) + add_optional_symbol(umfFileMemoryProviderOps) endif() endif() diff --git a/README.md b/README.md index 64894ecf2..0c80bee12 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,21 @@ with the `disable_provider_free` parameter set to true. 1) Linux OS 2) A character device file /dev/daxX.Y created in the OS. +#### File memory provider (Linux only yet) + +A memory provider that provides memory by mapping a regular, extendable file. + +The file memory provider does not support the free operation +(`umfMemoryProviderFree()` always returns `UMF_RESULT_ERROR_NOT_SUPPORTED`), +so it should be used with a pool manager that will take over +the managing of the provided memory - for example the jemalloc pool +with the `disable_provider_free` parameter set to true. + +##### Requirements + +1) Linux OS +2) A length of a path of a file to be mapped can be `PATH_MAX` (4096) characters at most. + ### Memory pool managers #### Proxy pool (part of libumf) diff --git a/include/umf/providers/provider_file_memory.h b/include/umf/providers/provider_file_memory.h new file mode 100644 index 000000000..4b5b59b81 --- /dev/null +++ b/include/umf/providers/provider_file_memory.h @@ -0,0 +1,57 @@ +/* + * 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_FILE_MEMORY_PROVIDER_H +#define UMF_FILE_MEMORY_PROVIDER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @cond +#define UMF_FILE_RESULTS_START_FROM 3000 +/// @endcond + +/// @brief Memory provider settings struct +typedef struct umf_file_memory_provider_params_t { + /// a path to the file (of maximum length PATH_MAX characters) + const char *path; + /// combination of 'umf_mem_protection_flags_t' flags + unsigned protection; + /// memory visibility mode + umf_memory_visibility_t visibility; +} umf_file_memory_provider_params_t; + +/// @brief File Memory Provider operation results +typedef enum umf_file_memory_provider_native_error { + UMF_FILE_RESULT_SUCCESS = UMF_FILE_RESULTS_START_FROM, ///< Success + UMF_FILE_RESULT_ERROR_ALLOC_FAILED, ///< Memory allocation failed + UMF_FILE_RESULT_ERROR_FREE_FAILED, ///< Memory deallocation failed + UMF_FILE_RESULT_ERROR_PURGE_FORCE_FAILED, ///< Force purging failed +} umf_file_memory_provider_native_error_t; + +umf_memory_provider_ops_t *umfFileMemoryProviderOps(void); + +/// @brief Create default params for the file memory provider +static inline umf_file_memory_provider_params_t +umfFileMemoryProviderParamsDefault(const char *path) { + umf_file_memory_provider_params_t params = { + path, /* a path to the file */ + UMF_PROTECTION_READ | UMF_PROTECTION_WRITE, /* protection */ + UMF_MEM_MAP_PRIVATE, /* visibility mode */ + }; + + return params; +} + +#ifdef __cplusplus +} +#endif + +#endif /* UMF_FILE_MEMORY_PROVIDER_H */ diff --git a/scripts/docs_config/api.rst b/scripts/docs_config/api.rst index 5e39361e1..625116dd2 100644 --- a/scripts/docs_config/api.rst +++ b/scripts/docs_config/api.rst @@ -104,6 +104,14 @@ A memory provider that provides memory from a device DAX (a character device fil .. doxygenfile:: provider_devdax_memory.h :sections: define enum typedef func var +File Memory Provider +------------------------------------------ + +A memory provider that provides memory by mapping a regular, extendable file. + +.. doxygenfile:: provider_file_memory.h + :sections: define enum typedef func var + Memspace ========================================== diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00aeb8a47..845a178e3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -102,6 +102,7 @@ set(UMF_SOURCES_WINDOWS libumf_windows.c) set(UMF_SOURCES_COMMON_LINUX_MACOSX provider/provider_devdax_memory.c + provider/provider_file_memory.c provider/provider_os_memory.c provider/provider_os_memory_posix.c memtargets/memtarget_numa.c diff --git a/src/provider/provider_file_memory.c b/src/provider/provider_file_memory.c new file mode 100644 index 000000000..fd8d04371 --- /dev/null +++ b/src/provider/provider_file_memory.c @@ -0,0 +1,644 @@ +/* + * 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 "critnib.h" +#include "provider_file_memory_internal.h" +#include "provider_os_memory_internal.h" +#include "utils_common.h" +#include "utils_concurrency.h" +#include "utils_log.h" + +#include +#include +#include + +#define TLS_MSG_BUF_LEN 1024 + +typedef struct file_last_native_error_t { + int32_t native_error; + int errno_value; + char msg_buff[TLS_MSG_BUF_LEN]; +} file_last_native_error_t; + +static __TLS file_last_native_error_t TLS_last_native_error; + +// helper values used only in the Native_error_str array +#define _UMF_FILE_RESULT_SUCCESS \ + (UMF_FILE_RESULT_SUCCESS - UMF_FILE_RESULT_SUCCESS) +#define _UMF_FILE_RESULT_ERROR_ALLOC_FAILED \ + (UMF_FILE_RESULT_ERROR_ALLOC_FAILED - UMF_FILE_RESULT_SUCCESS) +#define _UMF_FILE_RESULT_ERROR_FREE_FAILED \ + (UMF_FILE_RESULT_ERROR_FREE_FAILED - UMF_FILE_RESULT_SUCCESS) +#define _UMF_FILE_RESULT_ERROR_PURGE_FORCE_FAILED \ + (UMF_FILE_RESULT_ERROR_PURGE_FORCE_FAILED - UMF_FILE_RESULT_SUCCESS) + +static const char *Native_error_str[] = { + [_UMF_FILE_RESULT_SUCCESS] = "success", + [_UMF_FILE_RESULT_ERROR_ALLOC_FAILED] = "memory allocation failed", + [_UMF_FILE_RESULT_ERROR_FREE_FAILED] = "memory deallocation failed", + [_UMF_FILE_RESULT_ERROR_PURGE_FORCE_FAILED] = "force purging failed", +}; + +static void file_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 +file_translate_params(umf_file_memory_provider_params_t *in_params, + file_memory_provider_t *provider) { + umf_result_t result; + + result = os_translate_mem_protection_flags(in_params->protection, + &provider->protection); + if (result != UMF_RESULT_SUCCESS) { + LOG_ERR("incorrect memory protection flags: %u", in_params->protection); + return result; + } + + result = os_translate_mem_visibility_flag(in_params->visibility, + &provider->visibility); + if (result != UMF_RESULT_SUCCESS) { + LOG_ERR("incorrect memory visibility flag: %u", in_params->visibility); + return result; + } + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t file_initialize(void *params, void **provider) { + umf_result_t ret; + + if (provider == NULL || params == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + umf_file_memory_provider_params_t *in_params = + (umf_file_memory_provider_params_t *)params; + + size_t page_size = os_get_page_size(); + + if (in_params->path == NULL) { + LOG_ERR("file path is missing"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + file_memory_provider_t *file_provider = + umf_ba_global_alloc(sizeof(*file_provider)); + if (!file_provider) { + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + } + + memset(file_provider, 0, sizeof(*file_provider)); + + file_provider->page_size = page_size; + + ret = file_translate_params(in_params, file_provider); + if (ret != UMF_RESULT_SUCCESS) { + goto err_free_file_provider; + } + + if (util_copy_path(in_params->path, file_provider->path, PATH_MAX)) { + goto err_free_file_provider; + } + + file_provider->fd = os_file_open_or_create(in_params->path); + if (file_provider->fd == -1) { + LOG_ERR("cannot open the file: %s", in_params->path); + ret = UMF_RESULT_ERROR_INVALID_ARGUMENT; + goto err_free_file_provider; + } + + if (os_set_file_size(file_provider->fd, page_size)) { + LOG_ERR("cannot set size of the file: %s", in_params->path); + ret = UMF_RESULT_ERROR_UNKNOWN; + goto err_close_fd; + } + + file_provider->size_fd = page_size; + + LOG_DEBUG("size of the file %s is: %zu", in_params->path, + file_provider->size_fd); + + if (util_mutex_init(&file_provider->lock) == NULL) { + LOG_ERR("lock init failed"); + ret = UMF_RESULT_ERROR_UNKNOWN; + goto err_close_fd; + } + + file_provider->fd_offset_map = critnib_new(); + if (!file_provider->fd_offset_map) { + LOG_ERR("creating the map of file descriptor offsets failed"); + ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + goto err_mutex_destroy_not_free; + } + + file_provider->mmaps = critnib_new(); + if (!file_provider->mmaps) { + LOG_ERR("creating the map of memory mappings failed"); + ret = UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + goto err_delete_fd_offset_map; + } + + *provider = file_provider; + + return UMF_RESULT_SUCCESS; + +err_delete_fd_offset_map: + critnib_delete(file_provider->fd_offset_map); +err_mutex_destroy_not_free: + util_mutex_destroy_not_free(&file_provider->lock); +err_close_fd: + utils_close_fd(file_provider->fd); +err_free_file_provider: + umf_ba_global_free(file_provider); + return ret; +} + +static void file_finalize(void *provider) { + if (provider == NULL) { + assert(0); + return; + } + + file_memory_provider_t *file_provider = provider; + + uintptr_t key = 0; + uintptr_t rkey = 0; + void *rvalue = NULL; + while (1 == + critnib_find(file_provider->mmaps, key, FIND_G, &rkey, &rvalue)) { + os_munmap((void *)rkey, (size_t)rvalue); + critnib_remove(file_provider->mmaps, rkey); + key = rkey; + } + + util_mutex_destroy_not_free(&file_provider->lock); + utils_close_fd(file_provider->fd); + critnib_delete(file_provider->fd_offset_map); + critnib_delete(file_provider->mmaps); + umf_ba_global_free(file_provider); +} + +static umf_result_t file_mmap_aligned(file_memory_provider_t *file_provider, + size_t size, size_t alignment) { + int prot = file_provider->protection; + int flag = file_provider->visibility; + int fd = file_provider->fd; + size_t size_fd = file_provider->size_fd; + size_t offset_fd = file_provider->offset_fd; + size_t page_size = file_provider->page_size; + + assert(fd > 0); + + // We have to increase size by alignment to be able to "cut out" + // the correctly aligned part of the memory + size_t extended_size = size + alignment; + if (extended_size < size) { + LOG_ERR("invalid size of allocation"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; // arithmetic overflow + } + + size_t rest = extended_size & (page_size - 1); + if (rest) { + extended_size += page_size - rest; + } + if (extended_size < size) { + LOG_ERR("invalid size of allocation"); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; // arithmetic overflow + } + + if (offset_fd + extended_size > size_fd) { + if (os_fallocate(fd, offset_fd, extended_size)) { + LOG_ERR("cannot grow the file size from %zu to %zu", size_fd, + offset_fd + extended_size); + return UMF_RESULT_ERROR_UNKNOWN; + } + + LOG_DEBUG("file size grown from %zu to %zu", size_fd, + offset_fd + extended_size); + file_provider->size_fd = size_fd = offset_fd + extended_size; + } + + ASSERT_IS_ALIGNED(extended_size, page_size); + ASSERT_IS_ALIGNED(offset_fd, page_size); + + void *ptr = os_mmap(NULL, extended_size, prot, flag, fd, offset_fd); + if (ptr == NULL) { + LOG_PERR("memory mapping failed"); + return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; + } + + int ret = critnib_insert(file_provider->mmaps, (uintptr_t)ptr, + (void *)(uintptr_t)extended_size, 0 /* update */); + if (ret) { + LOG_ERR("inserting a value to the map of memory mapping failed " + "(addr=%p, size=%zu)", + ptr, extended_size); + } + + file_provider->base_mmap = ptr; + file_provider->size_mmap = extended_size; + file_provider->offset_mmap = 0; + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t file_alloc_aligned(file_memory_provider_t *file_provider, + size_t size, size_t alignment, + void **out_addr, + size_t *alloc_offset_fd) { + assert(alloc_offset_fd); + assert(out_addr); + + umf_result_t umf_result; + + if (util_mutex_lock(&file_provider->lock)) { + LOG_ERR("locking file data failed"); + return UMF_RESULT_ERROR_UNKNOWN; + } + + if (file_provider->size_mmap - file_provider->offset_mmap < size) { + umf_result = file_mmap_aligned(file_provider, size, alignment); + if (umf_result != UMF_RESULT_SUCCESS) { + util_mutex_unlock(&file_provider->lock); + return umf_result; + } + } + + void *base_mmap = file_provider->base_mmap; + assert(base_mmap); + + uintptr_t new_aligned_ptr = + (uintptr_t)base_mmap + file_provider->offset_mmap; + if (alignment) { + uintptr_t rest = new_aligned_ptr & (alignment - 1); + if (rest) { + new_aligned_ptr += alignment - rest; + } + ASSERT_IS_ALIGNED(new_aligned_ptr, alignment); + } + + size_t new_offset_mmap = new_aligned_ptr - (uintptr_t)base_mmap; + if (file_provider->size_mmap - new_offset_mmap < size) { + umf_result = file_mmap_aligned(file_provider, size, alignment); + if (umf_result != UMF_RESULT_SUCCESS) { + util_mutex_unlock(&file_provider->lock); + return umf_result; + } + } + + size_t old_offset_mmap = file_provider->offset_mmap; + file_provider->offset_mmap = new_offset_mmap; + *alloc_offset_fd = + file_provider->offset_fd + new_offset_mmap - old_offset_mmap; + file_provider->offset_fd = *alloc_offset_fd + size; + + *out_addr = (void *)new_aligned_ptr; + + util_mutex_unlock(&file_provider->lock); + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t file_alloc(void *provider, size_t size, size_t alignment, + void **resultPtr) { + umf_result_t umf_result; + int ret; + + if (provider == NULL || resultPtr == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + // alignment must be a power of two and a multiple of sizeof(void *) + if (alignment && + ((alignment & (alignment - 1)) || (alignment % sizeof(void *)))) { + LOG_ERR("wrong alignment: %zu (not a power of 2 or a multiple of " + "sizeof(void *))", + alignment); + return UMF_RESULT_ERROR_INVALID_ALIGNMENT; + } + + file_memory_provider_t *file_provider = (file_memory_provider_t *)provider; + + void *addr = NULL; + size_t alloc_offset_fd; // needed for critnib_insert() + umf_result = file_alloc_aligned(file_provider, size, alignment, &addr, + &alloc_offset_fd); + if (umf_result != UMF_RESULT_SUCCESS) { + file_store_last_native_error(UMF_FILE_RESULT_ERROR_ALLOC_FAILED, 0); + LOG_ERR("memory allocation failed"); + return umf_result; + } + + // store (offset_fd + 1) to be able to store offset_fd == 0 + ret = critnib_insert(file_provider->fd_offset_map, (uintptr_t)addr, + (void *)(uintptr_t)(alloc_offset_fd + 1), + 0 /* update */); + if (ret) { + LOG_ERR("inserting a value to the file descriptor offset map failed " + "(addr=%p, offset=%zu)", + addr, alloc_offset_fd); + } + + *resultPtr = addr; + + return UMF_RESULT_SUCCESS; +} + +// free() is not supported +static umf_result_t file_free(void *provider, void *ptr, size_t size) { + (void)provider; // unused + (void)ptr; // unused + (void)size; // unused + + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + +static void file_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_FILE_RESULT_SUCCESS]; + return; + } + + const char *msg; + size_t len; + size_t pos = 0; + + msg = Native_error_str[*pError - UMF_FILE_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; + + os_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 file_get_recommended_page_size(void *provider, size_t size, + size_t *page_size) { + (void)size; // unused + + if (provider == NULL || page_size == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *page_size = os_get_page_size(); + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t file_get_min_page_size(void *provider, void *ptr, + size_t *page_size) { + (void)ptr; // unused + + return file_get_recommended_page_size(provider, 0, page_size); +} + +static umf_result_t file_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 file memory provider, + // because the MADV_FREE operation can be applied + // only to private anonymous pages (see madvise(2)). + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} + +static umf_result_t file_purge_force(void *provider, void *ptr, size_t size) { + if (provider == NULL || ptr == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + errno = 0; + if (os_purge(ptr, size, UMF_PURGE_FORCE)) { + file_store_last_native_error(UMF_FILE_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 *file_get_name(void *provider) { + (void)provider; // unused + return "FILE"; +} + +// This function is supposed to be thread-safe, so it should NOT be called concurrently +// with file_allocation_merge() with the same pointer. +static umf_result_t file_allocation_split(void *provider, void *ptr, + size_t totalSize, size_t firstSize) { + (void)totalSize; + + file_memory_provider_t *file_provider = (file_memory_provider_t *)provider; + if (file_provider->fd <= 0) { + return UMF_RESULT_SUCCESS; + } + + void *value = critnib_get(file_provider->fd_offset_map, (uintptr_t)ptr); + if (value == NULL) { + LOG_ERR("file_allocation_split(): getting a value from the file " + "descriptor offset map failed (addr=%p)", + ptr); + return UMF_RESULT_ERROR_UNKNOWN; + } + + uintptr_t new_key = (uintptr_t)ptr + firstSize; + void *new_value = (void *)((uintptr_t)value + firstSize); + int ret = critnib_insert(file_provider->fd_offset_map, new_key, new_value, + 0 /* update */); + if (ret) { + LOG_ERR("file_allocation_split(): inserting a value to the file " + "descriptor offset map failed (addr=%p, offset=%zu)", + (void *)new_key, (size_t)new_value - 1); + return UMF_RESULT_ERROR_UNKNOWN; + } + + return UMF_RESULT_SUCCESS; +} + +// It should NOT be called concurrently with file_allocation_split() with the same pointer. +static umf_result_t file_allocation_merge(void *provider, void *lowPtr, + void *highPtr, size_t totalSize) { + (void)lowPtr; + (void)totalSize; + + file_memory_provider_t *file_provider = (file_memory_provider_t *)provider; + if (file_provider->fd <= 0) { + return UMF_RESULT_SUCCESS; + } + + void *value = + critnib_remove(file_provider->fd_offset_map, (uintptr_t)highPtr); + if (value == NULL) { + LOG_ERR("file_allocation_merge(): removing a value from the file " + "descriptor offset map failed (addr=%p)", + highPtr); + return UMF_RESULT_ERROR_UNKNOWN; + } + + return UMF_RESULT_SUCCESS; +} + +typedef struct file_ipc_data_t { + char path[PATH_MAX]; + size_t offset_fd; + size_t size; +} file_ipc_data_t; + +static umf_result_t file_get_ipc_handle_size(void *provider, size_t *size) { + if (provider == NULL || size == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *size = sizeof(file_ipc_data_t); + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t file_get_ipc_handle(void *provider, const void *ptr, + size_t size, void *providerIpcData) { + if (provider == NULL || ptr == NULL || providerIpcData == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + file_memory_provider_t *file_provider = (file_memory_provider_t *)provider; + + void *value = critnib_get(file_provider->fd_offset_map, (uintptr_t)ptr); + if (value == NULL) { + LOG_ERR("file_get_ipc_handle(): getting a value from the IPC cache " + "failed (addr=%p)", + ptr); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + file_ipc_data_t *file_ipc_data = (file_ipc_data_t *)providerIpcData; + file_ipc_data->offset_fd = (size_t)value - 1; + file_ipc_data->size = size; + strncpy(file_ipc_data->path, file_provider->path, PATH_MAX - 1); + file_ipc_data->path[PATH_MAX - 1] = '\0'; + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t file_put_ipc_handle(void *provider, void *providerIpcData) { + if (provider == NULL || providerIpcData == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + file_memory_provider_t *file_provider = (file_memory_provider_t *)provider; + file_ipc_data_t *file_ipc_data = (file_ipc_data_t *)providerIpcData; + + if (strncmp(file_ipc_data->path, file_provider->path, PATH_MAX)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + return UMF_RESULT_SUCCESS; +} + +static umf_result_t file_open_ipc_handle(void *provider, void *providerIpcData, + void **ptr) { + if (provider == NULL || providerIpcData == NULL || ptr == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + file_memory_provider_t *file_provider = (file_memory_provider_t *)provider; + file_ipc_data_t *file_ipc_data = (file_ipc_data_t *)providerIpcData; + umf_result_t ret = UMF_RESULT_SUCCESS; + int fd; + + if (strncmp(file_ipc_data->path, file_provider->path, PATH_MAX)) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + fd = os_file_open(file_ipc_data->path); + if (fd <= 0) { + LOG_PERR("opening the file to be mapped (%s) failed", + file_ipc_data->path); + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + *ptr = os_mmap(NULL, file_ipc_data->size, file_provider->protection, + file_provider->visibility, fd, file_ipc_data->offset_fd); + (void)utils_close_fd(fd); + if (*ptr == NULL) { + file_store_last_native_error(UMF_FILE_RESULT_ERROR_ALLOC_FAILED, errno); + LOG_PERR("memory mapping failed"); + ret = UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; + } + + return ret; +} + +static umf_result_t file_close_ipc_handle(void *provider, void *ptr, + size_t size) { + if (provider == NULL || ptr == NULL) { + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + } + + errno = 0; + int ret = os_munmap(ptr, size); + // ignore error when size == 0 + if (ret && (size > 0)) { + file_store_last_native_error(UMF_FILE_RESULT_ERROR_FREE_FAILED, errno); + LOG_PERR("memory unmapping failed"); + + return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; + } + + return UMF_RESULT_SUCCESS; +} + +static umf_memory_provider_ops_t UMF_FILE_MEMORY_PROVIDER_OPS = { + .version = UMF_VERSION_CURRENT, + .initialize = file_initialize, + .finalize = file_finalize, + .alloc = file_alloc, + .free = file_free, + .get_last_native_error = file_get_last_native_error, + .get_recommended_page_size = file_get_recommended_page_size, + .get_min_page_size = file_get_min_page_size, + .get_name = file_get_name, + .ext.purge_lazy = file_purge_lazy, + .ext.purge_force = file_purge_force, + .ext.allocation_merge = file_allocation_merge, + .ext.allocation_split = file_allocation_split, + .ipc.get_ipc_handle_size = file_get_ipc_handle_size, + .ipc.get_ipc_handle = file_get_ipc_handle, + .ipc.put_ipc_handle = file_put_ipc_handle, + .ipc.open_ipc_handle = file_open_ipc_handle, + .ipc.close_ipc_handle = file_close_ipc_handle}; + +umf_memory_provider_ops_t *umfFileMemoryProviderOps(void) { + return &UMF_FILE_MEMORY_PROVIDER_OPS; +} diff --git a/src/provider/provider_file_memory_internal.h b/src/provider/provider_file_memory_internal.h new file mode 100644 index 000000000..ce77719d5 --- /dev/null +++ b/src/provider/provider_file_memory_internal.h @@ -0,0 +1,50 @@ +/* + * 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_FILE_MEMORY_PROVIDER_INTERNAL_H +#define UMF_FILE_MEMORY_PROVIDER_INTERNAL_H + +#include + +#include "critnib.h" +#include "utils_concurrency.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct file_memory_provider_t { + os_mutex_t lock; // lock for file parameters (size and offsets) + + char path[PATH_MAX]; // a path to the file + int fd; // file descriptor for memory mapping + size_t size_fd; // size of the file used for memory mappings + size_t offset_fd; // offset in the file used for memory mappings + + void *base_mmap; // base address of the current memory mapping + size_t size_mmap; // size of the current memory mapping + size_t offset_mmap; // data offset in the current memory mapping + + unsigned protection; // combination of OS-specific protection flags + unsigned visibility; // memory visibility mode + size_t page_size; // minimum page size + + critnib *mmaps; // a critnib map storing mmap mappings (addr, size) + + // A critnib map storing (ptr, fd_offset + 1) pairs. We add 1 to fd_offset + // in order to be able to store fd_offset equal 0, because + // critnib_get() returns value or NULL, so a value cannot equal 0. + // It is needed mainly in the get_ipc_handle and open_ipc_handle hooks + // to mmap a specific part of a file. + critnib *fd_offset_map; +} file_memory_provider_t; + +#ifdef __cplusplus +} +#endif + +#endif /* UMF_FILE_MEMORY_PROVIDER_INTERNAL_H */ diff --git a/src/provider/provider_os_memory_internal.h b/src/provider/provider_os_memory_internal.h index e3bf4002c..54972686d 100644 --- a/src/provider/provider_os_memory_internal.h +++ b/src/provider/provider_os_memory_internal.h @@ -109,6 +109,12 @@ void os_strerror(int errnum, char *buf, size_t buflen); int os_devdax_open(const char *path); +int os_file_open(const char *path); + +int os_file_open_or_create(const char *path); + +int os_fallocate(int fd, long offset, long len); + #ifdef __cplusplus } #endif diff --git a/src/provider/provider_os_memory_linux.c b/src/provider/provider_os_memory_linux.c index a472dd4b7..9a90e1145 100644 --- a/src/provider/provider_os_memory_linux.c +++ b/src/provider/provider_os_memory_linux.c @@ -150,8 +150,11 @@ int os_set_file_size(int fd, size_t size) { errno = 0; int ret = ftruncate(fd, size); if (ret) { - LOG_PERR("ftruncate(%i, %zu) failed", fd, size); + LOG_PERR("setting size %zu of a file failed", size); + } else { + LOG_DEBUG("set size of a file to %zu bytes", size); } + return ret; } @@ -178,3 +181,7 @@ void *os_devdax_mmap(void *hint_addr, size_t length, int prot, int fd) { return NULL; } + +int os_fallocate(int fd, long offset, long len) { + return posix_fallocate(fd, offset, len); +} diff --git a/src/provider/provider_os_memory_macosx.c b/src/provider/provider_os_memory_macosx.c index 87bae1b0e..33835beac 100644 --- a/src/provider/provider_os_memory_macosx.c +++ b/src/provider/provider_os_memory_macosx.c @@ -67,3 +67,11 @@ void *os_devdax_mmap(void *hint_addr, size_t length, int prot, int fd) { (void)fd; // unused return NULL; // not supported on Windows } + +int os_fallocate(int fd, long offset, long len) { + (void)fd; // unused + (void)offset; // unused + (void)len; // unused + + return -1; +} diff --git a/src/provider/provider_os_memory_posix.c b/src/provider/provider_os_memory_posix.c index 63f38cca3..90348ebfa 100644 --- a/src/provider/provider_os_memory_posix.c +++ b/src/provider/provider_os_memory_posix.c @@ -145,3 +145,36 @@ int os_devdax_open(const char *path) { return fd; } + +// open a file +int os_file_open(const char *path) { + if (!path) { + LOG_ERR("empty path"); + return -1; + } + + int fd = open(path, O_RDWR); + if (fd == -1) { + LOG_PERR("cannot open the file: %s", path); + } + + return fd; +} + +// open a file or create +int os_file_open_or_create(const char *path) { + if (!path) { + LOG_ERR("empty path"); + return -1; + } + + int fd = open(path, O_RDWR | O_CREAT, 0600); + if (fd == -1) { + LOG_PERR("cannot open/create the file: %s", path); + return -1; + } + + LOG_DEBUG("opened/created the file: %s", path); + + return fd; +} diff --git a/src/provider/provider_os_memory_windows.c b/src/provider/provider_os_memory_windows.c index f9232fb66..b295c75f1 100644 --- a/src/provider/provider_os_memory_windows.c +++ b/src/provider/provider_os_memory_windows.c @@ -166,3 +166,25 @@ int os_devdax_open(const char *path) { return -1; } + +// open a file +int os_file_open(const char *path) { + (void)path; // unused + + return -1; +} + +// open a file or create +int os_file_open_or_create(const char *path) { + (void)path; // unused + + return -1; +} + +int os_fallocate(int fd, long offset, long len) { + (void)fd; // unused + (void)offset; // unused + (void)len; // unused + + return -1; +} diff --git a/src/utils/utils_common.h b/src/utils/utils_common.h index 5a6fb87c0..20206b1e4 100644 --- a/src/utils/utils_common.h +++ b/src/utils/utils_common.h @@ -31,6 +31,8 @@ extern "C" { #define ALIGN_UP(value, align) (((value) + (align)-1) & ~((align)-1)) #define ALIGN_DOWN(value, align) ((value) & ~((align)-1)) +#define ASSERT_IS_ALIGNED(value, align) \ + DO_WHILE_EXPRS(assert(((value) & ((align)-1)) == 0)) #define VALGRIND_ANNOTATE_NEW_MEMORY(p, s) DO_WHILE_EMPTY #define VALGRIND_HG_DRD_DISABLE_CHECKING(p, s) DO_WHILE_EMPTY diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a72868c4f..f486b6e15 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -236,6 +236,10 @@ if(LINUX AND (NOT UMF_DISABLE_HWLOC)) # OS-specific functions are implemented NAME provider_devdax_memory SRCS provider_devdax_memory.cpp LIBS ${UMF_UTILS_FOR_TEST}) + add_umf_test( + NAME provider_file_memory + SRCS provider_file_memory.cpp + LIBS ${UMF_UTILS_FOR_TEST}) if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND UMF_BUILD_FUZZTESTS) add_subdirectory(fuzz) endif() @@ -370,7 +374,24 @@ if(LINUX) common/ipc_common.c common/ipc_os_prov_common.c) add_umf_ipc_test(TEST ipc_devdax_prov) + + build_umf_test( + NAME + ipc_file_prov_consumer + SRCS + ipc_file_prov_consumer.c + common/ipc_common.c + common/ipc_os_prov_common.c) + build_umf_test( + NAME + ipc_file_prov_producer + SRCS + ipc_file_prov_producer.c + common/ipc_common.c + common/ipc_os_prov_common.c) + add_umf_ipc_test(TEST ipc_file_prov) endif() + if(UMF_BUILD_GPU_TESTS AND UMF_BUILD_LEVEL_ZERO_PROVIDER) build_umf_test( NAME diff --git a/test/ipc_file_prov.sh b/test/ipc_file_prov.sh new file mode 100755 index 000000000..6807f4009 --- /dev/null +++ b/test/ipc_file_prov.sh @@ -0,0 +1,32 @@ +# +# 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 + +FILE_NAME="/tmp/umf_file_provider_$$" + +# port should be a number from the range <1024, 65535> +PORT=$(( 1024 + ( $$ % ( 65535 - 1024 )))) + +UMF_LOG_VAL="level:debug;flush:debug;output:stderr;pid:yes" + +# make sure the temp file does not exist +rm -f ${FILE_NAME} + +echo "Starting ipc_file_prov CONSUMER on port $PORT ..." +UMF_LOG=$UMF_LOG_VAL ./umf_test-ipc_file_prov_consumer $PORT $FILE_NAME & + +echo "Waiting 1 sec ..." +sleep 1 + +echo "Starting ipc_file_prov PRODUCER on port $PORT ..." +UMF_LOG=$UMF_LOG_VAL ./umf_test-ipc_file_prov_producer $PORT $FILE_NAME + +# remove the SHM file +rm -f ${FILE_NAME} diff --git a/test/ipc_file_prov_consumer.c b/test/ipc_file_prov_consumer.c new file mode 100644 index 000000000..834fe1054 --- /dev/null +++ b/test/ipc_file_prov_consumer.c @@ -0,0 +1,32 @@ +/* + * 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 "ipc_common.h" +#include "ipc_os_prov_common.h" + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + return -1; + } + + int port = atoi(argv[1]); + char *file_name = argv[2]; + + umf_file_memory_provider_params_t file_params; + + file_params = umfFileMemoryProviderParamsDefault(file_name); + file_params.visibility = UMF_MEM_MAP_SHARED; + + return run_consumer(port, umfFileMemoryProviderOps(), &file_params, memcopy, + NULL); +} diff --git a/test/ipc_file_prov_producer.c b/test/ipc_file_prov_producer.c new file mode 100644 index 000000000..783cff31d --- /dev/null +++ b/test/ipc_file_prov_producer.c @@ -0,0 +1,32 @@ +/* + * 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 "ipc_common.h" +#include "ipc_os_prov_common.h" + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + return -1; + } + + int port = atoi(argv[1]); + char *file_name = argv[2]; + + umf_file_memory_provider_params_t file_params; + + file_params = umfFileMemoryProviderParamsDefault(file_name); + file_params.visibility = UMF_MEM_MAP_SHARED; + + return run_producer(port, umfFileMemoryProviderOps(), &file_params, memcopy, + NULL); +} diff --git a/test/provider_devdax_memory.cpp b/test/provider_devdax_memory.cpp index 8b0f0360f..1fdd53b08 100644 --- a/test/provider_devdax_memory.cpp +++ b/test/provider_devdax_memory.cpp @@ -19,9 +19,6 @@ using umf_test::test; #define INVALID_PTR ((void *)0x01) -#define ASSERT_IS_ALIGNED(ptr, alignment) \ - ASSERT_EQ(((uintptr_t)ptr % alignment), 0) - typedef enum purge_t { PURGE_NONE = 0, PURGE_LAZY = 1, diff --git a/test/provider_file_memory.cpp b/test/provider_file_memory.cpp new file mode 100644 index 000000000..3903febe7 --- /dev/null +++ b/test/provider_file_memory.cpp @@ -0,0 +1,341 @@ +// 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 "base.hpp" + +#include "cpp_helpers.hpp" +#include "test_helpers.h" + +#include +#include + +using umf_test::test; + +#define FILE_PATH ((char *)"tmp_file") +#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_FILE_RESULT_SUCCESS + "memory allocation failed", // UMF_FILE_RESULT_ERROR_ALLOC_FAILED + "memory deallocation failed", // UMF_FILE_RESULT_ERROR_FREE_FAILED + "force purging failed", // UMF_FILE_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_FILE_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 umfProviderTest + : umf_test::test, + ::testing::WithParamInterface { + void SetUp() override { + test::SetUp(); + providerCreateExt(this->GetParam(), &provider); + 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 { test::TearDown(); } + + umf::provider_unique_handle_t provider; + size_t page_size; + size_t page_plus_64; +}; + +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_ERROR_NOT_SUPPORTED); +} + +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 + +umf_file_memory_provider_params_t get_file_params_shared(char *path) { + umf_file_memory_provider_params_t file_params = + umfFileMemoryProviderParamsDefault(path); + file_params.visibility = UMF_MEM_MAP_SHARED; + return file_params; +} + +umf_file_memory_provider_params_t file_params_shared = + get_file_params_shared(FILE_PATH); + +INSTANTIATE_TEST_SUITE_P(fileProviderTest, umfProviderTest, + ::testing::Values(providerCreateExtParams{ + umfFileMemoryProviderOps(), &file_params_shared})); + +TEST_P(umfProviderTest, create_destroy) {} + +TEST_P(umfProviderTest, alloc_page64_align_0) { + test_alloc_free_success(provider.get(), page_plus_64, 0, PURGE_NONE); +} + +TEST_P(umfProviderTest, alloc_page64_align_page_div_2) { + test_alloc_free_success(provider.get(), page_plus_64, page_size / 2, + PURGE_NONE); +} + +TEST_P(umfProviderTest, purge_lazy) { + test_alloc_free_success(provider.get(), page_plus_64, 0, PURGE_LAZY); +} + +TEST_P(umfProviderTest, purge_force) { + test_alloc_free_success(provider.get(), page_plus_64, 0, PURGE_FORCE); +} + +// negative tests using test_alloc_failure + +TEST_P(umfProviderTest, alloc_WRONG_SIZE) { + test_alloc_failure(provider.get(), -1, 0, UMF_RESULT_ERROR_INVALID_ARGUMENT, + 0); +} + +TEST_P(umfProviderTest, 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(umfProviderTest, alloc_3pages_WRONG_ALIGNMENT_3pages) { + test_alloc_failure(provider.get(), 3 * page_size, 3 * page_size, + UMF_RESULT_ERROR_INVALID_ALIGNMENT, 0); +} + +TEST_P(umfProviderTest, alloc_page64_align_page_minus_1_WRONG_ALIGNMENT_1) { + test_alloc_failure(provider.get(), page_plus_64, page_size - 1, + UMF_RESULT_ERROR_INVALID_ALIGNMENT, 0); +} + +TEST_P(umfProviderTest, 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(umfProviderTest, 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(umfProviderTest, 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(umfProviderTest, get_name) { + const char *name = umfMemoryProviderGetName(provider.get()); + ASSERT_STREQ(name, "FILE"); +} + +TEST_P(umfProviderTest, free_size_0_ptr_not_null) { + umf_result_t umf_result = + umfMemoryProviderFree(provider.get(), INVALID_PTR, 0); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); +} + +TEST_P(umfProviderTest, free_NULL) { + umf_result_t umf_result = umfMemoryProviderFree(provider.get(), nullptr, 0); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); +} + +// other negative tests + +TEST_F(test, create_empty_path) { + umf_memory_provider_handle_t hProvider = nullptr; + const char *path = ""; + auto wrong_params = umfFileMemoryProviderParamsDefault((char *)path); + auto ret = umfMemoryProviderCreate(umfFileMemoryProviderOps(), + &wrong_params, &hProvider); + EXPECT_EQ(ret, UMF_RESULT_ERROR_INVALID_ARGUMENT); + EXPECT_EQ(hProvider, nullptr); +} + +TEST_P(umfProviderTest, 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_NOT_SUPPORTED); +} + +TEST_P(umfProviderTest, 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(umfProviderTest, 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_FILE_RESULT_ERROR_PURGE_FORCE_FAILED); +} + +// IPC tests + +TEST_P(umfProviderTest, IPC_base_success_test) { + umf_result_t umf_result; + void *ptr = nullptr; + size_t size = page_size; + void *ipc_handle = nullptr; + size_t ipc_handle_size; + void *new_ptr = nullptr; + + umf_result = + umfMemoryProviderGetIPCHandleSize(provider.get(), &ipc_handle_size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ipc_handle_size, 0); + + umf_result = umfMemoryProviderAlloc(provider.get(), size, page_size, &ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr, nullptr); + memset(ptr, 0xFF, size); + + umf_result = + umfMemoryProviderAlloc(provider.get(), ipc_handle_size, 0, &ipc_handle); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ipc_handle, nullptr); + memset(ipc_handle, 0x0, ipc_handle_size); + + umf_result = + umfMemoryProviderGetIPCHandle(provider.get(), ptr, size, ipc_handle); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + umf_result = + umfMemoryProviderOpenIPCHandle(provider.get(), ipc_handle, &new_ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(new_ptr, nullptr); + + // it requires mapping with UMF_MEM_MAP_SHARED to work + int ret = memcmp(ptr, new_ptr, size); + ASSERT_EQ(ret, 0); + + umf_result = umfMemoryProviderFree(provider.get(), ptr, size); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); +} + +TEST_P(umfProviderTest, IPC_file_not_exist) { + umf_result_t umf_result; + void *ptr = nullptr; + size_t size = page_size; + void *ipc_handle = nullptr; + size_t ipc_handle_size; + void *new_ptr = nullptr; + + umf_result = + umfMemoryProviderGetIPCHandleSize(provider.get(), &ipc_handle_size); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ipc_handle_size, 0); + + umf_result = umfMemoryProviderAlloc(provider.get(), size, page_size, &ptr); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ptr, nullptr); + memset(ptr, 0xFF, size); + + umf_result = + umfMemoryProviderAlloc(provider.get(), ipc_handle_size, 0, &ipc_handle); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + ASSERT_NE(ipc_handle, nullptr); + memset(ipc_handle, 0x0, ipc_handle_size); + + umf_result = + umfMemoryProviderGetIPCHandle(provider.get(), ptr, size, ipc_handle); + ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS); + + int ret = unlink(FILE_PATH); + ASSERT_EQ(ret, 0); + + umf_result = + umfMemoryProviderOpenIPCHandle(provider.get(), ipc_handle, &new_ptr); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_INVALID_ARGUMENT); + ASSERT_EQ(new_ptr, nullptr); + + umf_result = umfMemoryProviderFree(provider.get(), ptr, size); + ASSERT_EQ(umf_result, UMF_RESULT_ERROR_NOT_SUPPORTED); +} diff --git a/test/provider_os_memory.cpp b/test/provider_os_memory.cpp index 29fae1441..fca494af8 100644 --- a/test/provider_os_memory.cpp +++ b/test/provider_os_memory.cpp @@ -6,6 +6,7 @@ #include "cpp_helpers.hpp" #include "ipcFixtures.hpp" +#include "test_helpers.h" #include #include @@ -15,9 +16,6 @@ using umf_test::test; #define INVALID_PTR ((void *)0x01) -#define ASSERT_IS_ALIGNED(ptr, alignment) \ - ASSERT_EQ(((uintptr_t)ptr % alignment), 0) - typedef enum purge_t { PURGE_NONE = 0, PURGE_LAZY = 1, diff --git a/test/test_valgrind.sh b/test/test_valgrind.sh index 4c8a5633a..bfe4275af 100755 --- a/test/test_valgrind.sh +++ b/test/test_valgrind.sh @@ -92,6 +92,10 @@ for test in $(ls -1 umf_test-*); do echo "- SKIPPED" continue; # skip testing helper binaries used by the ipc_devdax_prov_* tests ;; + umf_test-ipc_file_prov_*) + echo "- SKIPPED" + continue; # skip testing helper binaries used by the ipc_file_prov test + ;; umf_test-memspace_host_all) FILTER='--gtest_filter="-*allocsSpreadAcrossAllNumaNodes"' ;;