diff --git a/src/provider/provider_level_zero.c b/src/provider/provider_level_zero.c index e6619ff657..b7a5fde50f 100644 --- a/src/provider/provider_level_zero.c +++ b/src/provider/provider_level_zero.c @@ -285,11 +285,16 @@ static umf_result_t ze_memory_provider_allocation_split(void *provider, return UMF_RESULT_ERROR_NOT_SUPPORTED; } +typedef struct ze_ipc_data_t { + int pid; + ze_ipc_mem_handle_t ze_handle; +} ze_ipc_data_t; + static umf_result_t ze_memory_provider_get_ipc_handle_size(void *provider, size_t *size) { (void)provider; ASSERT(size != NULL); - *size = sizeof(ze_ipc_mem_handle_t); + *size = sizeof(ze_ipc_data_t); return UMF_RESULT_SUCCESS; } @@ -301,17 +306,19 @@ static umf_result_t ze_memory_provider_get_ipc_handle(void *provider, ASSERT(providerIpcData != NULL); (void)size; ze_result_t ze_result; - ze_ipc_mem_handle_t *ze_ipc_handle = (ze_ipc_mem_handle_t *)providerIpcData; + ze_ipc_data_t *ze_ipc_data = (ze_ipc_data_t *)providerIpcData; struct ze_memory_provider_t *ze_provider = (struct ze_memory_provider_t *)provider; - ze_result = - g_ze_ops.zeMemGetIpcHandle(ze_provider->context, ptr, ze_ipc_handle); + ze_result = g_ze_ops.zeMemGetIpcHandle(ze_provider->context, ptr, + &ze_ipc_data->ze_handle); if (ze_result != ZE_RESULT_SUCCESS) { LOG_ERR("zeMemGetIpcHandle() failed."); return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; } + ze_ipc_data->pid = utils_getpid(); + return UMF_RESULT_SUCCESS; } @@ -322,7 +329,7 @@ static umf_result_t ze_memory_provider_put_ipc_handle(void *provider, ze_result_t ze_result; struct ze_memory_provider_t *ze_provider = (struct ze_memory_provider_t *)provider; - ze_ipc_mem_handle_t *ze_ipc_handle = (ze_ipc_mem_handle_t *)providerIpcData; + ze_ipc_data_t *ze_ipc_data = (ze_ipc_data_t *)providerIpcData; if (g_ze_ops.zeMemPutIpcHandle == NULL) { // g_ze_ops.zeMemPutIpcHandle can be NULL because it was introduced @@ -331,8 +338,8 @@ static umf_result_t ze_memory_provider_put_ipc_handle(void *provider, return UMF_RESULT_SUCCESS; } - ze_result = - g_ze_ops.zeMemPutIpcHandle(ze_provider->context, *ze_ipc_handle); + ze_result = g_ze_ops.zeMemPutIpcHandle(ze_provider->context, + ze_ipc_data->ze_handle); if (ze_result != ZE_RESULT_SUCCESS) { LOG_ERR("zeMemPutIpcHandle() failed."); return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; @@ -347,12 +354,29 @@ static umf_result_t ze_memory_provider_open_ipc_handle(void *provider, ASSERT(providerIpcData != NULL); ASSERT(ptr != NULL); ze_result_t ze_result; - ze_ipc_mem_handle_t *ze_ipc_handle = (ze_ipc_mem_handle_t *)providerIpcData; + ze_ipc_data_t *ze_ipc_data = (ze_ipc_data_t *)providerIpcData; struct ze_memory_provider_t *ze_provider = (struct ze_memory_provider_t *)provider; + int fd_local = -1; + ze_ipc_mem_handle_t ze_ipc_handle = ze_ipc_data->ze_handle; + + if (ze_ipc_data->pid != utils_getpid()) { + int fd_remote = -1; + memcpy(&fd_remote, &ze_ipc_handle, sizeof(fd_remote)); + umf_result_t umf_result = + utils_duplicate_fd(ze_ipc_data->pid, fd_remote, &fd_local); + if (umf_result != UMF_RESULT_SUCCESS) { + LOG_PERR("duplicating file descriptor failed"); + return umf_result; + } + memcpy(&ze_ipc_handle, &fd_local, sizeof(fd_local)); + } ze_result = g_ze_ops.zeMemOpenIpcHandle( - ze_provider->context, ze_provider->device, *ze_ipc_handle, 0, ptr); + ze_provider->context, ze_provider->device, ze_ipc_handle, 0, ptr); + if (fd_local != -1) { + (void)utils_close_fd(fd_local); + } if (ze_result != ZE_RESULT_SUCCESS) { LOG_ERR("zeMemOpenIpcHandle() failed."); return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; diff --git a/src/provider/provider_os_memory.c b/src/provider/provider_os_memory.c index dc29bb8440..6b71caac03 100644 --- a/src/provider/provider_os_memory.c +++ b/src/provider/provider_os_memory.c @@ -325,7 +325,7 @@ create_fd_for_mmap(umf_os_memory_provider_params_t *in_params, err_close_file: if (provider->fd > 0) { - (void)os_close_fd(provider->fd); + (void)utils_close_fd(provider->fd); } return result; @@ -1247,7 +1247,7 @@ static umf_result_t os_open_ipc_handle(void *provider, void *providerIpcData, (void)os_shm_unlink(os_provider->shm_name); } else { umf_result_t umf_result = - os_duplicate_fd(os_ipc_data->pid, os_ipc_data->fd, &fd); + utils_duplicate_fd(os_ipc_data->pid, os_ipc_data->fd, &fd); if (umf_result != UMF_RESULT_SUCCESS) { LOG_PERR("duplicating file descriptor failed"); return umf_result; @@ -1262,7 +1262,7 @@ static umf_result_t os_open_ipc_handle(void *provider, void *providerIpcData, ret = UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC; } - (void)os_close_fd(fd); + (void)utils_close_fd(fd); return ret; } diff --git a/src/provider/provider_os_memory_internal.h b/src/provider/provider_os_memory_internal.h index 7af24c368e..68750c6d1e 100644 --- a/src/provider/provider_os_memory_internal.h +++ b/src/provider/provider_os_memory_internal.h @@ -97,10 +97,6 @@ size_t os_get_page_size(void); void os_strerror(int errnum, char *buf, size_t buflen); -umf_result_t os_duplicate_fd(int pid, int fd_in, int *fd_out); - -umf_result_t os_close_fd(int fd); - #ifdef __cplusplus } #endif diff --git a/src/provider/provider_os_memory_posix.c b/src/provider/provider_os_memory_posix.c index 59ae9a94c5..526ae3e653 100644 --- a/src/provider/provider_os_memory_posix.c +++ b/src/provider/provider_os_memory_posix.c @@ -89,47 +89,3 @@ int os_purge(void *addr, size_t length, int advice) { void os_strerror(int errnum, char *buf, size_t buflen) { strerror_r(errnum, buf, buflen); } - -umf_result_t os_duplicate_fd(int pid, int fd_in, int *fd_out) { -// pidfd_getfd(2) is used to obtain a duplicate of another process's file descriptor. -// Permission to duplicate another process's file descriptor -// is governed by a ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check (see ptrace(2)) -// that can be changed using the /proc/sys/kernel/yama/ptrace_scope interface. -// pidfd_getfd(2) is supported since Linux 5.6 -// pidfd_open(2) is supported since Linux 5.3 -#if defined(__NR_pidfd_open) && defined(__NR_pidfd_getfd) - errno = 0; - int pid_fd = syscall(SYS_pidfd_open, pid, 0); - if (pid_fd == -1) { - LOG_PDEBUG("SYS_pidfd_open"); - return UMF_RESULT_ERROR_UNKNOWN; - } - - int fd_dup = syscall(SYS_pidfd_getfd, pid_fd, fd_in, 0); - close(pid_fd); - if (fd_dup == -1) { - LOG_PDEBUG("SYS_pidfd_getfd"); - return UMF_RESULT_ERROR_UNKNOWN; - } - - *fd_out = fd_dup; - - return UMF_RESULT_SUCCESS; -#else - // TODO: find another way to obtain a duplicate of another process's file descriptor - (void)pid; // unused - (void)fd_in; // unused - (void)fd_out; // unused - errno = ENOTSUP; - return UMF_RESULT_ERROR_NOT_SUPPORTED; // unsupported -#endif /* defined(__NR_pidfd_open) && defined(__NR_pidfd_getfd) */ -} - -umf_result_t os_close_fd(int fd) { - if (close(fd)) { - LOG_PERR("close() failed"); - return UMF_RESULT_ERROR_UNKNOWN; - } - - return UMF_RESULT_SUCCESS; -} diff --git a/src/provider/provider_os_memory_windows.c b/src/provider/provider_os_memory_windows.c index f5d80ad808..994f4d53c3 100644 --- a/src/provider/provider_os_memory_windows.c +++ b/src/provider/provider_os_memory_windows.c @@ -151,15 +151,3 @@ size_t os_get_page_size(void) { void os_strerror(int errnum, char *buf, size_t buflen) { strerror_s(buf, buflen, errnum); } - -umf_result_t os_duplicate_fd(int pid, int fd_in, int *fd_out) { - (void)pid; // unused - (void)fd_in; // unused - (void)fd_out; // unused - return UMF_RESULT_ERROR_NOT_SUPPORTED; // unsupported -} - -umf_result_t os_close_fd(int fd) { - (void)fd; // unused - return UMF_RESULT_ERROR_NOT_SUPPORTED; // unsupported -} diff --git a/src/utils/utils_common.h b/src/utils/utils_common.h index fd6b7b2b66..28579eb009 100644 --- a/src/utils/utils_common.h +++ b/src/utils/utils_common.h @@ -14,6 +14,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -77,6 +79,12 @@ int utils_getpid(void); // get the current thread ID int utils_gettid(void); +// close file descriptor +int utils_close_fd(int fd); + +// obtain a duplicate of another process's file descriptor +umf_result_t utils_duplicate_fd(int pid, int fd_in, int *fd_out); + #ifdef __cplusplus } #endif diff --git a/src/utils/utils_posix_common.c b/src/utils/utils_posix_common.c index a331f96571..51049e6130 100644 --- a/src/utils/utils_posix_common.c +++ b/src/utils/utils_posix_common.c @@ -7,12 +7,22 @@ * */ +#include #include #include #include #include +#include "utils_common.h" #include "utils_concurrency.h" +#include "utils_log.h" + +#ifndef __NR_pidfd_open +#define __NR_pidfd_open 434 /* Syscall id */ +#endif +#ifndef __NR_pidfd_getfd +#define __NR_pidfd_getfd 438 /* Syscall id */ +#endif static UTIL_ONCE_FLAG Page_size_is_initialized = UTIL_ONCE_FLAG_INIT; static size_t Page_size; @@ -38,3 +48,59 @@ int utils_gettid(void) { return syscall(SYS_gettid); #endif } + +int utils_close_fd(int fd) { return close(fd); } + +#ifndef __APPLE__ +static umf_result_t errno_to_umf_result(int err) { + switch (err) { + case EBADF: + case EINVAL: + case ESRCH: + case EPERM: + return UMF_RESULT_ERROR_INVALID_ARGUMENT; + case EMFILE: + case ENOMEM: + return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY; + case ENODEV: + case ENOSYS: + case ENOTSUP: + return UMF_RESULT_ERROR_NOT_SUPPORTED; + default: + return UMF_RESULT_ERROR_UNKNOWN; + } +} +#endif + +umf_result_t utils_duplicate_fd(int pid, int fd_in, int *fd_out) { +#ifdef __APPLE__ + (void)pid; // unused + (void)fd_in; // unused + (void)fd_out; // unused + return UMF_RESULT_ERROR_NOT_SUPPORTED; +#else + // pidfd_getfd(2) is used to obtain a duplicate of another process's file descriptor. + // Permission to duplicate another process's file descriptor + // is governed by a ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check (see ptrace(2)) + // that can be changed using the /proc/sys/kernel/yama/ptrace_scope interface. + // pidfd_getfd(2) is supported since Linux 5.6 + // pidfd_open(2) is supported since Linux 5.3 + errno = 0; + int pid_fd = syscall(__NR_pidfd_open, pid, 0); + if (pid_fd == -1) { + LOG_PERR("__NR_pidfd_open"); + return errno_to_umf_result(errno); + } + + int fd_dup = syscall(__NR_pidfd_getfd, pid_fd, fd_in, 0); + close(pid_fd); + if (fd_dup == -1) { + LOG_PERR("__NR_pidfd_open"); + return errno_to_umf_result(errno); + } + + *fd_out = fd_dup; + + return UMF_RESULT_SUCCESS; +#endif +} diff --git a/src/utils/utils_windows_common.c b/src/utils/utils_windows_common.c index 272535918b..9358891ad6 100644 --- a/src/utils/utils_windows_common.c +++ b/src/utils/utils_windows_common.c @@ -12,6 +12,7 @@ #include #include +#include "utils_common.h" #include "utils_concurrency.h" #define BUFFER_SIZE 1024 @@ -33,3 +34,15 @@ size_t util_get_page_size(void) { int utils_getpid(void) { return GetCurrentProcessId(); } int utils_gettid(void) { return GetCurrentThreadId(); } + +int utils_close_fd(int fd) { + (void)fd; // unused + return -1; +} + +umf_result_t utils_duplicate_fd(int pid, int fd_in, int *fd_out) { + (void)pid; // unused + (void)fd_in; // unused + (void)fd_out; // unused + return UMF_RESULT_ERROR_NOT_SUPPORTED; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index db21a99a10..dcb581c711 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -219,14 +219,14 @@ if(UMF_BUILD_GPU_TESTS AND UMF_BUILD_LEVEL_ZERO_PROVIDER) # dlopen) add_umf_test( NAME provider_level_zero - SRCS providers/provider_level_zero.cpp + SRCS providers/provider_level_zero.cpp providers/level_zero_helpers.cpp LIBS ${UMF_UTILS_FOR_TEST} ze_loader) target_include_directories(umf_test-provider_level_zero PRIVATE ${LEVEL_ZERO_INCLUDE_DIRS}) add_umf_test( NAME provider_level_zero_dlopen - SRCS providers/provider_level_zero.cpp + SRCS providers/provider_level_zero.cpp providers/level_zero_helpers.cpp LIBS ${UMF_UTILS_FOR_TEST}) target_compile_definitions(umf_test-provider_level_zero_dlopen PUBLIC USE_DLOPEN=1) @@ -322,6 +322,35 @@ if(LINUX) common/ipc_os_prov_common.c) add_umf_ipc_test(TEST ipc_os_prov_anon_fd) add_umf_ipc_test(TEST ipc_os_prov_shm) + if(UMF_BUILD_GPU_TESTS AND UMF_BUILD_LEVEL_ZERO_PROVIDER) + build_umf_test( + NAME + ipc_level_zero_prov_consumer + SRCS + providers/ipc_level_zero_prov_consumer.c + common/ipc_common.c + providers/ipc_level_zero_prov_common.c + providers/level_zero_helpers.cpp + LIBS + ze_loader + ${UMF_UTILS_FOR_TEST}) + build_umf_test( + NAME + ipc_level_zero_prov_producer + SRCS + providers/ipc_level_zero_prov_producer.c + common/ipc_common.c + providers/ipc_level_zero_prov_common.c + providers/level_zero_helpers.cpp + LIBS + ze_loader + ${UMF_UTILS_FOR_TEST}) + target_include_directories(umf_test-ipc_level_zero_prov_producer + PRIVATE ${LEVEL_ZERO_INCLUDE_DIRS}) + target_include_directories(umf_test-ipc_level_zero_prov_consumer + PRIVATE ${LEVEL_ZERO_INCLUDE_DIRS}) + add_umf_ipc_test(TEST ipc_level_zero_prov SRC_DIR providers) + endif() else() message(STATUS "IPC tests are supported on Linux only - skipping") endif() diff --git a/test/providers/ipc_level_zero_prov.sh b/test/providers/ipc_level_zero_prov.sh new file mode 100755 index 0000000000..d6bcef4f3a --- /dev/null +++ b/test/providers/ipc_level_zero_prov.sh @@ -0,0 +1,39 @@ +# +# 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 + +# port should be a number from the range <1024, 65535> +PORT=$(( 1024 + ( $$ % ( 65535 - 1024 )))) + +# The ipc_level_zero_prov test requires using pidfd_getfd(2) +# to obtain a duplicate of another process's file descriptor. +# Permission to duplicate another process's file descriptor +# is governed by a ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check (see ptrace(2)) +# that can be changed using the /proc/sys/kernel/yama/ptrace_scope interface. +PTRACE_SCOPE_FILE="/proc/sys/kernel/yama/ptrace_scope" +VAL=0 +if [ -f $PTRACE_SCOPE_FILE ]; then + PTRACE_SCOPE_VAL=$(cat $PTRACE_SCOPE_FILE) + if [ $PTRACE_SCOPE_VAL -ne $VAL ]; then + echo "SKIP: ptrace_scope is not set to 0 (classic ptrace permissions) - skipping the test" + exit 125 # skip code defined in CMakeLists.txt + fi +fi + +UMF_LOG_VAL="level:debug;flush:debug;output:stderr;pid:yes" + +echo "Starting ipc_level_zero_prov CONSUMER on port $PORT ..." +UMF_LOG=$UMF_LOG_VAL ./umf_test-ipc_level_zero_prov_consumer $PORT & + +echo "Waiting 1 sec ..." +sleep 1 + +echo "Starting ipc_level_zero_prov PRODUCER on port $PORT ..." +UMF_LOG=$UMF_LOG_VAL ./umf_test-ipc_level_zero_prov_producer $PORT diff --git a/test/providers/ipc_level_zero_prov_common.c b/test/providers/ipc_level_zero_prov_common.c new file mode 100644 index 0000000000..11813653de --- /dev/null +++ b/test/providers/ipc_level_zero_prov_common.c @@ -0,0 +1,24 @@ +/* + * 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 "ipc_level_zero_prov_common.h" +#include "level_zero_helpers.h" + +#include + +#include + +void memcopy(void *dst, const void *src, size_t size, void *context) { + level_zero_memory_provider_params_t *l0_params = + (level_zero_memory_provider_params_t *)context; + int ret = + level_zero_copy(l0_params->level_zero_context_handle, + l0_params->level_zero_device_handle, dst, src, size); + if (ret != 0) { + fprintf(stderr, "level_zero_copy failed with error %d\n", ret); + } +} diff --git a/test/providers/ipc_level_zero_prov_common.h b/test/providers/ipc_level_zero_prov_common.h new file mode 100644 index 0000000000..fdfdeba6de --- /dev/null +++ b/test/providers/ipc_level_zero_prov_common.h @@ -0,0 +1,15 @@ +/* + * 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 IPC_LEVEL_ZERO_PROV_COMMON_H +#define IPC_LEVEL_ZERO_PROV_COMMON_H + +#include + +void memcopy(void *dst, const void *src, size_t size, void *context); + +#endif // IPC_LEVEL_ZERO_PROV_COMMON_H diff --git a/test/providers/ipc_level_zero_prov_consumer.c b/test/providers/ipc_level_zero_prov_consumer.c new file mode 100644 index 0000000000..6d59ced537 --- /dev/null +++ b/test/providers/ipc_level_zero_prov_consumer.c @@ -0,0 +1,30 @@ +/* + * 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_level_zero_prov_common.h" +#include "level_zero_helpers.h" + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "usage: %s [shm_name]\n", argv[0]); + return -1; + } + + int port = atoi(argv[1]); + + level_zero_memory_provider_params_t l0_params = + create_level_zero_prov_params(UMF_MEMORY_TYPE_DEVICE); + + return run_consumer(port, umfLevelZeroMemoryProviderOps(), &l0_params, + memcopy, &l0_params); +} diff --git a/test/providers/ipc_level_zero_prov_producer.c b/test/providers/ipc_level_zero_prov_producer.c new file mode 100644 index 0000000000..d2d95d8850 --- /dev/null +++ b/test/providers/ipc_level_zero_prov_producer.c @@ -0,0 +1,30 @@ +/* + * 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_level_zero_prov_common.h" +#include "level_zero_helpers.h" + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "usage: %s [shm_name]\n", argv[0]); + return -1; + } + + int port = atoi(argv[1]); + + level_zero_memory_provider_params_t l0_params = + create_level_zero_prov_params(UMF_MEMORY_TYPE_DEVICE); + + return run_producer(port, umfLevelZeroMemoryProviderOps(), &l0_params, + memcopy, &l0_params); +} diff --git a/test/providers/level_zero_helpers.cpp b/test/providers/level_zero_helpers.cpp new file mode 100644 index 0000000000..067bf6a15d --- /dev/null +++ b/test/providers/level_zero_helpers.cpp @@ -0,0 +1,708 @@ +/* + * 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 "level_zero_helpers.h" + +#include +#include + +#include "utils_concurrency.h" +#include "utils_load_library.h" + +#include "ze_api.h" + +struct libze_ops { + ze_result_t (*zeInit)(ze_init_flags_t flags); + ze_result_t (*zeDriverGet)(uint32_t *pCount, ze_driver_handle_t *phDrivers); + ze_result_t (*zeDeviceGet)(ze_driver_handle_t hDriver, uint32_t *pCount, + ze_device_handle_t *phDevices); + ze_result_t (*zeDeviceGetProperties)( + ze_device_handle_t hDevice, ze_device_properties_t *pDeviceProperties); + + ze_result_t (*zeContextCreate)(ze_driver_handle_t hDriver, + const ze_context_desc_t *desc, + ze_context_handle_t *phContext); + ze_result_t (*zeContextDestroy)(ze_context_handle_t hContext); + ze_result_t (*zeCommandQueueCreate)( + ze_context_handle_t hContext, ze_device_handle_t hDevice, + const ze_command_queue_desc_t *desc, + ze_command_queue_handle_t *phCommandQueue); + ze_result_t (*zeCommandQueueDestroy)( + ze_command_queue_handle_t hCommandQueue); + ze_result_t (*zeCommandQueueExecuteCommandLists)( + ze_command_queue_handle_t hCommandQueue, uint32_t numCommandLists, + ze_command_list_handle_t *phCommandLists, ze_fence_handle_t hFence); + ze_result_t (*zeCommandQueueSynchronize)( + ze_command_queue_handle_t hCommandQueue, uint64_t timeout); + ze_result_t (*zeCommandListCreate)(ze_context_handle_t hContext, + ze_device_handle_t hDevice, + const ze_command_list_desc_t *desc, + ze_command_list_handle_t *phCommandList); + ze_result_t (*zeCommandListDestroy)(ze_command_list_handle_t hCommandList); + ze_result_t (*zeCommandListClose)(ze_command_list_handle_t hCommandList); + ze_result_t (*zeCommandListAppendMemoryCopy)( + ze_command_list_handle_t hCommandList, void *dstptr, const void *srcptr, + size_t size, ze_event_handle_t hSignalEvent, uint32_t numWaitEvents, + ze_event_handle_t *phWaitEvents); + ze_result_t (*zeCommandListAppendMemoryFill)( + ze_command_list_handle_t hCommandList, void *ptr, const void *pattern, + size_t pattern_size, size_t size, ze_event_handle_t hSignalEvent, + uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents); + ze_result_t (*zeMemGetAllocProperties)( + ze_context_handle_t hContext, const void *ptr, + ze_memory_allocation_properties_t *pMemAllocProperties, + ze_device_handle_t *phDevice); + ze_result_t (*zeMemAllocDevice)(ze_context_handle_t, + const ze_device_mem_alloc_desc_t *, size_t, + size_t, ze_device_handle_t, void **); + ze_result_t (*zeMemFree)(ze_context_handle_t, void *); +} libze_ops; + +#if USE_DLOPEN +struct DlHandleCloser { + void operator()(void *dlHandle) { + if (dlHandle) { + util_close_library(dlHandle); + } + } +}; + +std::unique_ptr zeDlHandle = nullptr; +int InitLevelZeroOps() { +#ifdef _WIN32 + const char *lib_name = "ze_loader.dll"; +#else + const char *lib_name = "libze_loader.so"; +#endif + // Load Level Zero symbols + // NOTE that we use UMF_UTIL_OPEN_LIBRARY_GLOBAL which add all loaded symbols to the + // global symbol table. + zeDlHandle = std::unique_ptr( + util_open_library(lib_name, UMF_UTIL_OPEN_LIBRARY_GLOBAL)); + *(void **)&libze_ops.zeInit = + util_get_symbol_addr(zeDlHandle.get(), "zeInit", lib_name); + if (libze_ops.zeInit == nullptr) { + fprintf(stderr, "zeInit symbol not found in %s\n", lib_name); + return -1; + } + *(void **)&libze_ops.zeDriverGet = + util_get_symbol_addr(zeDlHandle.get(), "zeDriverGet", lib_name); + if (libze_ops.zeDriverGet == nullptr) { + fprintf(stderr, "zeDriverGet symbol not found in %s\n", lib_name); + return -1; + } + *(void **)&libze_ops.zeDeviceGet = + util_get_symbol_addr(zeDlHandle.get(), "zeDeviceGet", lib_name); + if (libze_ops.zeDeviceGet == nullptr) { + fprintf(stderr, "zeDeviceGet symbol not found in %s\n", lib_name); + return -1; + } + *(void **)&libze_ops.zeDeviceGetProperties = util_get_symbol_addr( + zeDlHandle.get(), "zeDeviceGetProperties", lib_name); + if (libze_ops.zeDeviceGetProperties == nullptr) { + fprintf(stderr, "zeDeviceGetProperties symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeContextCreate = + util_get_symbol_addr(zeDlHandle.get(), "zeContextCreate", lib_name); + if (libze_ops.zeContextCreate == nullptr) { + fprintf(stderr, "zeContextCreate symbol not found in %s\n", lib_name); + return -1; + } + *(void **)&libze_ops.zeContextDestroy = + util_get_symbol_addr(zeDlHandle.get(), "zeContextDestroy", lib_name); + if (libze_ops.zeContextDestroy == nullptr) { + fprintf(stderr, "zeContextDestroy symbol not found in %s\n", lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandQueueCreate = util_get_symbol_addr( + zeDlHandle.get(), "zeCommandQueueCreate", lib_name); + if (libze_ops.zeCommandQueueCreate == nullptr) { + fprintf(stderr, "zeCommandQueueCreate symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandQueueDestroy = util_get_symbol_addr( + zeDlHandle.get(), "zeCommandQueueDestroy", lib_name); + if (libze_ops.zeCommandQueueDestroy == nullptr) { + fprintf(stderr, "zeCommandQueueDestroy symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandQueueExecuteCommandLists = + util_get_symbol_addr(zeDlHandle.get(), + "zeCommandQueueExecuteCommandLists", lib_name); + if (libze_ops.zeCommandQueueExecuteCommandLists == nullptr) { + fprintf(stderr, + "zeCommandQueueExecuteCommandLists symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandQueueSynchronize = util_get_symbol_addr( + zeDlHandle.get(), "zeCommandQueueSynchronize", lib_name); + if (libze_ops.zeCommandQueueSynchronize == nullptr) { + fprintf(stderr, "zeCommandQueueSynchronize symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandListCreate = + util_get_symbol_addr(zeDlHandle.get(), "zeCommandListCreate", lib_name); + if (libze_ops.zeCommandListCreate == nullptr) { + fprintf(stderr, "zeCommandListCreate symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandListDestroy = util_get_symbol_addr( + zeDlHandle.get(), "zeCommandListDestroy", lib_name); + if (libze_ops.zeCommandListDestroy == nullptr) { + fprintf(stderr, "zeCommandListDestroy symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandListClose = + util_get_symbol_addr(zeDlHandle.get(), "zeCommandListClose", lib_name); + if (libze_ops.zeCommandListClose == nullptr) { + fprintf(stderr, "zeCommandListClose symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandListAppendMemoryCopy = util_get_symbol_addr( + zeDlHandle.get(), "zeCommandListAppendMemoryCopy", lib_name); + if (libze_ops.zeCommandListAppendMemoryCopy == nullptr) { + fprintf(stderr, + "zeCommandListAppendMemoryCopy symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeCommandListAppendMemoryFill = util_get_symbol_addr( + zeDlHandle.get(), "zeCommandListAppendMemoryFill", lib_name); + if (libze_ops.zeCommandListAppendMemoryFill == nullptr) { + fprintf(stderr, + "zeCommandListAppendMemoryFill symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeMemGetAllocProperties = util_get_symbol_addr( + zeDlHandle.get(), "zeMemGetAllocProperties", lib_name); + if (libze_ops.zeMemGetAllocProperties == nullptr) { + fprintf(stderr, "zeMemGetAllocProperties symbol not found in %s\n", + lib_name); + return -1; + } + *(void **)&libze_ops.zeMemAllocDevice = + util_get_symbol_addr(zeDlHandle.get(), "zeMemAllocDevice", lib_name); + if (libze_ops.zeMemAllocDevice == nullptr) { + fprintf(stderr, "zeMemAllocDevice symbol not found in %s\n", lib_name); + return -1; + } + *(void **)&libze_ops.zeMemFree = + util_get_symbol_addr(zeDlHandle.get(), "zeMemFree", lib_name); + if (libze_ops.zeMemFree == nullptr) { + fprintf(stderr, "zeMemFree symbol not found in %s\n", lib_name); + return -1; + } + + return 0; +} + +#else // USE_DLOPEN +int InitLevelZeroOps() { + // Level Zero is linked statically but we prepare ops structure to + // make test code consistent + libze_ops.zeInit = zeInit; + libze_ops.zeDriverGet = zeDriverGet; + libze_ops.zeDeviceGet = zeDeviceGet; + libze_ops.zeDeviceGetProperties = zeDeviceGetProperties; + libze_ops.zeContextCreate = zeContextCreate; + libze_ops.zeContextDestroy = zeContextDestroy; + libze_ops.zeCommandQueueCreate = zeCommandQueueCreate; + libze_ops.zeCommandQueueDestroy = zeCommandQueueDestroy; + libze_ops.zeCommandQueueExecuteCommandLists = + zeCommandQueueExecuteCommandLists; + libze_ops.zeCommandQueueSynchronize = zeCommandQueueSynchronize; + libze_ops.zeCommandListCreate = zeCommandListCreate; + libze_ops.zeCommandListDestroy = zeCommandListDestroy; + libze_ops.zeCommandListClose = zeCommandListClose; + libze_ops.zeCommandListAppendMemoryCopy = zeCommandListAppendMemoryCopy; + libze_ops.zeCommandListAppendMemoryFill = zeCommandListAppendMemoryFill; + libze_ops.zeMemGetAllocProperties = zeMemGetAllocProperties; + libze_ops.zeMemAllocDevice = zeMemAllocDevice; + libze_ops.zeMemFree = zeMemFree; + + return 0; +} +#endif // USE_DLOPEN + +static int init_level_zero_lib(void) { + ze_init_flag_t flags = ZE_INIT_FLAG_GPU_ONLY; + ze_result_t result = libze_ops.zeInit(flags); + if (result != ZE_RESULT_SUCCESS) { + return -1; + } + return 0; +} + +int get_drivers(uint32_t *drivers_num_, ze_driver_handle_t **drivers_) { + int ret = 0; + ze_result_t ze_result; + ze_driver_handle_t *drivers = NULL; + uint32_t drivers_num = 0; + + ze_result = libze_ops.zeDriverGet(&drivers_num, NULL); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeDriverGet() failed!\n"); + ret = -1; + goto fn_fail; + } + if (drivers_num == 0) { + goto fn_exit; + } + + drivers = + (ze_driver_handle_t *)malloc(drivers_num * sizeof(ze_driver_handle_t)); + if (!drivers) { + ret = -1; + goto fn_fail; + } + + ze_result = libze_ops.zeDriverGet(&drivers_num, drivers); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeDriverGet() failed!\n"); + ret = -1; + goto fn_fail; + } + +fn_exit: + *drivers_num_ = drivers_num; + *drivers_ = drivers; + return ret; + +fn_fail: + *drivers_num_ = 0; + if (drivers) { + free(drivers); + *drivers_ = NULL; + } + return ret; +} + +int get_devices(ze_driver_handle_t driver, uint32_t *devices_num_, + ze_device_handle_t **devices_) { + ze_result_t ze_result; + int ret = 0; + uint32_t devices_num = 0; + ze_device_handle_t *devices = NULL; + + ze_result = libze_ops.zeDeviceGet(driver, &devices_num, NULL); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeDeviceGet() failed!\n"); + ret = -1; + goto fn_fail; + } + if (devices_num == 0) { + goto fn_exit; + } + + devices = + (ze_device_handle_t *)malloc(devices_num * sizeof(ze_device_handle_t)); + if (!devices) { + ret = -1; + goto fn_fail; + } + + ze_result = libze_ops.zeDeviceGet(driver, &devices_num, devices); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeDeviceGet() failed!\n"); + ret = -1; + goto fn_fail; + } + +fn_exit: + *devices_num_ = devices_num; + *devices_ = devices; + return ret; + +fn_fail: + devices_num = 0; + if (devices) { + free(devices); + devices = NULL; + } + return ret; +} + +int find_driver_with_gpu(uint32_t *driver_idx, ze_driver_handle_t *driver_) { + int ret = 0; + ze_result_t ze_result; + uint32_t drivers_num = 0; + ze_device_handle_t *devices = NULL; + ze_driver_handle_t *drivers = NULL; + ze_driver_handle_t driver_with_gpus = NULL; + + ret = get_drivers(&drivers_num, &drivers); + if (ret) { + goto fn_fail; + } + + /* Find a driver with GPU */ + for (uint32_t i = 0; i < drivers_num; ++i) { + uint32_t devices_num = 0; + ze_driver_handle_t driver = drivers[i]; + + ret = get_devices(driver, &devices_num, &devices); + if (ret) { + goto fn_fail; + } + + for (uint32_t d = 0; d < devices_num; ++d) { + ze_device_handle_t device = devices[d]; + ze_device_properties_t device_properties; + device_properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES; + device_properties.pNext = NULL; + + ze_result = + libze_ops.zeDeviceGetProperties(device, &device_properties); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeDeviceGetProperties() failed!\n"); + ret = -1; + goto fn_fail; + } + + if (device_properties.type == ZE_DEVICE_TYPE_GPU) { + driver_with_gpus = driver; + *driver_idx = i; + break; + } + } + + if (devices) { + free(devices); + devices = NULL; + } + + if (driver_with_gpus != NULL) { + goto fn_exit; + } + } + +fn_fail: + if (devices) { + free(devices); + } + +fn_exit: + *driver_ = driver_with_gpus; + if (drivers) { + free(drivers); + } + return ret; +} + +int find_gpu_device(ze_driver_handle_t driver, ze_device_handle_t *device_) { + int ret = -1; + uint32_t devices_num = 0; + ze_device_handle_t *devices = NULL; + ze_device_handle_t device; + + ret = get_devices(driver, &devices_num, &devices); + if (ret) { + return ret; + } + + for (uint32_t d = 0; d < devices_num; ++d) { + device = devices[d]; + ze_device_properties_t device_properties; + device_properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES; + device_properties.pNext = NULL; + + ze_result_t ze_result = + libze_ops.zeDeviceGetProperties(device, &device_properties); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeDeviceGetProperties() failed!\n"); + ret = -1; + break; + } + + if (device_properties.type == ZE_DEVICE_TYPE_GPU) { + *device_ = device; + ret = 0; + break; + } + } + + if (devices) { + free(devices); + } + return ret; +} + +int level_zero_fill(ze_context_handle_t context, ze_device_handle_t device, + void *ptr, size_t size, const void *pattern, + size_t pattern_size) { + int ret = 0; + + ze_command_queue_desc_t commandQueueDesc = { + ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC, + NULL, + 0, + 0, + 0, + ZE_COMMAND_QUEUE_MODE_DEFAULT, + ZE_COMMAND_QUEUE_PRIORITY_NORMAL}; + + ze_command_list_desc_t commandListDesc = { + ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC, 0, 0, + ZE_COMMAND_LIST_FLAG_RELAXED_ORDERING}; + + ze_command_queue_handle_t hCommandQueue; + ze_result_t ze_result = libze_ops.zeCommandQueueCreate( + context, device, &commandQueueDesc, &hCommandQueue); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueCreate() failed!\n"); + return -1; + } + + ze_command_list_handle_t hCommandList; + ze_result = libze_ops.zeCommandListCreate(context, device, &commandListDesc, + &hCommandList); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListCreate() failed!\n"); + ret = -1; + goto err_queue_destroy; + } + + // fill memory with a pattern + ze_result = libze_ops.zeCommandListAppendMemoryFill( + hCommandList, ptr, pattern, pattern_size, size, NULL, 0, NULL); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListAppendMemoryFill() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + // close and execute the command list + ze_result = libze_ops.zeCommandListClose(hCommandList); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListClose() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + ze_result = libze_ops.zeCommandQueueExecuteCommandLists( + hCommandQueue, 1, &hCommandList, NULL); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueExecuteCommandLists() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + // sync + ze_result = libze_ops.zeCommandQueueSynchronize(hCommandQueue, UINT64_MAX); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueSynchronize() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + // cleanup +err_list_destroy: + ze_result = libze_ops.zeCommandListDestroy(hCommandList); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListDestroy() failed!\n"); + ret = -1; + } + +err_queue_destroy: + ze_result = libze_ops.zeCommandQueueDestroy(hCommandQueue); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueDestroy() failed!\n"); + ret = -1; + } + + return ret; +} + +int level_zero_copy(ze_context_handle_t context, ze_device_handle_t device, + void *dst_ptr, const void *src_ptr, size_t size) { + int ret = 0; + ze_command_queue_desc_t commandQueueDesc = { + ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC, + NULL, + 0, + 0, + 0, + ZE_COMMAND_QUEUE_MODE_DEFAULT, + ZE_COMMAND_QUEUE_PRIORITY_NORMAL}; + + ze_command_list_desc_t commandListDesc = { + ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC, 0, 0, + ZE_COMMAND_LIST_FLAG_RELAXED_ORDERING}; + + ze_command_queue_handle_t hCommandQueue; + ze_result_t ze_result = libze_ops.zeCommandQueueCreate( + context, device, &commandQueueDesc, &hCommandQueue); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueCreate() failed!\n"); + return -1; + } + + ze_command_list_handle_t hCommandList; + ze_result = libze_ops.zeCommandListCreate(context, device, &commandListDesc, + &hCommandList); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListCreate() failed!\n"); + ret = -1; + goto err_queue_destroy; + } + + // copy from device memory to host memory + ze_result = libze_ops.zeCommandListAppendMemoryCopy( + hCommandList, dst_ptr, src_ptr, size, NULL, 0, NULL); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListAppendMemoryCopy() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + // close and execute the command list + ze_result = libze_ops.zeCommandListClose(hCommandList); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListClose() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + ze_result = libze_ops.zeCommandQueueExecuteCommandLists( + hCommandQueue, 1, &hCommandList, NULL); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueExecuteCommandLists() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + ze_result = libze_ops.zeCommandQueueSynchronize(hCommandQueue, UINT64_MAX); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueSynchronize() failed!\n"); + ret = -1; + goto err_list_destroy; + } + + // cleanup +err_list_destroy: + ze_result = libze_ops.zeCommandListDestroy(hCommandList); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandListDestroy() failed!\n"); + ret = -1; + } + +err_queue_destroy: + ze_result = libze_ops.zeCommandQueueDestroy(hCommandQueue); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeCommandQueueDestroy() failed!\n"); + ret = -1; + } + + return ret; +} + +int create_context(ze_driver_handle_t driver, ze_context_handle_t *context) { + ze_result_t ze_result; + ze_context_desc_t ctxtDesc; + ctxtDesc.stype = ZE_STRUCTURE_TYPE_CONTEXT_DESC; + ctxtDesc.pNext = NULL; + ctxtDesc.flags = 0; + + ze_result = libze_ops.zeContextCreate(driver, &ctxtDesc, context); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeContextCreate() failed!\n"); + return -1; + } + + return 0; +} + +int destroy_context(ze_context_handle_t context) { + ze_result_t ze_result; + ze_result = libze_ops.zeContextDestroy(context); + if (ze_result != ZE_RESULT_SUCCESS) { + fprintf(stderr, "zeContextDestroy() failed!\n"); + return -1; + } + + return 0; +} + +ze_memory_type_t get_mem_type(ze_context_handle_t context, void *ptr) { + ze_device_handle_t device = NULL; + ze_memory_allocation_properties_t alloc_props; + alloc_props.stype = ZE_STRUCTURE_TYPE_MEMORY_ALLOCATION_PROPERTIES; + alloc_props.pNext = NULL; + alloc_props.type = ZE_MEMORY_TYPE_UNKNOWN; + alloc_props.id = 0; + alloc_props.pageSize = 0; + + libze_ops.zeMemGetAllocProperties(context, ptr, &alloc_props, &device); + return alloc_props.type; +} + +UTIL_ONCE_FLAG level_zero_init_flag; +int InitResult; +void init_level_zero_once() { + InitResult = InitLevelZeroOps(); + if (InitResult != 0) { + return; + } + InitResult = init_level_zero_lib(); +} + +int init_level_zero() { + util_init_once(&level_zero_init_flag, init_level_zero_once); + + return InitResult; +} + +level_zero_memory_provider_params_t +create_level_zero_prov_params(umf_usm_memory_type_t memory_type) { + level_zero_memory_provider_params_t params = {NULL, NULL, + UMF_MEMORY_TYPE_UNKNOWN}; + uint32_t driver_idx = 0; + ze_driver_handle_t hDriver; + ze_device_handle_t hDevice; + ze_context_handle_t hContext; + int ret = -1; + + ret = init_level_zero(); + if (ret != 0) { + // Return empty params. Test will be skipped. + return params; + } + + ret = find_driver_with_gpu(&driver_idx, &hDriver); + if (ret != 0 || hDriver == NULL) { + // Return empty params. Test will be skipped. + return params; + } + + ret = find_gpu_device(hDriver, &hDevice); + if (ret != 0 || hDevice == NULL) { + // Return empty params. Test will be skipped. + return params; + } + + ret = create_context(hDriver, &hContext); + if (ret != 0) { + // Return empty params. Test will be skipped. + return params; + } + + params.level_zero_context_handle = hContext; + params.level_zero_device_handle = hDevice; + params.memory_type = memory_type; + + return params; +} \ No newline at end of file diff --git a/test/providers/level_zero_helpers.h b/test/providers/level_zero_helpers.h new file mode 100644 index 0000000000..2bb8261d5f --- /dev/null +++ b/test/providers/level_zero_helpers.h @@ -0,0 +1,48 @@ +/* + * 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 TEST_COMMON_LEVEL_ZERO_HELPERS_HPP +#define TEST_COMMON_LEVEL_ZERO_HELPERS_HPP + +#include + +#include "ze_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int get_drivers(uint32_t *drivers_num_, ze_driver_handle_t **drivers_); + +int get_devices(ze_driver_handle_t driver, uint32_t *devices_num_, + ze_device_handle_t **devices_); + +int find_driver_with_gpu(uint32_t *driver_idx, ze_driver_handle_t *driver_); + +int find_gpu_device(ze_driver_handle_t driver, ze_device_handle_t *device_); + +int level_zero_fill(ze_context_handle_t context, ze_device_handle_t device, + void *ptr, size_t size, const void *pattern, + size_t pattern_size); + +int level_zero_copy(ze_context_handle_t context, ze_device_handle_t device, + void *dst_ptr, const void *src_ptr, size_t size); + +int create_context(ze_driver_handle_t driver, ze_context_handle_t *context); + +int destroy_context(ze_context_handle_t context); + +ze_memory_type_t get_mem_type(ze_context_handle_t context, void *ptr); + +level_zero_memory_provider_params_t +create_level_zero_prov_params(umf_usm_memory_type_t memory_type); + +#ifdef __cplusplus +} +#endif + +#endif // TEST_COMMON_LEVEL_ZERO_HELPERS_HPP diff --git a/test/providers/provider_level_zero.cpp b/test/providers/provider_level_zero.cpp index 9eb3f50f53..ae5ece59cb 100644 --- a/test/providers/provider_level_zero.cpp +++ b/test/providers/provider_level_zero.cpp @@ -12,230 +12,13 @@ #include #include "ipcFixtures.hpp" +#include "level_zero_helpers.h" #include "pool.hpp" #include "utils_load_library.h" -#include "ze_api.h" using umf_test::test; using namespace umf_test; -struct libze_ops { - ze_result_t (*zeInit)(ze_init_flags_t flags); - ze_result_t (*zeDriverGet)(uint32_t *pCount, ze_driver_handle_t *phDrivers); - ze_result_t (*zeDeviceGet)(ze_driver_handle_t hDriver, uint32_t *pCount, - ze_device_handle_t *phDevices); - ze_result_t (*zeDeviceGetProperties)( - ze_device_handle_t hDevice, ze_device_properties_t *pDeviceProperties); - - ze_result_t (*zeContextCreate)(ze_driver_handle_t hDriver, - const ze_context_desc_t *desc, - ze_context_handle_t *phContext); - ze_result_t (*zeContextDestroy)(ze_context_handle_t hContext); - ze_result_t (*zeCommandQueueCreate)( - ze_context_handle_t hContext, ze_device_handle_t hDevice, - const ze_command_queue_desc_t *desc, - ze_command_queue_handle_t *phCommandQueue); - ze_result_t (*zeCommandQueueDestroy)( - ze_command_queue_handle_t hCommandQueue); - ze_result_t (*zeCommandQueueExecuteCommandLists)( - ze_command_queue_handle_t hCommandQueue, uint32_t numCommandLists, - ze_command_list_handle_t *phCommandLists, ze_fence_handle_t hFence); - ze_result_t (*zeCommandQueueSynchronize)( - ze_command_queue_handle_t hCommandQueue, uint64_t timeout); - ze_result_t (*zeCommandListCreate)(ze_context_handle_t hContext, - ze_device_handle_t hDevice, - const ze_command_list_desc_t *desc, - ze_command_list_handle_t *phCommandList); - ze_result_t (*zeCommandListDestroy)(ze_command_list_handle_t hCommandList); - ze_result_t (*zeCommandListClose)(ze_command_list_handle_t hCommandList); - ze_result_t (*zeCommandListAppendMemoryCopy)( - ze_command_list_handle_t hCommandList, void *dstptr, const void *srcptr, - size_t size, ze_event_handle_t hSignalEvent, uint32_t numWaitEvents, - ze_event_handle_t *phWaitEvents); - ze_result_t (*zeCommandListAppendMemoryFill)( - ze_command_list_handle_t hCommandList, void *ptr, const void *pattern, - size_t pattern_size, size_t size, ze_event_handle_t hSignalEvent, - uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents); - ze_result_t (*zeMemGetAllocProperties)( - ze_context_handle_t hContext, const void *ptr, - ze_memory_allocation_properties_t *pMemAllocProperties, - ze_device_handle_t *phDevice); - ze_result_t (*zeMemAllocDevice)(ze_context_handle_t, - const ze_device_mem_alloc_desc_t *, size_t, - size_t, ze_device_handle_t, void **); - ze_result_t (*zeMemFree)(ze_context_handle_t, void *); -} libze_ops; - -#if USE_DLOPEN -struct DlHandleCloser { - void operator()(void *dlHandle) { - if (dlHandle) { - util_close_library(dlHandle); - } - } -}; - -std::unique_ptr zeDlHandle = nullptr; -void InitLevelZeroOps() { -#ifdef _WIN32 - const char *lib_name = "ze_loader.dll"; -#else - const char *lib_name = "libze_loader.so"; -#endif - // Load Level Zero symbols - // NOTE that we use UMF_UTIL_OPEN_LIBRARY_GLOBAL which add all loaded symbols to the - // global symbol table. - zeDlHandle = std::unique_ptr( - util_open_library(lib_name, UMF_UTIL_OPEN_LIBRARY_GLOBAL)); - *(void **)&libze_ops.zeInit = - util_get_symbol_addr(zeDlHandle.get(), "zeInit", lib_name); - ASSERT_NE(libze_ops.zeInit, nullptr); - *(void **)&libze_ops.zeDriverGet = - util_get_symbol_addr(zeDlHandle.get(), "zeDriverGet", lib_name); - ASSERT_NE(libze_ops.zeDriverGet, nullptr); - *(void **)&libze_ops.zeDeviceGet = - util_get_symbol_addr(zeDlHandle.get(), "zeDeviceGet", lib_name); - ASSERT_NE(libze_ops.zeDeviceGet, nullptr); - *(void **)&libze_ops.zeDeviceGetProperties = util_get_symbol_addr( - zeDlHandle.get(), "zeDeviceGetProperties", lib_name); - ASSERT_NE(libze_ops.zeDeviceGetProperties, nullptr); - *(void **)&libze_ops.zeContextCreate = - util_get_symbol_addr(zeDlHandle.get(), "zeContextCreate", lib_name); - ASSERT_NE(libze_ops.zeContextCreate, nullptr); - *(void **)&libze_ops.zeContextDestroy = - util_get_symbol_addr(zeDlHandle.get(), "zeContextDestroy", lib_name); - ASSERT_NE(libze_ops.zeContextDestroy, nullptr); - *(void **)&libze_ops.zeCommandQueueCreate = util_get_symbol_addr( - zeDlHandle.get(), "zeCommandQueueCreate", lib_name); - ASSERT_NE(libze_ops.zeCommandQueueCreate, nullptr); - *(void **)&libze_ops.zeCommandQueueDestroy = util_get_symbol_addr( - zeDlHandle.get(), "zeCommandQueueDestroy", lib_name); - ASSERT_NE(libze_ops.zeCommandQueueDestroy, nullptr); - *(void **)&libze_ops.zeCommandQueueExecuteCommandLists = - util_get_symbol_addr(zeDlHandle.get(), - "zeCommandQueueExecuteCommandLists", lib_name); - ASSERT_NE(libze_ops.zeCommandQueueExecuteCommandLists, nullptr); - *(void **)&libze_ops.zeCommandQueueSynchronize = util_get_symbol_addr( - zeDlHandle.get(), "zeCommandQueueSynchronize", lib_name); - ASSERT_NE(libze_ops.zeCommandQueueSynchronize, nullptr); - *(void **)&libze_ops.zeCommandListCreate = - util_get_symbol_addr(zeDlHandle.get(), "zeCommandListCreate", lib_name); - ASSERT_NE(libze_ops.zeCommandListCreate, nullptr); - *(void **)&libze_ops.zeCommandListDestroy = util_get_symbol_addr( - zeDlHandle.get(), "zeCommandListDestroy", lib_name); - ASSERT_NE(libze_ops.zeCommandListDestroy, nullptr); - *(void **)&libze_ops.zeCommandListClose = - util_get_symbol_addr(zeDlHandle.get(), "zeCommandListClose", lib_name); - ASSERT_NE(libze_ops.zeCommandListClose, nullptr); - *(void **)&libze_ops.zeCommandListAppendMemoryCopy = util_get_symbol_addr( - zeDlHandle.get(), "zeCommandListAppendMemoryCopy", lib_name); - ASSERT_NE(libze_ops.zeCommandListAppendMemoryCopy, nullptr); - *(void **)&libze_ops.zeCommandListAppendMemoryFill = util_get_symbol_addr( - zeDlHandle.get(), "zeCommandListAppendMemoryFill", lib_name); - ASSERT_NE(libze_ops.zeCommandListAppendMemoryFill, nullptr); - *(void **)&libze_ops.zeMemGetAllocProperties = util_get_symbol_addr( - zeDlHandle.get(), "zeMemGetAllocProperties", lib_name); - ASSERT_NE(libze_ops.zeMemGetAllocProperties, nullptr); - *(void **)&libze_ops.zeMemAllocDevice = - util_get_symbol_addr(zeDlHandle.get(), "zeMemAllocDevice", lib_name); - ASSERT_NE(libze_ops.zeMemAllocDevice, nullptr); - *(void **)&libze_ops.zeMemFree = - util_get_symbol_addr(zeDlHandle.get(), "zeMemFree", lib_name); - ASSERT_NE(libze_ops.zeMemFree, nullptr); -} - -#else // USE_DLOPEN -void InitLevelZeroOps() { - // Level Zero is linked statically but we prepare ops structure to - // make test code consistent - libze_ops.zeInit = zeInit; - libze_ops.zeDriverGet = zeDriverGet; - libze_ops.zeDeviceGet = zeDeviceGet; - libze_ops.zeDeviceGetProperties = zeDeviceGetProperties; - libze_ops.zeContextCreate = zeContextCreate; - libze_ops.zeContextDestroy = zeContextDestroy; - libze_ops.zeCommandQueueCreate = zeCommandQueueCreate; - libze_ops.zeCommandQueueDestroy = zeCommandQueueDestroy; - libze_ops.zeCommandQueueExecuteCommandLists = - zeCommandQueueExecuteCommandLists; - libze_ops.zeCommandQueueSynchronize = zeCommandQueueSynchronize; - libze_ops.zeCommandListCreate = zeCommandListCreate; - libze_ops.zeCommandListDestroy = zeCommandListDestroy; - libze_ops.zeCommandListClose = zeCommandListClose; - libze_ops.zeCommandListAppendMemoryCopy = zeCommandListAppendMemoryCopy; - libze_ops.zeCommandListAppendMemoryFill = zeCommandListAppendMemoryFill; - libze_ops.zeMemGetAllocProperties = zeMemGetAllocProperties; - libze_ops.zeMemAllocDevice = zeMemAllocDevice; - libze_ops.zeMemFree = zeMemFree; -} -#endif // USE_DLOPEN - -std::once_flag level_zero_init_flag; -ze_result_t zeInitResult; -ze_result_t InitLevelZero() { - std::call_once(level_zero_init_flag, []() { - InitLevelZeroOps(); - zeInitResult = libze_ops.zeInit(0); - }); - return zeInitResult; -} - -ze_result_t CreateContext(ze_driver_handle_t hDriver, - ze_context_handle_t &hContext) { - ze_context_desc_t ctxtDesc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC, nullptr, 0}; - return libze_ops.zeContextCreate(hDriver, &ctxtDesc, &hContext); -} - -ze_result_t FindDriverWithGpu(ze_driver_handle_t &hDriver, - ze_device_handle_t &hDevice) { - // Discover all the driver instances - uint32_t driverCount = 0; - ze_result_t ze_result = libze_ops.zeDriverGet(&driverCount, nullptr); - if (ze_result != ZE_RESULT_SUCCESS) { - return ze_result; - } - - std::vector allDrivers(driverCount); - ze_result = libze_ops.zeDriverGet(&driverCount, allDrivers.data()); - if (ze_result != ZE_RESULT_SUCCESS) { - return ze_result; - } - - // Find a driver instance with a GPU device - for (uint32_t i = 0; i < driverCount; ++i) { - uint32_t deviceCount = 0; - ze_result = libze_ops.zeDeviceGet(allDrivers[i], &deviceCount, nullptr); - if (ze_result != ZE_RESULT_SUCCESS) { - return ze_result; - } - - std::vector allDevices(deviceCount); - ze_result = libze_ops.zeDeviceGet(allDrivers[i], &deviceCount, - allDevices.data()); - if (ze_result != ZE_RESULT_SUCCESS) { - return ze_result; - } - - for (uint32_t d = 0; d < deviceCount; ++d) { - ze_device_properties_t device_properties{}; - device_properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES; - ze_result = libze_ops.zeDeviceGetProperties(allDevices[d], - &device_properties); - if (ze_result != ZE_RESULT_SUCCESS) { - return ze_result; - } - - if (ZE_DEVICE_TYPE_GPU == device_properties.type) { - hDriver = allDrivers[i]; - hDevice = allDevices[d]; - return ze_result; - } - } - } - - return ze_result; -} - class LevelZeroMemoryAccessor : public MemoryAccessor { public: LevelZeroMemoryAccessor(ze_context_handle_t hContext, @@ -245,98 +28,17 @@ class LevelZeroMemoryAccessor : public MemoryAccessor { size_t pattern_size) { ASSERT_NE(ptr, nullptr); - ze_command_queue_desc_t commandQueueDesc = { - ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC, - NULL, - 0, - 0, - 0, - ZE_COMMAND_QUEUE_MODE_DEFAULT, - ZE_COMMAND_QUEUE_PRIORITY_NORMAL}; - - ze_command_list_desc_t commandListDesc = { - ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC, 0, 0, - ZE_COMMAND_LIST_FLAG_RELAXED_ORDERING}; - - ze_command_queue_handle_t hCommandQueue; - ze_result_t ze_result = libze_ops.zeCommandQueueCreate( - hContext_, hDevice_, &commandQueueDesc, &hCommandQueue); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - - ze_command_list_handle_t hCommandList; - ze_result = libze_ops.zeCommandListCreate( - hContext_, hDevice_, &commandListDesc, &hCommandList); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - - // fill memory with a pattern - ze_result = libze_ops.zeCommandListAppendMemoryFill( - hCommandList, ptr, pattern, pattern_size, size, nullptr, 0, - nullptr); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - - // close and execute the command list - ze_result = libze_ops.zeCommandListClose(hCommandList); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - ze_result = libze_ops.zeCommandQueueExecuteCommandLists( - hCommandQueue, 1, &hCommandList, nullptr); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - - // sync - ze_result = libze_ops.zeCommandQueueSynchronize( - hCommandQueue, std::numeric_limits::max()); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - - // cleanup - ze_result = libze_ops.zeCommandListDestroy(hCommandList); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - ze_result = libze_ops.zeCommandQueueDestroy(hCommandQueue); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); + int ret = level_zero_fill(hContext_, hDevice_, ptr, size, pattern, + pattern_size); + ASSERT_EQ(ret, 0); } void copy(void *dst_ptr, void *src_ptr, size_t size) { ASSERT_NE(dst_ptr, nullptr); ASSERT_NE(src_ptr, nullptr); - ze_command_queue_desc_t commandQueueDesc = { - ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC, - NULL, - 0, - 0, - 0, - ZE_COMMAND_QUEUE_MODE_DEFAULT, - ZE_COMMAND_QUEUE_PRIORITY_NORMAL}; - - ze_command_list_desc_t commandListDesc = { - ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC, 0, 0, - ZE_COMMAND_LIST_FLAG_RELAXED_ORDERING}; - - ze_command_queue_handle_t hCommandQueue; - ze_result_t ze_result = libze_ops.zeCommandQueueCreate( - hContext_, hDevice_, &commandQueueDesc, &hCommandQueue); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - - ze_command_list_handle_t hCommandList; - ze_result = libze_ops.zeCommandListCreate( - hContext_, hDevice_, &commandListDesc, &hCommandList); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - - // copy from device memory to host memory - libze_ops.zeCommandListAppendMemoryCopy(hCommandList, dst_ptr, src_ptr, - size, nullptr, 0, nullptr); - - // close and execute the command list - libze_ops.zeCommandListClose(hCommandList); - libze_ops.zeCommandQueueExecuteCommandLists(hCommandQueue, 1, - &hCommandList, nullptr); - - libze_ops.zeCommandQueueSynchronize( - hCommandQueue, std::numeric_limits::max()); - - // cleanup - ze_result = libze_ops.zeCommandListDestroy(hCommandList); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); - ze_result = libze_ops.zeCommandQueueDestroy(hCommandQueue); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); + int ret = level_zero_copy(hContext_, hDevice_, dst_ptr, src_ptr, size); + ASSERT_EQ(ret, 0); } private: @@ -383,8 +85,8 @@ struct umfLevelZeroProviderTest } void TearDown() override { - ze_result_t ze_result = libze_ops.zeContextDestroy(hContext); - ASSERT_EQ(ze_result, ZE_RESULT_SUCCESS); + int ret = destroy_context(hContext); + ASSERT_EQ(ret, 0); test::TearDown(); } @@ -416,12 +118,8 @@ TEST_P(umfLevelZeroProviderTest, basic) { // use the allocated memory - fill it with a 0xAB pattern memAccessor->fill(ptr, size, &pattern, sizeof(pattern)); - // get properties of the allocation - ze_memory_allocation_properties_t alloc_props{ - ZE_STRUCTURE_TYPE_MEMORY_ALLOCATION_PROPERTIES, 0, - ZE_MEMORY_TYPE_UNKNOWN, 0, 0}; - libze_ops.zeMemGetAllocProperties(hContext, ptr, &alloc_props, &hDevice); - ASSERT_EQ(alloc_props.type, zeMemoryTypeExpected); + ze_memory_type_t zeMemoryTypeActual = get_mem_type(hContext, ptr); + ASSERT_EQ(zeMemoryTypeActual, zeMemoryTypeExpected); // check if the pattern was successfully applied uint32_t *hostMemory = (uint32_t *)calloc(1, size); @@ -441,45 +139,12 @@ TEST_P(umfLevelZeroProviderTest, basic) { // TODO add negative test and check for Level Zero native errors // TODO add tests that mixes Level Zero Memory Provider and Disjoint Pool -level_zero_memory_provider_params_t -CreateLevelZeroProviderParams(umf_usm_memory_type_t memory_type) { - level_zero_memory_provider_params_t params = {NULL, NULL, - UMF_MEMORY_TYPE_UNKNOWN}; - ze_driver_handle_t hDriver; - ze_device_handle_t hDevice; - ze_context_handle_t hContext; - - ze_result_t ze_result = InitLevelZero(); - if (ze_result != ZE_RESULT_SUCCESS) { - // Return empty params. Test will be skipped. - return params; - } - - ze_result = FindDriverWithGpu(hDriver, hDevice); - if (ze_result != ZE_RESULT_SUCCESS) { - // Return empty params. Test will be skipped. - return params; - } - - ze_result = CreateContext(hDriver, hContext); - if (ze_result != ZE_RESULT_SUCCESS) { - // Return empty params. Test will be skipped. - return params; - } - - params.level_zero_context_handle = hContext; - params.level_zero_device_handle = hDevice; - params.memory_type = memory_type; - - return params; -} - level_zero_memory_provider_params_t l0Params_device_memory = - CreateLevelZeroProviderParams(UMF_MEMORY_TYPE_DEVICE); + create_level_zero_prov_params(UMF_MEMORY_TYPE_DEVICE); level_zero_memory_provider_params_t l0Params_shared_memory = - CreateLevelZeroProviderParams(UMF_MEMORY_TYPE_SHARED); + create_level_zero_prov_params(UMF_MEMORY_TYPE_SHARED); level_zero_memory_provider_params_t l0Params_host_memory = - CreateLevelZeroProviderParams(UMF_MEMORY_TYPE_HOST); + create_level_zero_prov_params(UMF_MEMORY_TYPE_HOST); LevelZeroMemoryAccessor l0Accessor( (ze_context_handle_t)l0Params_device_memory.level_zero_context_handle,