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:
Jaime Arteaga
2022-01-31 23:29:01 +00:00
committed by Compute-Runtime-Automation
parent c0de4fd243
commit 3f26f45c10
38 changed files with 1311 additions and 52 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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{};

View File

@@ -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) {

View File

@@ -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)

View 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.