Skip to content

Commit c3b8360

Browse files
authored
Merge pull request #1737 from kswiecicki/ur-l0-ipc
[L0] Implement IPC ops for L0 adapter
2 parents bc1a28e + 73df8f9 commit c3b8360

File tree

7 files changed

+278
-9
lines changed

7 files changed

+278
-9
lines changed

source/adapters/level_zero/usm.cpp

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//===--------- usm.cpp - Level Zero Adapter -------------------------------===//
22
//
3-
// Copyright (C) 2023 Intel Corporation
3+
// Copyright (C) 2023-2024 Intel Corporation
44
//
55
// Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM
66
// Exceptions. See LICENSE.TXT
@@ -18,6 +18,7 @@
1818

1919
#include "logger/ur_logger.hpp"
2020
#include "ur_level_zero.hpp"
21+
#include "ur_util.hpp"
2122

2223
#include <umf_helpers.hpp>
2324

@@ -766,6 +767,97 @@ umf_result_t L0MemoryProvider::get_min_page_size(void *Ptr, size_t *PageSize) {
766767
return UMF_RESULT_SUCCESS;
767768
}
768769

770+
typedef struct ze_ipc_data_t {
771+
int pid;
772+
ze_ipc_mem_handle_t zeHandle;
773+
} ze_ipc_data_t;
774+
775+
umf_result_t L0MemoryProvider::get_ipc_handle_size(size_t *Size) {
776+
UR_ASSERT(Size, UMF_RESULT_ERROR_INVALID_ARGUMENT);
777+
*Size = sizeof(ze_ipc_data_t);
778+
779+
return UMF_RESULT_SUCCESS;
780+
}
781+
782+
umf_result_t L0MemoryProvider::get_ipc_handle(const void *Ptr, size_t Size,
783+
void *IpcData) {
784+
std::ignore = Size;
785+
786+
UR_ASSERT(Ptr && IpcData, UMF_RESULT_ERROR_INVALID_ARGUMENT);
787+
ze_ipc_data_t *zeIpcData = (ze_ipc_data_t *)IpcData;
788+
auto Ret = ZE_CALL_NOCHECK(zeMemGetIpcHandle,
789+
(Context->ZeContext, Ptr, &zeIpcData->zeHandle));
790+
if (Ret != ZE_RESULT_SUCCESS) {
791+
return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC;
792+
}
793+
794+
zeIpcData->pid = ur_getpid();
795+
796+
return UMF_RESULT_SUCCESS;
797+
}
798+
799+
umf_result_t L0MemoryProvider::put_ipc_handle(void *IpcData) {
800+
UR_ASSERT(IpcData, UMF_RESULT_ERROR_INVALID_ARGUMENT);
801+
ze_ipc_data_t *zeIpcData = (ze_ipc_data_t *)IpcData;
802+
std::ignore = zeIpcData;
803+
804+
// zeMemPutIpcHandle was introduced in Level Zero 1.6. Before Level Zero 1.6,
805+
// IPC handle was released automatically when corresponding memory buffer
806+
// was freed.
807+
#if (ZE_API_VERSION_CURRENT >= ZE_MAKE_VERSION(1, 6))
808+
auto Ret = ZE_CALL_NOCHECK(zeMemPutIpcHandle,
809+
(Context->ZeContext, zeIpcData->zeHandle));
810+
if (Ret != ZE_RESULT_SUCCESS) {
811+
return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC;
812+
}
813+
#endif
814+
815+
return UMF_RESULT_SUCCESS;
816+
}
817+
818+
umf_result_t L0MemoryProvider::open_ipc_handle(void *IpcData, void **Ptr) {
819+
UR_ASSERT(IpcData && Ptr, UMF_RESULT_ERROR_INVALID_ARGUMENT);
820+
ze_ipc_data_t *zeIpcData = (ze_ipc_data_t *)IpcData;
821+
822+
int fdLocal = -1;
823+
if (zeIpcData->pid != ur_getpid()) {
824+
int fdRemote = -1;
825+
memcpy(&fdRemote, &zeIpcData->zeHandle, sizeof(fdRemote));
826+
fdLocal = ur_duplicate_fd(zeIpcData->pid, fdRemote);
827+
if (fdLocal == -1) {
828+
logger::error("duplicating file descriptor from IPC handle failed");
829+
return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC;
830+
}
831+
832+
memcpy(&zeIpcData->zeHandle, &fdLocal, sizeof(fdLocal));
833+
}
834+
835+
auto Ret =
836+
ZE_CALL_NOCHECK(zeMemOpenIpcHandle, (Context->ZeContext, Device->ZeDevice,
837+
zeIpcData->zeHandle, 0, Ptr));
838+
if (fdLocal != -1) {
839+
ur_close_fd(fdLocal);
840+
}
841+
842+
if (Ret != ZE_RESULT_SUCCESS) {
843+
return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC;
844+
}
845+
846+
return UMF_RESULT_SUCCESS;
847+
}
848+
849+
umf_result_t L0MemoryProvider::close_ipc_handle(void *Ptr, size_t Size) {
850+
std::ignore = Size;
851+
852+
UR_ASSERT(Ptr, UMF_RESULT_ERROR_INVALID_ARGUMENT);
853+
auto Ret = ZE_CALL_NOCHECK(zeMemCloseIpcHandle, (Context->ZeContext, Ptr));
854+
if (Ret != ZE_RESULT_SUCCESS) {
855+
return UMF_RESULT_ERROR_MEMORY_PROVIDER_SPECIFIC;
856+
}
857+
858+
return UMF_RESULT_SUCCESS;
859+
}
860+
769861
ur_result_t L0SharedMemoryProvider::allocateImpl(void **ResultPtr, size_t Size,
770862
uint32_t Alignment) {
771863
return USMSharedAllocImpl(ResultPtr, Context, Device, /*host flags*/ 0,

source/adapters/level_zero/usm.hpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,26 @@ class USMMemoryProviderBase {
8585
virtual umf_result_t purge_force(void *, size_t) {
8686
return UMF_RESULT_ERROR_NOT_SUPPORTED;
8787
};
88-
umf_result_t allocation_merge(void *, void *, size_t) {
89-
return UMF_RESULT_ERROR_UNKNOWN;
88+
virtual umf_result_t allocation_merge(void *, void *, size_t) {
89+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
90+
}
91+
virtual umf_result_t allocation_split(void *, size_t, size_t) {
92+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
93+
}
94+
virtual umf_result_t get_ipc_handle_size(size_t *) {
95+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
96+
}
97+
virtual umf_result_t get_ipc_handle(const void *, size_t, void *) {
98+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
99+
}
100+
virtual umf_result_t put_ipc_handle(void *) {
101+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
90102
}
91-
umf_result_t allocation_split(void *, size_t, size_t) {
92-
return UMF_RESULT_ERROR_UNKNOWN;
103+
virtual umf_result_t open_ipc_handle(void *, void **) {
104+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
105+
}
106+
virtual umf_result_t close_ipc_handle(void *, size_t) {
107+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
93108
}
94109
virtual const char *get_name() { return ""; };
95110
virtual ~USMMemoryProviderBase() = default;
@@ -111,6 +126,11 @@ class L0MemoryProvider : public USMMemoryProviderBase {
111126
umf_result_t get_min_page_size(void *, size_t *) override;
112127
// TODO: Different name for each provider (Host/Shared/SharedRO/Device)
113128
const char *get_name() override { return "L0"; };
129+
umf_result_t get_ipc_handle_size(size_t *) override;
130+
umf_result_t get_ipc_handle(const void *, size_t, void *) override;
131+
umf_result_t put_ipc_handle(void *) override;
132+
umf_result_t open_ipc_handle(void *, void **) override;
133+
umf_result_t close_ipc_handle(void *, size_t) override;
114134
};
115135

116136
// Allocation routines for shared memory type

source/common/umf_helpers.hpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
*
3-
* Copyright (C) 2023 Intel Corporation
3+
* Copyright (C) 2023-2024 Intel Corporation
44
*
55
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
66
* See LICENSE.TXT
@@ -33,6 +33,35 @@ using provider_unique_handle_t =
3333
std::unique_ptr<umf_memory_provider_t,
3434
std::function<void(umf_memory_provider_handle_t)>>;
3535

36+
#define DEFINE_CHECK_OP(op) \
37+
template <typename T> class HAS_OP_##op { \
38+
typedef char check_success; \
39+
typedef long check_fail; \
40+
template <typename U> static check_success test(decltype(&U::op)); \
41+
template <typename U> static check_fail test(...); \
42+
\
43+
public: \
44+
static constexpr bool value = \
45+
sizeof(test<T>(0)) == sizeof(check_success); \
46+
}; \
47+
\
48+
template <typename T, typename... Args> \
49+
static inline \
50+
typename std::enable_if<HAS_OP_##op<T>::value, umf_result_t>::type \
51+
CALL_OP_##op(T *t, Args &&...args) { \
52+
return t->op(std::forward<Args>(args)...); \
53+
}; \
54+
\
55+
static inline umf_result_t CALL_OP_##op(...) { \
56+
return UMF_RESULT_ERROR_NOT_SUPPORTED; \
57+
}
58+
59+
DEFINE_CHECK_OP(get_ipc_handle_size);
60+
DEFINE_CHECK_OP(get_ipc_handle);
61+
DEFINE_CHECK_OP(put_ipc_handle);
62+
DEFINE_CHECK_OP(open_ipc_handle);
63+
DEFINE_CHECK_OP(close_ipc_handle);
64+
3665
#define UMF_ASSIGN_OP(ops, type, func, default_return) \
3766
ops.func = [](void *obj, auto... args) { \
3867
try { \
@@ -50,6 +79,15 @@ using provider_unique_handle_t =
5079
} \
5180
}
5281

82+
#define UMF_ASSIGN_OP_OPT(ops, type, func, default_return) \
83+
ops.func = [](void *obj, auto... args) { \
84+
try { \
85+
return CALL_OP_##func(reinterpret_cast<type *>(obj), args...); \
86+
} catch (...) { \
87+
return default_return; \
88+
} \
89+
}
90+
5391
namespace detail {
5492
template <typename T, typename ArgsTuple>
5593
umf_result_t initialize(T *obj, ArgsTuple &&args) {
@@ -133,6 +171,12 @@ auto memoryProviderMakeUnique(Args &&...args) {
133171
UMF_ASSIGN_OP(ops.ext, T, purge_force, UMF_RESULT_ERROR_UNKNOWN);
134172
UMF_ASSIGN_OP(ops.ext, T, allocation_merge, UMF_RESULT_ERROR_UNKNOWN);
135173
UMF_ASSIGN_OP(ops.ext, T, allocation_split, UMF_RESULT_ERROR_UNKNOWN);
174+
UMF_ASSIGN_OP_OPT(ops.ipc, T, get_ipc_handle_size,
175+
UMF_RESULT_ERROR_UNKNOWN);
176+
UMF_ASSIGN_OP_OPT(ops.ipc, T, get_ipc_handle, UMF_RESULT_ERROR_UNKNOWN);
177+
UMF_ASSIGN_OP_OPT(ops.ipc, T, put_ipc_handle, UMF_RESULT_ERROR_UNKNOWN);
178+
UMF_ASSIGN_OP_OPT(ops.ipc, T, open_ipc_handle, UMF_RESULT_ERROR_UNKNOWN);
179+
UMF_ASSIGN_OP_OPT(ops.ipc, T, close_ipc_handle, UMF_RESULT_ERROR_UNKNOWN);
136180

137181
umf_memory_provider_handle_t hProvider = nullptr;
138182
auto ret = umfMemoryProviderCreate(&ops, &argsTuple, &hProvider);

source/common/ur_util.cpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
*
3-
* Copyright (C) 2022-2023 Intel Corporation
3+
* Copyright (C) 2022-2024 Intel Corporation
44
*
55
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
66
* See LICENSE.TXT
@@ -9,15 +9,63 @@
99
*/
1010

1111
#include "ur_util.hpp"
12+
#include "logger/ur_logger.hpp"
1213

1314
#ifdef _WIN32
1415
#include <windows.h>
1516
int ur_getpid(void) { return static_cast<int>(GetCurrentProcessId()); }
17+
18+
int ur_close_fd(int fd) { return -1; }
19+
20+
int ur_duplicate_fd(int pid, int fd_in) {
21+
// TODO: find another way to obtain a duplicate of another process's file descriptor
22+
(void)pid; // unused
23+
(void)fd_in; // unused
24+
return -1;
25+
}
26+
1627
#else
1728

29+
#include <sys/syscall.h>
1830
#include <unistd.h>
1931
int ur_getpid(void) { return static_cast<int>(getpid()); }
20-
#endif
32+
33+
int ur_close_fd(int fd) { return close(fd); }
34+
35+
int ur_duplicate_fd(int pid, int fd_in) {
36+
// pidfd_getfd(2) is used to obtain a duplicate of another process's file descriptor.
37+
// Permission to duplicate another process's file descriptor
38+
// is governed by a ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check (see ptrace(2))
39+
// that can be changed using the /proc/sys/kernel/yama/ptrace_scope interface.
40+
// pidfd_getfd(2) is supported since Linux 5.6
41+
// pidfd_open(2) is supported since Linux 5.3
42+
#if defined(__NR_pidfd_open) && defined(__NR_pidfd_getfd)
43+
errno = 0;
44+
int pid_fd = syscall(SYS_pidfd_open, pid, 0);
45+
if (pid_fd == -1) {
46+
logger::error("SYS_pidfd_open");
47+
return -1;
48+
}
49+
50+
int fd_dup = syscall(SYS_pidfd_getfd, pid_fd, fd_in, 0);
51+
close(pid_fd);
52+
if (fd_dup == -1) {
53+
logger::error("SYS_pidfd_getfd");
54+
return -1;
55+
}
56+
57+
return fd_dup;
58+
#else
59+
// TODO: find another way to obtain a duplicate of another process's file descriptor
60+
(void)pid; // unused
61+
(void)fd_in; // unused
62+
errno = ENOTSUP; // unsupported
63+
logger::error("__NR_pidfd_open or __NR_pidfd_getfd not available");
64+
return -1;
65+
#endif /* defined(__NR_pidfd_open) && defined(__NR_pidfd_getfd) */
66+
}
67+
68+
#endif /* _WIN32 */
2169

2270
std::optional<std::string> ur_getenv(const char *name) {
2371
#if defined(_WIN32)

source/common/ur_util.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
*
3-
* Copyright (C) 2022-2023 Intel Corporation
3+
* Copyright (C) 2022-2024 Intel Corporation
44
*
55
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
66
* See LICENSE.TXT
@@ -27,6 +27,8 @@
2727
#include <vector>
2828

2929
int ur_getpid(void);
30+
int ur_close_fd(int fd);
31+
int ur_duplicate_fd(int pid, int fd_in);
3032

3133
/* for compatibility with non-clang compilers */
3234
#if defined(__has_feature)

test/adapters/level_zero/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,16 @@ if(NOT WIN32)
6969
target_link_libraries(test-adapter-level_zero_multi_queue PRIVATE zeCallMap)
7070
endif()
7171

72+
add_adapter_test(level_zero_ipc
73+
FIXTURE DEVICES
74+
SOURCES
75+
ipc.cpp
76+
ENVIRONMENT
77+
"UR_ADAPTERS_FORCE_LOAD=\"$<TARGET_FILE:ur_adapter_level_zero>\""
78+
)
79+
80+
target_link_libraries(test-adapter-level_zero_ipc PRIVATE
81+
ur_umf
82+
)
83+
7284
add_subdirectory(v2)

test/adapters/level_zero/ipc.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (C) 2024 Intel Corporation
2+
// Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
3+
// See LICENSE.TXT
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
6+
#include <umf/memory_pool.h>
7+
#include <umf/memory_provider.h>
8+
#include <uur/fixtures.h>
9+
10+
#ifndef ASSERT_UMF_SUCCESS
11+
#define ASSERT_UMF_SUCCESS(ACTUAL) ASSERT_EQ(ACTUAL, UMF_RESULT_SUCCESS)
12+
#endif
13+
14+
using urL0IpcTest = uur::urContextTest;
15+
UUR_INSTANTIATE_DEVICE_TEST_SUITE_P(urL0IpcTest);
16+
17+
TEST_P(urL0IpcTest, SuccessHostL0Ipc) {
18+
ur_device_usm_access_capability_flags_t hostUSMSupport = 0;
19+
ASSERT_SUCCESS(uur::GetDeviceUSMHostSupport(device, hostUSMSupport));
20+
if (!hostUSMSupport) {
21+
GTEST_SKIP() << "Host USM is not supported.";
22+
}
23+
24+
void *ptr = nullptr;
25+
size_t allocSize = sizeof(int);
26+
ASSERT_SUCCESS(urUSMHostAlloc(context, nullptr, nullptr, allocSize, &ptr));
27+
ASSERT_NE(ptr, nullptr);
28+
29+
umf_memory_pool_handle_t umfPool = umfPoolByPtr(ptr);
30+
ASSERT_NE(umfPool, nullptr);
31+
32+
umf_memory_provider_handle_t umfProvider = nullptr;
33+
ASSERT_UMF_SUCCESS(umfPoolGetMemoryProvider(umfPool, &umfProvider));
34+
35+
size_t ipcHandleSize = 0;
36+
ASSERT_UMF_SUCCESS(
37+
umfMemoryProviderGetIPCHandleSize(umfProvider, &ipcHandleSize));
38+
39+
void *ipcHandle = nullptr;
40+
ASSERT_UMF_SUCCESS(
41+
umfMemoryProviderAlloc(umfProvider, ipcHandleSize, 0, &ipcHandle));
42+
ASSERT_UMF_SUCCESS(
43+
umfMemoryProviderGetIPCHandle(umfProvider, ptr, allocSize, ipcHandle));
44+
45+
ASSERT_UMF_SUCCESS(umfMemoryProviderPutIPCHandle(umfProvider, ipcHandle));
46+
47+
ASSERT_UMF_SUCCESS(
48+
umfMemoryProviderFree(umfProvider, ipcHandle, ipcHandleSize));
49+
50+
ASSERT_SUCCESS(urUSMFree(context, ptr));
51+
}

0 commit comments

Comments
 (0)