mirror of
https://github.com/intel/compute-runtime.git
synced 2026-01-05 09:09:04 +08:00
Add support for IPC handles with implicit scaling
When using implicit scaling, device allocations may have more than one internal allocation created internally. In that case, a separate dma-buf handle per internal allocation needs to be exported. So introduced two driver experimental extensions to export and import more than one IPC handle: - zexMemGetIpcHandles - zexMemOpenIpcHandles Related-To: LOCI-2919 Signed-off-by: Jaime Arteaga <jaime.a.arteaga.molina@intel.com>
This commit is contained in:
committed by
Compute-Runtime-Automation
parent
c0de4fd243
commit
3f26f45c10
@@ -7,3 +7,23 @@
|
||||
|
||||
#include "level_zero/api/driver_experimental/public/zex_api.h"
|
||||
#include "level_zero/core/source/context/context.h"
|
||||
|
||||
ZE_APIEXPORT ze_result_t ZE_APICALL
|
||||
zexMemGetIpcHandles(
|
||||
ze_context_handle_t hContext,
|
||||
const void *ptr,
|
||||
uint32_t *numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles) {
|
||||
return L0::Context::fromHandle(hContext)->getIpcMemHandles(ptr, numIpcHandles, pIpcHandles);
|
||||
}
|
||||
|
||||
ZE_APIEXPORT ze_result_t ZE_APICALL
|
||||
zexMemOpenIpcHandles(
|
||||
ze_context_handle_t hContext,
|
||||
ze_device_handle_t hDevice,
|
||||
uint32_t numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles,
|
||||
ze_ipc_memory_flags_t flags,
|
||||
void **pptr) {
|
||||
return L0::Context::fromHandle(hContext)->openIpcMemHandles(hDevice, numIpcHandles, pIpcHandles, flags, pptr);
|
||||
}
|
||||
@@ -17,6 +17,71 @@ extern "C" {
|
||||
|
||||
#include "level_zero/api/driver_experimental/public/zex_api.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef ZEX_MEM_IPC_HANDLES_NAME
|
||||
/// @brief Multiple IPC handles driver extension name
|
||||
#define ZEX_MEM_IPC_HANDLES_NAME "ZEX_mem_ipc_handles"
|
||||
#endif // ZEX_MEM_IPC_HANDLES_NAME
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Multiple IPC handles driver extension Version(s)
|
||||
typedef enum _zex_mem_ipc_handles_version_t {
|
||||
ZEX_MEM_IPC_HANDLES_VERSION_1_0 = ZE_MAKE_VERSION(1, 0), ///< version 1.0
|
||||
ZEX_MEM_IPC_HANDLES_VERSION_CURRENT = ZE_MAKE_VERSION(1, 0), ///< latest known version
|
||||
ZEX_MEM_IPC_HANDLES_VERSION_FORCE_UINT32 = 0x7fffffff
|
||||
|
||||
} zex_mem_ipc_handles_version_t;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Returns an array IPC memory handles for the specified allocation
|
||||
///
|
||||
/// @details
|
||||
/// - Takes a pointer to a device memory allocation and returns an array of
|
||||
// IPC memory handle for exporting it for use in another process.
|
||||
/// - The application may call this function from simultaneous threads.
|
||||
/// - The implementation of this function must be thread-safe.
|
||||
///
|
||||
/// @returns
|
||||
/// - ::ZE_RESULT_SUCCESS
|
||||
/// - ::ZE_RESULT_ERROR_INVALID_ARGUMENT
|
||||
/// + `ptr` not known
|
||||
ZE_APIEXPORT ze_result_t ZE_APICALL
|
||||
zexMemGetIpcHandles(
|
||||
ze_context_handle_t hContext, ///< [in] handle of the context object
|
||||
const void *ptr, ///< [in] pointer to the device memory allocation
|
||||
uint32_t *numIpcHandles, ///< [in,out] number of IPC handles associated with the allocation
|
||||
///< if numIpcHandles is zero, then the driver shall update the value with the
|
||||
///< total number of IPC handles associated with the allocation.
|
||||
ze_ipc_mem_handle_t *pIpcHandles ///< [in,out][optional][range(0, *numIpcHandles)] returned array of IPC memory handles
|
||||
);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Creates an allocation associated with an array of IPC memory handles
|
||||
/// imported from another process.
|
||||
///
|
||||
/// @details
|
||||
/// - Takes an array of IPC memory handles from a remote process and associates it
|
||||
/// with a device pointer usable in this process.
|
||||
/// - The device pointer in this process should not be freed with
|
||||
/// ::zeMemFree, but rather with ::zeMemCloseIpcHandle.
|
||||
/// - The application may call this function from simultaneous threads.
|
||||
/// - The implementation of this function must be thread-safe.
|
||||
///
|
||||
/// @returns
|
||||
/// - ::ZE_RESULT_SUCCESS
|
||||
/// - ::ZE_RESULT_ERROR_INVALID_ARGUMENT
|
||||
/// + handles not known
|
||||
ZE_APIEXPORT ze_result_t ZE_APICALL
|
||||
zexMemOpenIpcHandles(
|
||||
ze_context_handle_t hContext, ///< [in] handle of the context object
|
||||
ze_device_handle_t hDevice, ///< [in] handle of the device to associate with the IPC memory handle
|
||||
uint32_t numIpcHandles, ///< [in] number of IPC handles associated with the allocation
|
||||
ze_ipc_mem_handle_t *pIpcHandles, ///< [in][range(0, *numIpcHandles)] array of IPC memory handles
|
||||
ze_ipc_memory_flags_t flags, ///< [in] flags controlling the operation.
|
||||
///< must be 0 (default) or a valid combination of ::ze_ipc_memory_flag_t.
|
||||
void **pptr ///< [out] pointer to device allocation in this process
|
||||
);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020-2021 Intel Corporation
|
||||
* Copyright (C) 2020-2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
@@ -8,3 +8,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <level_zero/ze_api.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -71,6 +71,20 @@ struct Context : _ze_context_handle_t {
|
||||
virtual ze_result_t closeIpcMemHandle(const void *ptr) = 0;
|
||||
virtual ze_result_t getIpcMemHandle(const void *ptr,
|
||||
ze_ipc_mem_handle_t *pIpcHandle) = 0;
|
||||
virtual ze_result_t
|
||||
getIpcMemHandles(
|
||||
const void *ptr,
|
||||
uint32_t *numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles) = 0;
|
||||
|
||||
virtual ze_result_t
|
||||
openIpcMemHandles(
|
||||
ze_device_handle_t hDevice,
|
||||
uint32_t numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles,
|
||||
ze_ipc_memory_flags_t flags,
|
||||
void **pptr) = 0;
|
||||
|
||||
virtual ze_result_t openIpcMemHandle(ze_device_handle_t hDevice,
|
||||
ze_ipc_mem_handle_t handle,
|
||||
ze_ipc_memory_flags_t flags,
|
||||
|
||||
@@ -429,6 +429,31 @@ ze_result_t ContextImp::getIpcMemHandle(const void *ptr,
|
||||
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
ze_result_t ContextImp::getIpcMemHandles(const void *ptr,
|
||||
uint32_t *numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles) {
|
||||
NEO::SvmAllocationData *allocData = this->driverHandle->svmAllocsManager->getSVMAlloc(ptr);
|
||||
if (allocData) {
|
||||
auto alloc = allocData->gpuAllocations.getDefaultGraphicsAllocation();
|
||||
uint32_t numHandles = alloc->getNumHandles();
|
||||
if (pIpcHandles == nullptr) {
|
||||
*numIpcHandles = numHandles;
|
||||
return ZE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < numHandles; i++) {
|
||||
int handle = static_cast<int>(allocData->gpuAllocations.getDefaultGraphicsAllocation()->peekInternalHandle(this->driverHandle->getMemoryManager(), i));
|
||||
memcpy_s(reinterpret_cast<void *>(pIpcHandles[i].data),
|
||||
sizeof(ze_ipc_mem_handle_t),
|
||||
&handle,
|
||||
sizeof(handle));
|
||||
}
|
||||
|
||||
return ZE_RESULT_SUCCESS;
|
||||
}
|
||||
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
ze_result_t ContextImp::openIpcMemHandle(ze_device_handle_t hDevice,
|
||||
ze_ipc_mem_handle_t pIpcHandle,
|
||||
ze_ipc_memory_flags_t flags,
|
||||
@@ -447,6 +472,29 @@ ze_result_t ContextImp::openIpcMemHandle(ze_device_handle_t hDevice,
|
||||
return ZE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ze_result_t ContextImp::openIpcMemHandles(ze_device_handle_t hDevice,
|
||||
uint32_t numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles,
|
||||
ze_ipc_memory_flags_t flags,
|
||||
void **pptr) {
|
||||
std::vector<NEO::osHandle> handles;
|
||||
for (uint32_t i = 0; i < numIpcHandles; i++) {
|
||||
uint64_t handle = 0;
|
||||
memcpy_s(&handle,
|
||||
sizeof(handle),
|
||||
reinterpret_cast<void *>(pIpcHandles[i].data),
|
||||
sizeof(handle));
|
||||
handles.push_back(static_cast<NEO::osHandle>(handle));
|
||||
}
|
||||
|
||||
*pptr = this->driverHandle->importFdHandles(hDevice, flags, handles, nullptr);
|
||||
if (nullptr == *pptr) {
|
||||
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return ZE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ze_result_t EventPoolImp::closeIpcHandle() {
|
||||
return this->destroy();
|
||||
}
|
||||
|
||||
@@ -57,6 +57,21 @@ struct ContextImp : Context {
|
||||
ze_ipc_mem_handle_t handle,
|
||||
ze_ipc_memory_flags_t flags,
|
||||
void **ptr) override;
|
||||
|
||||
ze_result_t
|
||||
getIpcMemHandles(
|
||||
const void *ptr,
|
||||
uint32_t *numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles) override;
|
||||
|
||||
ze_result_t
|
||||
openIpcMemHandles(
|
||||
ze_device_handle_t hDevice,
|
||||
uint32_t numIpcHandles,
|
||||
ze_ipc_mem_handle_t *pIpcHandles,
|
||||
ze_ipc_memory_flags_t flags,
|
||||
void **pptr) override;
|
||||
|
||||
ze_result_t getMemAllocProperties(const void *ptr,
|
||||
ze_memory_allocation_properties_t *pMemAllocProperties,
|
||||
ze_device_handle_t *phDevice) override;
|
||||
|
||||
@@ -459,6 +459,46 @@ void *DriverHandleImp::importFdHandle(ze_device_handle_t hDevice, ze_ipc_memory_
|
||||
return reinterpret_cast<void *>(alloc->getGpuAddress());
|
||||
}
|
||||
|
||||
void *DriverHandleImp::importFdHandles(ze_device_handle_t hDevice, ze_ipc_memory_flags_t flags, std::vector<NEO::osHandle> handles, NEO::GraphicsAllocation **pAlloc) {
|
||||
auto neoDevice = Device::fromHandle(hDevice)->getNEODevice();
|
||||
NEO::AllocationProperties unifiedMemoryProperties{neoDevice->getRootDeviceIndex(),
|
||||
MemoryConstants::pageSize,
|
||||
NEO::AllocationType::BUFFER,
|
||||
neoDevice->getDeviceBitfield()};
|
||||
unifiedMemoryProperties.subDevicesBitfield = neoDevice->getDeviceBitfield();
|
||||
|
||||
NEO::GraphicsAllocation *alloc =
|
||||
this->getMemoryManager()->createGraphicsAllocationFromMultipleSharedHandles(handles,
|
||||
unifiedMemoryProperties,
|
||||
false,
|
||||
false);
|
||||
if (alloc == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NEO::SvmAllocationData allocData(neoDevice->getRootDeviceIndex());
|
||||
allocData.gpuAllocations.addAllocation(alloc);
|
||||
allocData.cpuAllocation = nullptr;
|
||||
allocData.size = alloc->getUnderlyingBufferSize();
|
||||
allocData.memoryType = InternalMemoryType::DEVICE_UNIFIED_MEMORY;
|
||||
allocData.device = neoDevice;
|
||||
if (flags & ZE_DEVICE_MEM_ALLOC_FLAG_BIAS_UNCACHED) {
|
||||
allocData.allocationFlagsProperty.flags.locallyUncachedResource = 1;
|
||||
}
|
||||
|
||||
if (flags & ZE_IPC_MEMORY_FLAG_BIAS_UNCACHED) {
|
||||
allocData.allocationFlagsProperty.flags.locallyUncachedResource = 1;
|
||||
}
|
||||
|
||||
this->getSvmAllocsManager()->insertSVMAlloc(allocData);
|
||||
|
||||
if (pAlloc) {
|
||||
*pAlloc = alloc;
|
||||
}
|
||||
|
||||
return reinterpret_cast<void *>(alloc->getGpuAddress());
|
||||
}
|
||||
|
||||
NEO::GraphicsAllocation *DriverHandleImp::getPeerAllocation(Device *device,
|
||||
NEO::SvmAllocationData *allocData,
|
||||
void *basePtr,
|
||||
|
||||
@@ -35,6 +35,7 @@ struct DriverHandleImp : public DriverHandle {
|
||||
NEO::MemoryManager *getMemoryManager() override;
|
||||
void setMemoryManager(NEO::MemoryManager *memoryManager) override;
|
||||
MOCKABLE_VIRTUAL void *importFdHandle(ze_device_handle_t hDevice, ze_ipc_memory_flags_t flags, uint64_t handle, NEO::GraphicsAllocation **pAlloc);
|
||||
MOCKABLE_VIRTUAL void *importFdHandles(ze_device_handle_t hDevice, ze_ipc_memory_flags_t flags, std::vector<NEO::osHandle> handles, NEO::GraphicsAllocation **pAlloc);
|
||||
MOCKABLE_VIRTUAL void *importNTHandle(ze_device_handle_t hDevice, void *handle);
|
||||
ze_result_t checkMemoryAccessFromDevice(Device *device, const void *ptr) override;
|
||||
NEO::SVMAllocsManager *getSvmAllocsManager() override;
|
||||
|
||||
@@ -19,6 +19,9 @@ std::unordered_map<std::string, void *> getExtensionFunctionsLookupMap() {
|
||||
addToMap(lookupMap, zexDriverGetHostPointerBaseAddress);
|
||||
|
||||
addToMap(lookupMap, zexKernelGetBaseAddress);
|
||||
|
||||
addToMap(lookupMap, zexMemGetIpcHandles);
|
||||
addToMap(lookupMap, zexMemOpenIpcHandles);
|
||||
#undef addToMap
|
||||
|
||||
return lookupMap;
|
||||
|
||||
@@ -18,65 +18,59 @@ int sv[CHILDPROCESSES][2];
|
||||
extern bool verbose;
|
||||
bool verbose = false;
|
||||
|
||||
size_t allocSize = 4096 + 7; // +7 to break alignment and make it harder
|
||||
size_t allocSize = 131072 + 7; // +7 to break alignment and make it harder
|
||||
|
||||
static int sendmsg_fd(int socket, int fd) {
|
||||
char sendBuf[sizeof(ze_ipc_mem_handle_t)] = {};
|
||||
char cmsgBuf[CMSG_SPACE(sizeof(ze_ipc_mem_handle_t))];
|
||||
static int sendmsgForIpcHandle(int socket, int fd, char *payload) {
|
||||
char sendBuf[ZE_MAX_IPC_HANDLE_SIZE] = {};
|
||||
memcpy(sendBuf, payload, sizeof(sendBuf));
|
||||
char cmsgBuf[CMSG_SPACE(ZE_MAX_IPC_HANDLE_SIZE)];
|
||||
|
||||
struct iovec msgBuffer;
|
||||
struct iovec msgBuffer = {};
|
||||
msgBuffer.iov_base = sendBuf;
|
||||
msgBuffer.iov_len = sizeof(*sendBuf);
|
||||
msgBuffer.iov_len = ZE_MAX_IPC_HANDLE_SIZE;
|
||||
|
||||
struct msghdr msgHeader = {};
|
||||
msgHeader.msg_iov = &msgBuffer;
|
||||
msgHeader.msg_iovlen = 1;
|
||||
msgHeader.msg_control = cmsgBuf;
|
||||
msgHeader.msg_controllen = CMSG_LEN(sizeof(fd));
|
||||
|
||||
struct cmsghdr *controlHeader = CMSG_FIRSTHDR(&msgHeader);
|
||||
controlHeader->cmsg_type = SCM_RIGHTS;
|
||||
controlHeader->cmsg_level = SOL_SOCKET;
|
||||
controlHeader->cmsg_len = CMSG_LEN(sizeof(fd));
|
||||
|
||||
*(int *)CMSG_DATA(controlHeader) = fd;
|
||||
ssize_t bytesSent = sendmsg(socket, &msgHeader, 0);
|
||||
if (bytesSent < 0) {
|
||||
std::cerr << "Error on sendmsgForIpcHandle " << strerror(errno) << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recvmsg_fd(int socket) {
|
||||
static int recvmsgForIpcHandle(int socket, char *payload) {
|
||||
int fd = -1;
|
||||
char recvBuf[sizeof(ze_ipc_mem_handle_t)] = {};
|
||||
char cmsgBuf[CMSG_SPACE(sizeof(ze_ipc_mem_handle_t))];
|
||||
|
||||
char recvBuf[ZE_MAX_IPC_HANDLE_SIZE] = {};
|
||||
char cmsgBuf[CMSG_SPACE(ZE_MAX_IPC_HANDLE_SIZE)];
|
||||
struct iovec msgBuffer;
|
||||
msgBuffer.iov_base = recvBuf;
|
||||
msgBuffer.iov_len = sizeof(recvBuf);
|
||||
|
||||
msgBuffer.iov_len = ZE_MAX_IPC_HANDLE_SIZE;
|
||||
struct msghdr msgHeader = {};
|
||||
msgHeader.msg_iov = &msgBuffer;
|
||||
msgHeader.msg_iovlen = 1;
|
||||
msgHeader.msg_control = cmsgBuf;
|
||||
msgHeader.msg_controllen = CMSG_LEN(sizeof(fd));
|
||||
|
||||
ssize_t bytesSent = recvmsg(socket, &msgHeader, 0);
|
||||
if (bytesSent < 0) {
|
||||
std::cerr << "Error on recvmsgForIpcHandle " << strerror(errno) << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct cmsghdr *controlHeader = CMSG_FIRSTHDR(&msgHeader);
|
||||
if (!CMSG_DATA(controlHeader)) {
|
||||
return -1;
|
||||
}
|
||||
memmove(&fd, CMSG_DATA(controlHeader), sizeof(int));
|
||||
memmove(payload, recvBuf, sizeof(recvBuf));
|
||||
return fd;
|
||||
}
|
||||
|
||||
inline void initializeProcess(ze_context_handle_t &context,
|
||||
inline void initializeProcess(ze_driver_handle_t &driverHandle,
|
||||
ze_context_handle_t &context,
|
||||
ze_device_handle_t &device,
|
||||
ze_command_queue_handle_t &cmdQueue,
|
||||
ze_command_list_handle_t &cmdList) {
|
||||
@@ -86,7 +80,6 @@ inline void initializeProcess(ze_context_handle_t &context,
|
||||
uint32_t driverCount = 0;
|
||||
SUCCESS_OR_TERMINATE(zeDriverGet(&driverCount, nullptr));
|
||||
|
||||
ze_driver_handle_t driverHandle;
|
||||
SUCCESS_OR_TERMINATE(zeDriverGet(&driverCount, &driverHandle));
|
||||
|
||||
ze_context_desc_t contextDesc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC};
|
||||
@@ -137,33 +130,63 @@ inline void initializeProcess(ze_context_handle_t &context,
|
||||
SUCCESS_OR_TERMINATE(zeCommandListCreate(context, device, &cmdListDesc, &cmdList));
|
||||
}
|
||||
|
||||
typedef ze_result_t (*pFnzexMemGetIpcHandle)(ze_context_handle_t, const void *, uint32_t *, ze_ipc_mem_handle_t *);
|
||||
typedef ze_result_t (*pFnzexMemOpenIpcHandle)(ze_context_handle_t, ze_device_handle_t, uint32_t, ze_ipc_mem_handle_t *, ze_ipc_memory_flags_t, void **);
|
||||
|
||||
void run_client(int commSocket, uint32_t clientId) {
|
||||
std::cout << "Client " << clientId << ", process ID: " << std::dec << getpid() << "\n";
|
||||
|
||||
ze_driver_handle_t driverHandle;
|
||||
ze_context_handle_t context;
|
||||
ze_device_handle_t device;
|
||||
ze_command_queue_handle_t cmdQueue;
|
||||
ze_command_list_handle_t cmdList;
|
||||
initializeProcess(context, device, cmdQueue, cmdList);
|
||||
initializeProcess(driverHandle, context, device, cmdQueue, cmdList);
|
||||
|
||||
char *heapBuffer = new char[allocSize];
|
||||
for (size_t i = 0; i < allocSize; ++i) {
|
||||
heapBuffer[i] = static_cast<char>(i + 1);
|
||||
}
|
||||
|
||||
// get the dma_buf from the other process
|
||||
int dma_buf_fd = recvmsg_fd(commSocket);
|
||||
if (dma_buf_fd < 0) {
|
||||
std::cerr << "Failing to get dma_buf fd from server\n";
|
||||
pFnzexMemOpenIpcHandle zexMemOpenIpcHandlePointer = nullptr;
|
||||
SUCCESS_OR_TERMINATE(zeDriverGetExtensionFunctionAddress(driverHandle, "zexMemOpenIpcHandles", reinterpret_cast<void **>(&zexMemOpenIpcHandlePointer)));
|
||||
|
||||
// receive the IPC handle for the memory from the other process
|
||||
std::vector<ze_ipc_mem_handle_t> pIpcHandles;
|
||||
|
||||
char payload[ZE_MAX_IPC_HANDLE_SIZE];
|
||||
int handle = recvmsgForIpcHandle(commSocket, payload);
|
||||
if (handle < 0) {
|
||||
std::cerr << "Failing to get IPC memory handle from server\n";
|
||||
std::terminate();
|
||||
}
|
||||
ze_ipc_mem_handle_t pIpcHandle;
|
||||
memcpy(&pIpcHandle, static_cast<void *>(&dma_buf_fd), sizeof(dma_buf_fd));
|
||||
|
||||
// get a memory pointer to the BO associated with the dma_buf
|
||||
// check for the number of ipc (dma-buf) handles sent in the payload
|
||||
uint32_t numIpcHandles = 0;
|
||||
memcpy(&numIpcHandles, static_cast<void *>(&payload), sizeof(numIpcHandles));
|
||||
if (numIpcHandles == 0) {
|
||||
std::cerr << "numIpcHandles is zero\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
// get first handle
|
||||
pIpcHandles.resize(numIpcHandles);
|
||||
memcpy(pIpcHandles[0].data, static_cast<void *>(&handle), sizeof(handle));
|
||||
|
||||
// get rest of handles
|
||||
for (uint32_t h = 1; h < numIpcHandles; h++) {
|
||||
int handle = recvmsgForIpcHandle(commSocket, payload);
|
||||
if (handle < 0) {
|
||||
std::cerr << "Failing to get IPC memory handle from server\n";
|
||||
std::terminate();
|
||||
}
|
||||
memcpy(pIpcHandles[h].data, static_cast<void *>(&handle), sizeof(handle));
|
||||
}
|
||||
|
||||
// get a memory pointer to the BO associated with the dma_buf handles
|
||||
void *zeIpcBuffer;
|
||||
SUCCESS_OR_TERMINATE(zeMemOpenIpcHandle(context, device, pIpcHandle,
|
||||
0u, &zeIpcBuffer));
|
||||
SUCCESS_OR_TERMINATE(zexMemOpenIpcHandlePointer(context, device, numIpcHandles, pIpcHandles.data(), 0u, &zeIpcBuffer));
|
||||
|
||||
// Copy from heap to IPC buffer memory
|
||||
SUCCESS_OR_TERMINATE(zeCommandListAppendMemoryCopy(cmdList, zeIpcBuffer, heapBuffer, allocSize,
|
||||
nullptr, 0, nullptr));
|
||||
@@ -182,11 +205,12 @@ void run_client(int commSocket, uint32_t clientId) {
|
||||
void run_server(bool &validRet) {
|
||||
std::cout << "Server process ID " << std::dec << getpid() << "\n";
|
||||
|
||||
ze_driver_handle_t driverHandle;
|
||||
ze_context_handle_t context;
|
||||
ze_device_handle_t device;
|
||||
ze_command_queue_handle_t cmdQueue;
|
||||
ze_command_list_handle_t cmdList;
|
||||
initializeProcess(context, device, cmdQueue, cmdList);
|
||||
initializeProcess(driverHandle, context, device, cmdQueue, cmdList);
|
||||
|
||||
void *zeBuffer = nullptr;
|
||||
ze_device_mem_alloc_desc_t deviceDesc = {ZE_STRUCTURE_TYPE_DEVICE_MEM_ALLOC_DESC};
|
||||
@@ -203,19 +227,39 @@ void run_server(bool &validRet) {
|
||||
SUCCESS_OR_TERMINATE(zeCommandQueueSynchronize(cmdQueue, std::numeric_limits<uint64_t>::max()));
|
||||
SUCCESS_OR_TERMINATE(zeCommandListReset(cmdList));
|
||||
|
||||
// Get a dma_buf for the previously allocated pointer
|
||||
ze_ipc_mem_handle_t pIpcHandle;
|
||||
SUCCESS_OR_TERMINATE(zeMemGetIpcHandle(context, zeBuffer, &pIpcHandle));
|
||||
// Get the IPC handles for the previously allocated pointer
|
||||
pFnzexMemGetIpcHandle zexMemGetIpcHandlePointer = nullptr;
|
||||
SUCCESS_OR_TERMINATE(zeDriverGetExtensionFunctionAddress(driverHandle, "zexMemGetIpcHandles", reinterpret_cast<void **>(&zexMemGetIpcHandlePointer)));
|
||||
|
||||
// Pass the dma_buf to the other process
|
||||
int dma_buf_fd;
|
||||
memcpy(static_cast<void *>(&dma_buf_fd), &pIpcHandle, sizeof(dma_buf_fd));
|
||||
int commSocket = sv[i][0];
|
||||
if (sendmsg_fd(commSocket, static_cast<int>(dma_buf_fd)) < 0) {
|
||||
std::cerr << "Failing to send dma_buf fd to client\n";
|
||||
std::vector<ze_ipc_mem_handle_t> pIpcHandles;
|
||||
|
||||
// get the number of ipc handles associated with the allocation
|
||||
uint32_t numIpcHandles = 0;
|
||||
SUCCESS_OR_TERMINATE(zexMemGetIpcHandlePointer(context, zeBuffer, &numIpcHandles, nullptr));
|
||||
if (numIpcHandles == 0) {
|
||||
std::cerr << "numIpcHandles is zero\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
// get the handles
|
||||
pIpcHandles.resize(numIpcHandles);
|
||||
SUCCESS_OR_TERMINATE(zexMemGetIpcHandlePointer(context, zeBuffer, &numIpcHandles, pIpcHandles.data()));
|
||||
|
||||
// copy the number of ipc handles to the payload, then transmit each handle to the client
|
||||
char payload[ZE_MAX_IPC_HANDLE_SIZE];
|
||||
memcpy(static_cast<void *>(&payload), &numIpcHandles, sizeof(numIpcHandles));
|
||||
for (uint32_t h = 0; h < numIpcHandles; h++) {
|
||||
int commSocket = sv[i][0];
|
||||
int handle = 0;
|
||||
memcpy(static_cast<void *>(&handle), &pIpcHandles[h].data, sizeof(handle));
|
||||
|
||||
int ret = sendmsgForIpcHandle(commSocket, handle, payload);
|
||||
if (ret < 0) {
|
||||
std::cerr << "Failing to send IPC memory handle to client\n";
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
char *heapBuffer = new char[allocSize];
|
||||
for (size_t i = 0; i < allocSize; ++i) {
|
||||
heapBuffer[i] = static_cast<char>(i + 1);
|
||||
@@ -248,6 +292,14 @@ void run_server(bool &validRet) {
|
||||
SUCCESS_OR_TERMINATE(zeCommandQueueExecuteCommandLists(cmdQueue, 1, &cmdList, nullptr));
|
||||
SUCCESS_OR_TERMINATE(zeCommandQueueSynchronize(cmdQueue, std::numeric_limits<uint64_t>::max()));
|
||||
|
||||
char *ipcBuffer = static_cast<char *>(validateBuffer);
|
||||
for (uint32_t h = 0; h < allocSize; h++) {
|
||||
if (static_cast<uint32_t>(ipcBuffer[h]) != static_cast<uint32_t>(heapBuffer[h])) {
|
||||
printf("Error: ipcBuffer %d %d, heapBuffer %d\n", h, static_cast<uint32_t>(ipcBuffer[h]), heapBuffer[h]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate stack and buffers have the original data from heapBuffer
|
||||
validRet = (0 == memcmp(heapBuffer, validateBuffer, allocSize));
|
||||
delete[] heapBuffer;
|
||||
|
||||
@@ -384,6 +384,7 @@ struct ContextGetIpcHandleMock : public L0::ContextImp {
|
||||
class MemoryManagerIpcMock : public NEO::MemoryManager {
|
||||
public:
|
||||
MemoryManagerIpcMock(NEO::ExecutionEnvironment &executionEnvironment) : NEO::MemoryManager(executionEnvironment) {}
|
||||
NEO::GraphicsAllocation *createGraphicsAllocationFromMultipleSharedHandles(std::vector<osHandle> handles, AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation) override { return nullptr; }
|
||||
NEO::GraphicsAllocation *createGraphicsAllocationFromSharedHandle(osHandle handle, const AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation) override { return nullptr; }
|
||||
void addAllocationToHostPtrManager(NEO::GraphicsAllocation *memory) override{};
|
||||
void removeAllocationFromHostPtrManager(NEO::GraphicsAllocation *memory) override{};
|
||||
@@ -414,20 +415,63 @@ class MemoryManagerIpcMock : public NEO::MemoryManager {
|
||||
void unlockResourceImpl(NEO::GraphicsAllocation &graphicsAllocation) override{};
|
||||
};
|
||||
|
||||
class IpcImplicitScalingMockGraphicsAllocation : public NEO::MemoryAllocation {
|
||||
public:
|
||||
using MemoryAllocation::allocationOffset;
|
||||
using MemoryAllocation::allocationType;
|
||||
using MemoryAllocation::aubInfo;
|
||||
using MemoryAllocation::gpuAddress;
|
||||
using MemoryAllocation::MemoryAllocation;
|
||||
using MemoryAllocation::memoryPool;
|
||||
using MemoryAllocation::objectNotResident;
|
||||
using MemoryAllocation::objectNotUsed;
|
||||
using MemoryAllocation::size;
|
||||
using MemoryAllocation::usageInfos;
|
||||
|
||||
IpcImplicitScalingMockGraphicsAllocation()
|
||||
: NEO::MemoryAllocation(0, AllocationType::UNKNOWN, nullptr, 0u, 0, MemoryPool::MemoryNull, MemoryManager::maxOsContextCount) {}
|
||||
|
||||
IpcImplicitScalingMockGraphicsAllocation(void *buffer, size_t sizeIn)
|
||||
: NEO::MemoryAllocation(0, AllocationType::UNKNOWN, buffer, castToUint64(buffer), 0llu, sizeIn, MemoryPool::MemoryNull, MemoryManager::maxOsContextCount) {}
|
||||
|
||||
IpcImplicitScalingMockGraphicsAllocation(void *buffer, uint64_t gpuAddr, size_t sizeIn)
|
||||
: NEO::MemoryAllocation(0, AllocationType::UNKNOWN, buffer, gpuAddr, 0llu, sizeIn, MemoryPool::MemoryNull, MemoryManager::maxOsContextCount) {}
|
||||
|
||||
IpcImplicitScalingMockGraphicsAllocation(uint32_t rootDeviceIndex, void *buffer, size_t sizeIn)
|
||||
: NEO::MemoryAllocation(rootDeviceIndex, AllocationType::UNKNOWN, buffer, castToUint64(buffer), 0llu, sizeIn, MemoryPool::MemoryNull, MemoryManager::maxOsContextCount) {}
|
||||
|
||||
uint32_t getNumHandles() override {
|
||||
return 2u;
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryManagerOpenIpcMock : public MemoryManagerIpcMock {
|
||||
public:
|
||||
MemoryManagerOpenIpcMock(NEO::ExecutionEnvironment &executionEnvironment) : MemoryManagerIpcMock(executionEnvironment) {}
|
||||
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryWithProperties(const AllocationProperties &properties) override {
|
||||
auto alloc = new IpcImplicitScalingMockGraphicsAllocation(0,
|
||||
NEO::AllocationType::BUFFER,
|
||||
reinterpret_cast<void *>(sharedHandleAddress++),
|
||||
0x1000,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
MemoryPool::System4KBPages);
|
||||
alloc->setGpuBaseAddress(0xabcd);
|
||||
return alloc;
|
||||
}
|
||||
|
||||
NEO::GraphicsAllocation *createGraphicsAllocationFromSharedHandle(osHandle handle, const AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation) override {
|
||||
if (failOnCreateGraphicsAllocationFromSharedHandle) {
|
||||
return nullptr;
|
||||
}
|
||||
auto alloc = new NEO::MockGraphicsAllocation(0,
|
||||
NEO::AllocationType::BUFFER,
|
||||
reinterpret_cast<void *>(sharedHandleAddress++),
|
||||
0x1000,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
MemoryPool::System4KBPages);
|
||||
auto alloc = new IpcImplicitScalingMockGraphicsAllocation(0,
|
||||
NEO::AllocationType::BUFFER,
|
||||
reinterpret_cast<void *>(sharedHandleAddress++),
|
||||
0x1000,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
MemoryPool::System4KBPages);
|
||||
alloc->setGpuBaseAddress(0xabcd);
|
||||
return alloc;
|
||||
}
|
||||
@@ -502,5 +546,136 @@ struct MemoryOpenIpcHandleTest : public ::testing::Test {
|
||||
std::unique_ptr<ContextIpcMock> context;
|
||||
};
|
||||
|
||||
class MemoryManagerIpcImplicitScalingMock : public NEO::MemoryManager {
|
||||
public:
|
||||
MemoryManagerIpcImplicitScalingMock(NEO::ExecutionEnvironment &executionEnvironment) : NEO::MemoryManager(executionEnvironment) {}
|
||||
|
||||
NEO::GraphicsAllocation *createGraphicsAllocationFromSharedHandle(osHandle handle, const AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation) override { return nullptr; }
|
||||
void addAllocationToHostPtrManager(NEO::GraphicsAllocation *memory) override{};
|
||||
void removeAllocationFromHostPtrManager(NEO::GraphicsAllocation *memory) override{};
|
||||
NEO::GraphicsAllocation *createGraphicsAllocationFromNTHandle(void *handle, uint32_t rootDeviceIndex, AllocationType allocType) override { return nullptr; };
|
||||
AllocationStatus populateOsHandles(NEO::OsHandleStorage &handleStorage, uint32_t rootDeviceIndex) override { return AllocationStatus::Success; };
|
||||
void cleanOsHandles(NEO::OsHandleStorage &handleStorage, uint32_t rootDeviceIndex) override{};
|
||||
void freeGraphicsMemoryImpl(NEO::GraphicsAllocation *gfxAllocation) override{};
|
||||
void freeGraphicsMemoryImpl(GraphicsAllocation *gfxAllocation, bool isImportedAllocation) override{};
|
||||
uint64_t getSystemSharedMemory(uint32_t rootDeviceIndex) override { return 0; };
|
||||
uint64_t getLocalMemorySize(uint32_t rootDeviceIndex, uint32_t deviceBitfield) override { return 0; };
|
||||
double getPercentOfGlobalMemoryAvailable(uint32_t rootDeviceIndex) override { return 0; }
|
||||
AddressRange reserveGpuAddress(size_t size, uint32_t rootDeviceIndex) override {
|
||||
return {};
|
||||
}
|
||||
void freeGpuAddress(AddressRange addressRange, uint32_t rootDeviceIndex) override{};
|
||||
NEO::GraphicsAllocation *createGraphicsAllocation(OsHandleStorage &handleStorage, const NEO::AllocationData &allocationData) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryForNonSvmHostPtr(const NEO::AllocationData &allocationData) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryWithAlignment(const NEO::AllocationData &allocationData) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocateUSMHostGraphicsMemory(const NEO::AllocationData &allocationData) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemory64kb(const NEO::AllocationData &allocationData) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocate32BitGraphicsMemoryImpl(const NEO::AllocationData &allocationData, bool useLocalMemory) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryInDevicePool(const NEO::AllocationData &allocationData, AllocationStatus &status) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryWithGpuVa(const NEO::AllocationData &allocationData) override { return nullptr; };
|
||||
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryForImageImpl(const NEO::AllocationData &allocationData, std::unique_ptr<Gmm> gmm) override { return nullptr; };
|
||||
NEO::GraphicsAllocation *allocateMemoryByKMD(const NEO::AllocationData &allocationData) override { return nullptr; };
|
||||
void *lockResourceImpl(NEO::GraphicsAllocation &graphicsAllocation) override { return nullptr; };
|
||||
void unlockResourceImpl(NEO::GraphicsAllocation &graphicsAllocation) override{};
|
||||
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryInPreferredPool(const AllocationProperties &properties, const void *hostPtr) override {
|
||||
auto alloc = new IpcImplicitScalingMockGraphicsAllocation(0,
|
||||
NEO::AllocationType::BUFFER,
|
||||
reinterpret_cast<void *>(sharedHandleAddress++),
|
||||
0x1000,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
MemoryPool::System4KBPages);
|
||||
alloc->setGpuBaseAddress(0xabcd);
|
||||
return alloc;
|
||||
}
|
||||
|
||||
NEO::GraphicsAllocation *allocateGraphicsMemoryWithProperties(const AllocationProperties &properties) override {
|
||||
auto alloc = new IpcImplicitScalingMockGraphicsAllocation(0,
|
||||
NEO::AllocationType::BUFFER,
|
||||
reinterpret_cast<void *>(sharedHandleAddress++),
|
||||
0x1000,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
MemoryPool::System4KBPages);
|
||||
alloc->setGpuBaseAddress(0xabcd);
|
||||
return alloc;
|
||||
}
|
||||
|
||||
NEO::GraphicsAllocation *createGraphicsAllocationFromMultipleSharedHandles(std::vector<osHandle> handles, AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation) override {
|
||||
if (failOnCreateGraphicsAllocationFromSharedHandle) {
|
||||
return nullptr;
|
||||
}
|
||||
auto alloc = new IpcImplicitScalingMockGraphicsAllocation(0,
|
||||
NEO::AllocationType::BUFFER,
|
||||
reinterpret_cast<void *>(sharedHandleAddress++),
|
||||
0x1000,
|
||||
0,
|
||||
sizeof(uint32_t),
|
||||
MemoryPool::System4KBPages);
|
||||
alloc->setGpuBaseAddress(0xabcd);
|
||||
return alloc;
|
||||
}
|
||||
|
||||
void freeGraphicsMemory(NEO::GraphicsAllocation *alloc, bool isImportedAllocation) override {
|
||||
delete alloc;
|
||||
}
|
||||
|
||||
uint64_t sharedHandleAddress = 0x1234;
|
||||
|
||||
bool failOnCreateGraphicsAllocationFromSharedHandle = false;
|
||||
};
|
||||
|
||||
struct MemoryExportImportImplicitScalingTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
DebugManagerStateRestore restorer;
|
||||
DebugManager.flags.EnableImplicitScaling.set(1);
|
||||
|
||||
NEO::MockCompilerEnableGuard mock(true);
|
||||
neoDevice =
|
||||
NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get());
|
||||
auto mockBuiltIns = new MockBuiltins();
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->builtins.reset(mockBuiltIns);
|
||||
NEO::DeviceVector devices;
|
||||
devices.push_back(std::unique_ptr<NEO::Device>(neoDevice));
|
||||
driverHandle = std::make_unique<DriverHandleImp>();
|
||||
driverHandle->initialize(std::move(devices));
|
||||
prevMemoryManager = driverHandle->getMemoryManager();
|
||||
currMemoryManager = new MemoryManagerIpcImplicitScalingMock(*neoDevice->executionEnvironment);
|
||||
driverHandle->setMemoryManager(currMemoryManager);
|
||||
|
||||
prevSvmAllocsManager = driverHandle->svmAllocsManager;
|
||||
currSvmAllocsManager = new NEO::SVMAllocsManager(currMemoryManager, false);
|
||||
driverHandle->svmAllocsManager = currSvmAllocsManager;
|
||||
|
||||
device = driverHandle->devices[0];
|
||||
|
||||
context = std::make_unique<ContextIpcMock>(driverHandle.get());
|
||||
EXPECT_NE(context, nullptr);
|
||||
context->getDevices().insert(std::make_pair(device->toHandle(), device));
|
||||
auto neoDevice = device->getNEODevice();
|
||||
context->rootDeviceIndices.push_back(neoDevice->getRootDeviceIndex());
|
||||
context->deviceBitfields.insert({neoDevice->getRootDeviceIndex(), neoDevice->getDeviceBitfield()});
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
driverHandle->svmAllocsManager = prevSvmAllocsManager;
|
||||
delete currSvmAllocsManager;
|
||||
driverHandle->setMemoryManager(prevMemoryManager);
|
||||
delete currMemoryManager;
|
||||
}
|
||||
|
||||
NEO::SVMAllocsManager *prevSvmAllocsManager;
|
||||
NEO::SVMAllocsManager *currSvmAllocsManager;
|
||||
|
||||
NEO::MemoryManager *prevMemoryManager = nullptr;
|
||||
MemoryManagerIpcImplicitScalingMock *currMemoryManager = nullptr;
|
||||
std::unique_ptr<DriverHandleImp> driverHandle;
|
||||
NEO::MockDevice *neoDevice = nullptr;
|
||||
L0::Device *device = nullptr;
|
||||
std::unique_ptr<ContextIpcMock> context;
|
||||
};
|
||||
|
||||
} // namespace ult
|
||||
} // namespace L0
|
||||
|
||||
@@ -205,6 +205,45 @@ HWTEST_F(ImportNTHandle, givenNTHandleWhenCreatingDeviceMemoryThenSuccessIsRetur
|
||||
delete device;
|
||||
}
|
||||
|
||||
HWTEST_F(ImportNTHandle, whenCallingCreateGraphicsAllocationFromMultipleSharedHandlesFromOsAgnosticMemoryManagerThenNullptrIsReturned) {
|
||||
using RENDER_SURFACE_STATE = typename FamilyType::RENDER_SURFACE_STATE;
|
||||
|
||||
ze_device_mem_alloc_desc_t devProperties = {};
|
||||
devProperties.stype = ZE_STRUCTURE_TYPE_DEVICE_MEMORY_PROPERTIES;
|
||||
|
||||
uint64_t imageHandle = 0x1;
|
||||
ze_external_memory_import_win32_handle_t importNTHandle = {};
|
||||
importNTHandle.handle = &imageHandle;
|
||||
importNTHandle.flags = ZE_EXTERNAL_MEMORY_TYPE_FLAG_OPAQUE_WIN32;
|
||||
importNTHandle.stype = ZE_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMPORT_WIN32;
|
||||
devProperties.pNext = &importNTHandle;
|
||||
|
||||
NEO::MockDevice *neoDevice = nullptr;
|
||||
auto executionEnvironment = NEO::MockDevice::prepareExecutionEnvironment(NEO::defaultHwInfo.get(), 0);
|
||||
executionEnvironment->memoryManager.reset(new MemoryManagerNTHandleMock(*executionEnvironment));
|
||||
|
||||
neoDevice = NEO::MockDevice::createWithExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get(), executionEnvironment, 0);
|
||||
|
||||
driverHandle->setMemoryManager(executionEnvironment->memoryManager.get());
|
||||
|
||||
ze_result_t result = ZE_RESULT_SUCCESS;
|
||||
auto device = L0::Device::create(driverHandle.get(), neoDevice, false, &result);
|
||||
|
||||
std::vector<osHandle> handles{6, 7};
|
||||
AllocationProperties properties = {device->getRootDeviceIndex(),
|
||||
true,
|
||||
MemoryConstants::pageSize,
|
||||
AllocationType::BUFFER,
|
||||
false,
|
||||
device->getNEODevice()->getDeviceBitfield()};
|
||||
bool requireSpecificBitness{};
|
||||
bool isHostIpcAllocation{};
|
||||
auto ptr = executionEnvironment->memoryManager->createGraphicsAllocationFromMultipleSharedHandles(handles, properties, requireSpecificBitness, isHostIpcAllocation);
|
||||
EXPECT_EQ(nullptr, ptr);
|
||||
|
||||
delete device;
|
||||
}
|
||||
|
||||
HWTEST_F(ImportNTHandle, givenNotExistingNTHandleWhenCreatingDeviceMemoryThenErrorIsReturned) {
|
||||
using RENDER_SURFACE_STATE = typename FamilyType::RENDER_SURFACE_STATE;
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ class MemoryManagerEventPoolFailMock : public NEO::MemoryManager {
|
||||
void *createMultiGraphicsAllocationInSystemMemoryPool(RootDeviceIndicesContainer &rootDeviceIndices, AllocationProperties &properties, NEO::MultiGraphicsAllocation &multiGraphicsAllocation) override {
|
||||
return nullptr;
|
||||
};
|
||||
GraphicsAllocation *createGraphicsAllocationFromMultipleSharedHandles(std::vector<osHandle> handles, AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation) override { return nullptr; }
|
||||
NEO::GraphicsAllocation *createGraphicsAllocationFromSharedHandle(osHandle handle, const AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation) override { return nullptr; }
|
||||
void addAllocationToHostPtrManager(NEO::GraphicsAllocation *memory) override{};
|
||||
void removeAllocationFromHostPtrManager(NEO::GraphicsAllocation *memory) override{};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "level_zero/core/source/cmdlist/cmdlist_hw.h"
|
||||
#include "level_zero/core/source/context/context_imp.h"
|
||||
#include "level_zero/core/source/device/device_imp.h"
|
||||
#include "level_zero/core/source/driver/driver_handle_imp.h"
|
||||
#include "level_zero/core/source/driver/host_pointer_manager.h"
|
||||
#include "level_zero/core/source/hw_helpers/l0_hw_helper.h"
|
||||
#include "level_zero/core/source/image/image.h"
|
||||
@@ -35,6 +36,215 @@
|
||||
namespace L0 {
|
||||
namespace ult {
|
||||
|
||||
TEST_F(MemoryExportImportImplicitScalingTest,
|
||||
givenCallToGetIpcHandleWithNotKnownPointerThenInvalidArgumentIsReturned) {
|
||||
|
||||
uint32_t value = 0;
|
||||
|
||||
uint32_t numIpcHandles = 0;
|
||||
ze_result_t result = context->getIpcMemHandles(&value, &numIpcHandles, nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryExportImportImplicitScalingTest,
|
||||
givenCallToGetIpcHandleWithDeviceAllocationThenIpcHandleIsReturned) {
|
||||
size_t size = 10;
|
||||
size_t alignment = 1u;
|
||||
void *ptr = nullptr;
|
||||
|
||||
ze_device_mem_alloc_desc_t deviceDesc = {};
|
||||
ze_result_t result = context->allocDeviceMem(device->toHandle(),
|
||||
&deviceDesc,
|
||||
size, alignment, &ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_NE(nullptr, ptr);
|
||||
|
||||
uint32_t numIpcHandles = 0;
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_EQ(numIpcHandles, 2u);
|
||||
|
||||
std::vector<ze_ipc_mem_handle_t> ipcHandles(numIpcHandles);
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, ipcHandles.data());
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
result = context->freeMem(ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryExportImportImplicitScalingTest,
|
||||
whenCallingOpenIpcHandlesWithIpcHandleThenDeviceAllocationIsReturned) {
|
||||
size_t size = 10;
|
||||
size_t alignment = 1u;
|
||||
void *ptr = nullptr;
|
||||
|
||||
ze_device_mem_alloc_desc_t deviceDesc = {};
|
||||
ze_result_t result = context->allocDeviceMem(device->toHandle(),
|
||||
&deviceDesc,
|
||||
size, alignment, &ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_NE(nullptr, ptr);
|
||||
|
||||
uint32_t numIpcHandles = 0;
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_EQ(numIpcHandles, 2u);
|
||||
|
||||
std::vector<ze_ipc_mem_handle_t> ipcHandles(numIpcHandles);
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, ipcHandles.data());
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<MockDriverModelDRM>(512));
|
||||
|
||||
ze_ipc_memory_flags_t flags = {};
|
||||
void *ipcPtr;
|
||||
result = context->openIpcMemHandles(device->toHandle(), numIpcHandles, ipcHandles.data(), flags, &ipcPtr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
result = context->closeIpcMemHandle(ipcPtr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
result = context->freeMem(ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryExportImportImplicitScalingTest,
|
||||
whenCallingImportFdHandlesWithAllocationPointerThenAllocationIsReturned) {
|
||||
size_t size = 10;
|
||||
size_t alignment = 1u;
|
||||
void *ptr = nullptr;
|
||||
|
||||
ze_device_mem_alloc_desc_t deviceDesc = {};
|
||||
ze_result_t result = context->allocDeviceMem(device->toHandle(),
|
||||
&deviceDesc,
|
||||
size, alignment, &ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_NE(nullptr, ptr);
|
||||
|
||||
uint32_t numIpcHandles = 0;
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_EQ(numIpcHandles, 2u);
|
||||
|
||||
std::vector<ze_ipc_mem_handle_t> ipcHandles(numIpcHandles);
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, ipcHandles.data());
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<MockDriverModelDRM>(512));
|
||||
|
||||
std::vector<NEO::osHandle> handles;
|
||||
for (uint32_t i = 0; i < numIpcHandles; i++) {
|
||||
uint64_t handle = 0;
|
||||
memcpy_s(&handle,
|
||||
sizeof(handle),
|
||||
reinterpret_cast<void *>(ipcHandles[i].data),
|
||||
sizeof(handle));
|
||||
handles.push_back(static_cast<NEO::osHandle>(handle));
|
||||
}
|
||||
|
||||
ze_ipc_memory_flags_t flags = {};
|
||||
void *ipcPtr;
|
||||
NEO::GraphicsAllocation *ipcAlloc = nullptr;
|
||||
DriverHandleImp *driverHandleImp = static_cast<DriverHandleImp *>(context->getDriverHandle());
|
||||
ipcPtr = driverHandleImp->importFdHandles(device->toHandle(), flags, handles, &ipcAlloc);
|
||||
EXPECT_NE(ipcPtr, nullptr);
|
||||
EXPECT_NE(ipcAlloc, nullptr);
|
||||
|
||||
result = context->closeIpcMemHandle(ipcPtr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
result = context->freeMem(ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryExportImportImplicitScalingTest,
|
||||
whenCallingImportFdHandlesWithUncachedFlagAllocationPointerThenAllocationIsReturned) {
|
||||
size_t size = 10;
|
||||
size_t alignment = 1u;
|
||||
void *ptr = nullptr;
|
||||
|
||||
ze_device_mem_alloc_desc_t deviceDesc = {};
|
||||
ze_result_t result = context->allocDeviceMem(device->toHandle(),
|
||||
&deviceDesc,
|
||||
size, alignment, &ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_NE(nullptr, ptr);
|
||||
|
||||
uint32_t numIpcHandles = 0;
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_EQ(numIpcHandles, 2u);
|
||||
|
||||
std::vector<ze_ipc_mem_handle_t> ipcHandles(numIpcHandles);
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, ipcHandles.data());
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<MockDriverModelDRM>(512));
|
||||
|
||||
std::vector<NEO::osHandle> handles;
|
||||
for (uint32_t i = 0; i < numIpcHandles; i++) {
|
||||
uint64_t handle = 0;
|
||||
memcpy_s(&handle,
|
||||
sizeof(handle),
|
||||
reinterpret_cast<void *>(ipcHandles[i].data),
|
||||
sizeof(handle));
|
||||
handles.push_back(static_cast<NEO::osHandle>(handle));
|
||||
}
|
||||
|
||||
ze_ipc_memory_flags_t flags = {ZE_IPC_MEMORY_FLAG_BIAS_UNCACHED};
|
||||
void *ipcPtr;
|
||||
NEO::GraphicsAllocation *ipcAlloc = nullptr;
|
||||
DriverHandleImp *driverHandleImp = static_cast<DriverHandleImp *>(context->getDriverHandle());
|
||||
ipcPtr = driverHandleImp->importFdHandles(device->toHandle(), flags, handles, &ipcAlloc);
|
||||
EXPECT_NE(ipcPtr, nullptr);
|
||||
EXPECT_NE(ipcAlloc, nullptr);
|
||||
|
||||
result = context->closeIpcMemHandle(ipcPtr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
result = context->freeMem(ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryExportImportImplicitScalingTest,
|
||||
whenCallingGetIpcMemHandlesAndImportFailsThenInvalidArgumentFails) {
|
||||
currMemoryManager->failOnCreateGraphicsAllocationFromSharedHandle = true;
|
||||
|
||||
size_t size = 10;
|
||||
size_t alignment = 1u;
|
||||
void *ptr = nullptr;
|
||||
|
||||
ze_device_mem_alloc_desc_t deviceDesc = {};
|
||||
ze_result_t result = context->allocDeviceMem(device->toHandle(),
|
||||
&deviceDesc,
|
||||
size, alignment, &ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_NE(nullptr, ptr);
|
||||
|
||||
uint32_t numIpcHandles = 0;
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_EQ(numIpcHandles, 2u);
|
||||
|
||||
std::vector<ze_ipc_mem_handle_t> ipcHandles(numIpcHandles);
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, ipcHandles.data());
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<MockDriverModelDRM>(512));
|
||||
|
||||
ze_ipc_memory_flags_t flags = {};
|
||||
void *ipcPtr;
|
||||
result = context->openIpcMemHandles(device->toHandle(), numIpcHandles, ipcHandles.data(), flags, &ipcPtr);
|
||||
EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, result);
|
||||
|
||||
result = context->freeMem(ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
}
|
||||
|
||||
using MemoryTest = Test<DeviceFixture>;
|
||||
|
||||
struct CompressionMemoryTest : public MemoryTest {
|
||||
@@ -2526,6 +2736,44 @@ struct MemoryFailedOpenIpcHandleTest : public ::testing::Test {
|
||||
std::unique_ptr<ContextImp> context;
|
||||
};
|
||||
|
||||
struct MemoryFailedOpenIpcHandleImplicitScalingTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
DebugManagerStateRestore restorer;
|
||||
DebugManager.flags.EnableImplicitScaling.set(1);
|
||||
NEO::MockCompilerEnableGuard mock(true);
|
||||
neoDevice =
|
||||
NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get());
|
||||
auto mockBuiltIns = new MockBuiltins();
|
||||
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->builtins.reset(mockBuiltIns);
|
||||
NEO::DeviceVector devices;
|
||||
devices.push_back(std::unique_ptr<NEO::Device>(neoDevice));
|
||||
driverHandle = std::make_unique<DriverHandleImp>();
|
||||
driverHandle->initialize(std::move(devices));
|
||||
prevMemoryManager = driverHandle->getMemoryManager();
|
||||
currMemoryManager = new MemoryManagerIpcMock(*neoDevice->executionEnvironment);
|
||||
driverHandle->setMemoryManager(currMemoryManager);
|
||||
device = driverHandle->devices[0];
|
||||
|
||||
context = std::make_unique<ContextImp>(driverHandle.get());
|
||||
EXPECT_NE(context, nullptr);
|
||||
context->getDevices().insert(std::make_pair(device->toHandle(), device));
|
||||
auto neoDevice = device->getNEODevice();
|
||||
context->rootDeviceIndices.push_back(neoDevice->getRootDeviceIndex());
|
||||
context->deviceBitfields.insert({neoDevice->getRootDeviceIndex(), neoDevice->getDeviceBitfield()});
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
driverHandle->setMemoryManager(prevMemoryManager);
|
||||
delete currMemoryManager;
|
||||
}
|
||||
NEO::MemoryManager *prevMemoryManager = nullptr;
|
||||
NEO::MemoryManager *currMemoryManager = nullptr;
|
||||
std::unique_ptr<DriverHandleImp> driverHandle;
|
||||
NEO::MockDevice *neoDevice = nullptr;
|
||||
L0::Device *device = nullptr;
|
||||
std::unique_ptr<ContextImp> context;
|
||||
};
|
||||
|
||||
TEST_F(MemoryFailedOpenIpcHandleTest,
|
||||
givenCallToOpenIpcMemHandleWithNullPtrFromCreateGraphicsAllocationFromSharedHandleThenInvalidArgumentIsReturned) {
|
||||
size_t size = 10;
|
||||
@@ -2553,6 +2801,37 @@ TEST_F(MemoryFailedOpenIpcHandleTest,
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryFailedOpenIpcHandleImplicitScalingTest,
|
||||
givenCallToOpenIpcMemHandleWithNullPtrFromCreateGraphicsAllocationFromSharedHandleThenInvalidArgumentIsReturned) {
|
||||
size_t size = 10;
|
||||
size_t alignment = 1u;
|
||||
void *ptr = nullptr;
|
||||
|
||||
ze_device_mem_alloc_desc_t deviceDesc = {};
|
||||
ze_result_t result = context->allocDeviceMem(device->toHandle(),
|
||||
&deviceDesc,
|
||||
size, alignment, &ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
EXPECT_NE(nullptr, ptr);
|
||||
|
||||
uint32_t numIpcHandles = 0;
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
std::vector<ze_ipc_mem_handle_t> ipcHandles(numIpcHandles);
|
||||
result = context->getIpcMemHandles(ptr, &numIpcHandles, ipcHandles.data());
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
|
||||
ze_ipc_memory_flags_t flags = {};
|
||||
void *ipcPtr;
|
||||
result = context->openIpcMemHandles(device->toHandle(), numIpcHandles, ipcHandles.data(), flags, &ipcPtr);
|
||||
EXPECT_EQ(ZE_RESULT_ERROR_INVALID_ARGUMENT, result);
|
||||
EXPECT_EQ(ipcPtr, nullptr);
|
||||
|
||||
result = context->freeMem(ptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
|
||||
}
|
||||
|
||||
using DeviceMemorySizeTest = Test<DeviceFixture>;
|
||||
|
||||
TEST_F(DeviceMemorySizeTest, givenSizeGreaterThanLimitThenDeviceAllocationFails) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<!---
|
||||
|
||||
Copyright (C) 2022 Intel Corporation
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
-->
|
||||
|
||||
|
||||
# Level Zero GPU Driver Driver Experimental Extensions
|
||||
|
||||
## Introduction
|
||||
|
||||
The following document describes the driver experimental extensions implemented in the Level Zero Intel(R) GPU driver. These extensions are meant to test and/or gather feedback on interfaces before they could potentially be added Level Zero specification, as well to provide access to functionality specific to Intel(R) GPUs.
|
||||
|
||||
Access to these extensions is possible through [zeDriverGetExtensionFunctionAddress](https://spec.oneapi.io/level-zero/latest/core/api.html?highlight=zedrivergetextensionfunctionaddress#_CPPv435zeDriverGetExtensionFunctionAddress18ze_driver_handle_tPKcPPv). Sample code:
|
||||
|
||||
|
||||
```cpp
|
||||
typedef ze_result_t (*pFnzexMemGetIpcHandles)(ze_context_handle_t, const void *, uint32_t *, ze_ipc_mem_handle_t *);
|
||||
...
|
||||
|
||||
pFnzexMemGetIpcHandles zexMemOpenIpcHandlePointer = nullptr;
|
||||
ze_result_t res = zeDriverGetExtensionFunctionAddress(hDriver, "zexMemOpenIpcHandles", reinterpret_cast<void **>(&zexMemOpenIpcHandlePointer)));
|
||||
```
|
||||
|
||||
### [Multiple IPC Handles](MULTIPLE_IPC_HANDLES.md)
|
||||
106
level_zero/doc/experimental_extensions/MULTIPLE_IPC_HANDLES.md
Normal file
106
level_zero/doc/experimental_extensions/MULTIPLE_IPC_HANDLES.md
Normal file
@@ -0,0 +1,106 @@
|
||||
<!---
|
||||
|
||||
Copyright (C) 2022 Intel Corporation
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
-->
|
||||
|
||||
# Multiple IPC handles
|
||||
|
||||
* [Overview](#Overview)
|
||||
* [Definitions](#Definitions)
|
||||
* [Known Issues and Limitations](#Known-Issues-and-Limitations)
|
||||
|
||||
# Overview
|
||||
|
||||
When using multi-tile architectures, Level Zero runtime allocations (i.e. `zeMemAllocDevice`) may be split by the GPU driver into several smaller allocations, each one allocated in a given tile. This is done for instance when using implicit scaling, to equally distribute memory on the available tiles. Although this is done as an internal optimization the application should not be aware of, there are scenarios where the number of internal allocations need to be known by the application. One of those being when exchanging the IPC handles of the runtime allocation with other process. If the allocation has been split into several smaller ones, then each of thse internal allocations would have its own file descriptor, and each of them need to be separately interchanged with the other process.
|
||||
|
||||
Current IPC memory interfaces in the Level Zero specification `zeMemGetIpcHandle` and `zeMemOpenIpcHandle` assume the existence of only one file descriptor, and cannot be used when multiple internal allocations and file descriptors are associated with a single Level Zero allocation.
|
||||
|
||||
This extension provides two new interfaces with which applications can query the number of file descriptors associated with a Level Zero allocation, get those descriptors, and then create a new allocation composed of the exported internal allocations.
|
||||
|
||||
# Definitions
|
||||
|
||||
```cpp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef ZEX_MEM_IPC_HANDLES_NAME
|
||||
/// @brief Multiple IPC handles driver extension name
|
||||
#define ZEX_MEM_IPC_HANDLES_NAME "ZEX_mem_ipc_handles"
|
||||
#endif // ZEX_MEM_IPC_HANDLES_NAME
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Multiple IPC handles driver extension Version(s)
|
||||
typedef enum _zex_mem_ipc_handles_version_t
|
||||
{
|
||||
ZEX_MEM_IPC_HANDLES_VERSION_1_0 = ZE_MAKE_VERSION( 1, 0 ), ///< version 1.0
|
||||
ZEX_MEM_IPC_HANDLES_VERSION_CURRENT = ZE_MAKE_VERSION( 1, 0 ), ///< latest known version
|
||||
ZEX_MEM_IPC_HANDLES_VERSION_FORCE_UINT32 = 0x7fffffff
|
||||
|
||||
} zex_mem_ipc_handles_version_t;
|
||||
```
|
||||
|
||||
## Interfaces
|
||||
|
||||
```cpp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Returns an array IPC memory handles for the specified allocation
|
||||
///
|
||||
/// @details
|
||||
/// - Takes a pointer to a device memory allocation and returns an array of
|
||||
// IPC memory handle for exporting it for use in another process.
|
||||
/// - The application may call this function from simultaneous threads.
|
||||
/// - The implementation of this function must be thread-safe.
|
||||
///
|
||||
/// @returns
|
||||
/// - ::ZE_RESULT_SUCCESS
|
||||
/// - ::ZE_RESULT_ERROR_INVALID_ARGUMENT
|
||||
/// + ptr` not known
|
||||
ZE_APIEXPORT ze_result_t ZE_APICALL
|
||||
zexMemGetIpcHandles(
|
||||
ze_context_handle_t hContext, ///< [in] handle of the context object
|
||||
const void *ptr, ///< [in] pointer to the device memory allocation
|
||||
uint32_t *numIpcHandles, ///< [in,out] number of IPC handles associated with the allocation
|
||||
///< if numIpcHandles is zero, then the driver shall update the value with the
|
||||
///< total number of IPC handles associated with the allocation.
|
||||
ze_ipc_mem_handle_t *pIpcHandles ///< [in,out][optional][range(0, *numIpcHandles)] returned array of IPC memory handles
|
||||
);
|
||||
```
|
||||
|
||||
```cpp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Creates an allocation associated with an array of IPC memory handles
|
||||
/// imported from another process.
|
||||
///
|
||||
/// @details
|
||||
/// - Takes an array of IPC memory handles from a remote process and associates it
|
||||
/// with a device pointer usable in this process.
|
||||
/// - The device pointer in this process should not be freed with
|
||||
/// ::zeMemFree, but rather with ::zeMemCloseIpcHandle.
|
||||
/// - The application may call this function from simultaneous threads.
|
||||
/// - The implementation of this function must be thread-safe.
|
||||
///
|
||||
/// @returns
|
||||
/// - ::ZE_RESULT_SUCCESS
|
||||
/// - ::ZE_RESULT_ERROR_INVALID_ARGUMENT
|
||||
/// + handles not known
|
||||
ZE_APIEXPORT ze_result_t ZE_APICALL
|
||||
zexMemOpenIpcHandles(
|
||||
ze_context_handle_t hContext, ///< [in] handle of the context object
|
||||
ze_device_handle_t hDevice, ///< [in] handle of the device to associate with the IPC memory handle
|
||||
uint32_t numIpcHandles, ///< [in] number of IPC handles associated with the allocation
|
||||
ze_ipc_mem_handle_t *pIpcHandles, ///< [in][range(0, *numIpcHandles)] array of IPC memory handles
|
||||
ze_ipc_memory_flags_t flags, ///< [in] flags controlling the operation.
|
||||
///< must be 0 (default) or a valid combination of ::ze_ipc_memory_flag_t.
|
||||
void **pptr ///< [out] pointer to device allocation in this process
|
||||
);
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
None.
|
||||
|
||||
# Known Issues and Limitations
|
||||
|
||||
* Currently supported only on Linux.
|
||||
* Mainly implemented for and validated on Xe HPC (PVC) and XeHP_SDV platforms.
|
||||
Reference in New Issue
Block a user