feature: Support for opaque IPC handles on Windows and Linux

- Added support for creating and managing opaque IPC NT handles in the
WDDM layer.
- Introduced a new flag `shareableWithoutNTHandle` to indicate if memory
can be shared without an NT handle.
- Updated the `isShareableMemory` method to accommodate this new flag.
- Added debug variable EnableShareableWithoutNTHandle to control the
behavior of sharing memory without NT handles until requested.
- Updated Linux path to enable sharing DMA Buf FDs between processes
for use in pidfd_getfd

Related-To: NEO-15345 , NEO-15346 , NEO-15347, NEO-10380

Signed-off-by: Neil R. Spruit <neil.r.spruit@intel.com>
This commit is contained in:
Neil R. Spruit
2025-08-13 19:34:37 +00:00
committed by Compute-Runtime-Automation
parent 274c5043b9
commit 46b1b2783b
45 changed files with 1406 additions and 130 deletions

View File

@@ -8,6 +8,7 @@
#pragma once
#include "shared/source/memory_manager/allocation_type.h"
#include "shared/source/memory_manager/graphics_allocation.h"
#include "shared/source/unified_memory/unified_memory.h"
#include "level_zero/core/source/helpers/api_handle_helper.h"
@@ -30,6 +31,45 @@ struct Image;
class ContextExt;
#pragma pack(1)
struct IpcMemoryData {
uint64_t handle = 0;
uint64_t poolOffset = 0;
uint8_t type = 0;
};
#pragma pack()
static_assert(sizeof(IpcMemoryData) <= ZE_MAX_IPC_HANDLE_SIZE, "IpcMemoryData is bigger than ZE_MAX_IPC_HANDLE_SIZE");
enum class IpcHandleType : uint8_t {
fdHandle = 0,
ntHandle = 1,
maxHandle
};
#pragma pack(1)
struct IpcOpaqueMemoryData {
union IpcHandle {
int fd;
uint64_t reserved;
};
IpcHandle handle = {};
uint64_t poolOffset = 0;
unsigned int processId = 0;
IpcHandleType type = IpcHandleType::maxHandle;
uint8_t memoryType = 0;
};
#pragma pack()
static_assert(sizeof(IpcOpaqueMemoryData) <= ZE_MAX_IPC_HANDLE_SIZE, "IpcOpaqueMemoryData is bigger than ZE_MAX_IPC_HANDLE_SIZE");
struct IpcHandleTracking {
uint64_t refcnt = 0;
NEO::GraphicsAllocation *alloc = nullptr;
uint32_t handleId = 0;
uint64_t handle = 0;
uint64_t ptr = 0;
struct IpcMemoryData ipcData = {};
};
struct Context : _ze_context_handle_t {
inline static ze_memory_type_t parseUSMType(InternalMemoryType memoryType) {
switch (memoryType) {
@@ -170,8 +210,10 @@ struct Context : _ze_context_handle_t {
ze_ipc_mem_handle_t *pIpcHandle) = 0;
virtual ze_result_t putVirtualAddressSpaceIpcHandle(ze_ipc_mem_handle_t ipcHandle) = 0;
virtual ze_result_t lockMemory(ze_device_handle_t hDevice, void *ptr, size_t size) = 0;
virtual bool isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice) = 0;
virtual void *getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, ze_ipc_memory_flags_t flags) = 0;
virtual bool isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle) = 0;
virtual void *getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, unsigned int processId, ze_ipc_memory_flags_t flags) = 0;
virtual void getDataFromIpcHandle(ze_device_handle_t hDevice, const ze_ipc_mem_handle_t ipcHandle, uint64_t &handle, uint8_t &type, unsigned int &processId, uint64_t &poolOffset) = 0;
virtual bool isOpaqueHandleSupported(IpcHandleType *handleType) = 0;
virtual ze_result_t getPitchFor2dImage(
ze_device_handle_t hDevice,

View File

@@ -9,14 +9,17 @@
#include "shared/source/command_container/implicit_scaling.h"
#include "shared/source/command_stream/command_stream_receiver.h"
#include "shared/source/execution_environment/execution_environment.h"
#include "shared/source/execution_environment/root_device_environment.h"
#include "shared/source/helpers/aligned_memory.h"
#include "shared/source/helpers/driver_model_type.h"
#include "shared/source/helpers/gfx_core_helper.h"
#include "shared/source/helpers/ptr_math.h"
#include "shared/source/memory_manager/allocation_properties.h"
#include "shared/source/memory_manager/memory_manager.h"
#include "shared/source/memory_manager/memory_operations_handler.h"
#include "shared/source/memory_manager/unified_memory_manager.h"
#include "shared/source/os_interface/os_interface.h"
#include "shared/source/utilities/cpu_info.h"
#include "level_zero/core/source/cmdlist/cmdlist.h"
@@ -75,15 +78,15 @@ ContextImp::ContextImp(DriverHandle *driverHandle) {
}
contextSettings.enableSvmHeapReservation = platformSupportSvmHeapReservation;
bool pidfdOrSocket = true;
bool useOpaqueHandle = true;
for (auto &device : this->driverHandle->devices) {
auto &productHelper = device->getNEODevice()->getProductHelper();
if (!productHelper.isPidFdOrSocketForIpcSupported()) {
pidfdOrSocket = false;
useOpaqueHandle = false;
break;
}
}
contextSettings.enablePidfdOrSockets = pidfdOrSocket;
contextSettings.enablePidfdOrSockets = useOpaqueHandle;
}
ContextImp::~ContextImp() {
@@ -135,6 +138,7 @@ ze_result_t ContextImp::allocHostMem(const ze_host_mem_alloc_desc_t *hostDesc,
*ptr = getMemHandlePtr(this->devices.begin()->second,
lookupTable.sharedHandleType.fd,
NEO::AllocationType::bufferHostMemory,
0u,
flags);
if (nullptr == *ptr) {
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
@@ -143,7 +147,7 @@ ze_result_t ContextImp::allocHostMem(const ze_host_mem_alloc_desc_t *hostDesc,
UNRECOVERABLE_IF(!lookupTable.sharedHandleType.isNTHandle);
*ptr = this->driverHandle->importNTHandle(this->devices.begin()->second,
lookupTable.sharedHandleType.ntHandle,
NEO::AllocationType::bufferHostMemory);
NEO::AllocationType::bufferHostMemory, 0);
if (*ptr == nullptr) {
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
}
@@ -266,6 +270,7 @@ ze_result_t ContextImp::allocDeviceMem(ze_device_handle_t hDevice,
*ptr = getMemHandlePtr(hDevice,
lookupTable.sharedHandleType.fd,
NEO::AllocationType::buffer,
0u,
flags);
if (nullptr == *ptr) {
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
@@ -274,7 +279,8 @@ ze_result_t ContextImp::allocDeviceMem(ze_device_handle_t hDevice,
UNRECOVERABLE_IF(!lookupTable.sharedHandleType.isNTHandle);
*ptr = this->driverHandle->importNTHandle(hDevice,
lookupTable.sharedHandleType.ntHandle,
NEO::AllocationType::buffer);
NEO::AllocationType::buffer,
0);
if (*ptr == nullptr) {
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
}
@@ -289,7 +295,10 @@ ze_result_t ContextImp::allocDeviceMem(ze_device_handle_t hDevice,
deviceBitfields[rootDeviceIndex] = neoDevice->getDeviceBitfield();
NEO::SVMAllocsManager::UnifiedMemoryProperties unifiedMemoryProperties(InternalMemoryType::deviceUnifiedMemory, alignment, this->driverHandle->rootDeviceIndices, deviceBitfields);
unifiedMemoryProperties.allocationFlags.flags.shareable = isShareableMemory(deviceMemDesc->pNext, static_cast<uint32_t>(lookupTable.exportMemory), neoDevice);
if (NEO::debugManager.flags.EnableShareableWithoutNTHandle.get()) {
unifiedMemoryProperties.allocationFlags.flags.shareableWithoutNTHandle = 1;
}
unifiedMemoryProperties.allocationFlags.flags.shareable = isShareableMemory(deviceMemDesc->pNext, static_cast<uint32_t>(lookupTable.exportMemory), neoDevice, unifiedMemoryProperties.allocationFlags.flags.shareableWithoutNTHandle);
unifiedMemoryProperties.device = neoDevice;
unifiedMemoryProperties.allocationFlags.flags.compressedHint = isAllocationSuitableForCompression(lookupTable, *device, size);
@@ -799,7 +808,8 @@ ze_result_t ContextImp::getIpcMemHandlesImpl(const void *ptr,
ipcType = InternalIpcMemoryType::hostUnifiedMemory;
}
bool pidfdOrSocket = contextSettings.enablePidfdOrSockets;
IpcHandleType handleType = IpcHandleType::maxHandle;
bool useOpaqueHandle = isOpaqueHandleSupported(&handleType);
uint32_t loopCount = numIpcHandles ? *numIpcHandles : 1u;
for (uint32_t i = 0u; i < loopCount; i++) {
uint64_t handle = 0;
@@ -810,12 +820,12 @@ ze_result_t ContextImp::getIpcMemHandlesImpl(const void *ptr,
memoryManager->registerIpcExportedAllocation(alloc);
if (pidfdOrSocket) {
if (useOpaqueHandle) {
IpcOpaqueMemoryData &ipcData = *reinterpret_cast<IpcOpaqueMemoryData *>(pIpcHandles[i].data);
setIPCHandleData<IpcOpaqueMemoryData>(alloc, handle, ipcData, reinterpret_cast<uint64_t>(ptr), static_cast<uint8_t>(ipcType), usmPool);
setIPCHandleData<IpcOpaqueMemoryData>(alloc, handle, ipcData, reinterpret_cast<uint64_t>(ptr), static_cast<uint8_t>(ipcType), usmPool, handleType);
} else {
IpcMemoryData &ipcData = *reinterpret_cast<IpcMemoryData *>(pIpcHandles[i].data);
setIPCHandleData<IpcMemoryData>(alloc, handle, ipcData, reinterpret_cast<uint64_t>(ptr), static_cast<uint8_t>(ipcType), usmPool);
setIPCHandleData<IpcMemoryData>(alloc, handle, ipcData, reinterpret_cast<uint64_t>(ptr), static_cast<uint8_t>(ipcType), usmPool, handleType);
}
}
return ZE_RESULT_SUCCESS;
@@ -837,11 +847,12 @@ ze_result_t ContextImp::openIpcMemHandle(ze_device_handle_t hDevice,
const ze_ipc_mem_handle_t &pIpcHandle,
ze_ipc_memory_flags_t flags,
void **ptr) {
using IpcDataT = IpcMemoryData;
const IpcDataT &ipcData = *reinterpret_cast<const IpcDataT *>(pIpcHandle.data);
uint64_t handle;
uint8_t type;
unsigned int processId;
uint64_t poolOffset;
uint64_t handle = ipcData.handle;
uint8_t type = ipcData.type;
getDataFromIpcHandle(hDevice, pIpcHandle, handle, type, processId, poolOffset);
NEO::AllocationType allocationType = NEO::AllocationType::unknown;
if (type == static_cast<uint8_t>(InternalIpcMemoryType::deviceUnifiedMemory)) {
@@ -855,12 +866,13 @@ ze_result_t ContextImp::openIpcMemHandle(ze_device_handle_t hDevice,
*ptr = getMemHandlePtr(hDevice,
handle,
allocationType,
processId,
flags);
if (nullptr == *ptr) {
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
}
*ptr = ptrOffset(*ptr, ipcData.poolOffset);
*ptr = ptrOffset(*ptr, poolOffset);
return ZE_RESULT_SUCCESS;
}
@@ -873,12 +885,15 @@ ze_result_t ContextImp::openIpcMemHandles(ze_device_handle_t hDevice,
std::vector<NEO::osHandle> handles;
handles.reserve(numIpcHandles);
using IpcDataT = IpcMemoryData;
for (uint32_t i = 0; i < numIpcHandles; i++) {
const IpcDataT &ipcData = *reinterpret_cast<const IpcDataT *>(pIpcHandles[i].data);
uint64_t handle = ipcData.handle;
uint64_t handle;
uint8_t type;
unsigned int processId;
[[maybe_unused]] uint64_t poolOffset;
if (ipcData.type != static_cast<uint8_t>(InternalIpcMemoryType::deviceUnifiedMemory)) {
getDataFromIpcHandle(hDevice, pIpcHandles[i], handle, type, processId, poolOffset);
if (type != static_cast<uint8_t>(InternalIpcMemoryType::deviceUnifiedMemory)) {
return ZE_RESULT_ERROR_INVALID_ARGUMENT;
}

View File

@@ -174,8 +174,10 @@ struct ContextImp : Context, NEO::NonCopyableAndNonMovableClass {
ContextSettings contextSettings;
bool isDeviceDefinedForThisContext(Device *inDevice);
bool isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice) override;
void *getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, ze_ipc_memory_flags_t flags) override;
bool isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle) override;
void *getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, unsigned int processId, ze_ipc_memory_flags_t flags) override;
void getDataFromIpcHandle(ze_device_handle_t hDevice, const ze_ipc_mem_handle_t ipcHandle, uint64_t &handle, uint8_t &type, unsigned int &processId, uint64_t &poolOffset) override;
bool isOpaqueHandleSupported(IpcHandleType *handleType) override;
void initDeviceHandles(uint32_t numDevices, ze_device_handle_t *deviceHandles) {
this->numDevices = numDevices;
@@ -207,19 +209,25 @@ struct ContextImp : Context, NEO::NonCopyableAndNonMovableClass {
protected:
ze_result_t getIpcMemHandlesImpl(const void *ptr, uint32_t *numIpcHandles, ze_ipc_mem_handle_t *pIpcHandles);
template <typename IpcDataT>
void setIPCHandleData(NEO::GraphicsAllocation *graphicsAllocation, uint64_t handle, IpcDataT &ipcData, uint64_t ptrAddress, uint8_t type, NEO::UsmMemAllocPool *usmPool) {
void setIPCHandleData(NEO::GraphicsAllocation *graphicsAllocation, uint64_t handle, IpcDataT &ipcData, uint64_t ptrAddress, uint8_t type, NEO::UsmMemAllocPool *usmPool, IpcHandleType handleType) {
std::map<uint64_t, IpcHandleTracking *>::iterator ipcHandleIterator;
ipcData = {};
if constexpr (std::is_same_v<IpcDataT, IpcMemoryData>) {
ipcData.handle = handle;
ipcData.type = type;
} else if constexpr (std::is_same_v<IpcDataT, IpcOpaqueMemoryData>) {
}
if constexpr (std::is_same_v<IpcDataT, IpcOpaqueMemoryData>) {
ipcData.memoryType = type;
ipcData.processId = NEO::SysCalls::getCurrentProcessId();
ipcData.type = IpcHandleType::fdHandle;
ipcData.type = handleType;
if (handleType == IpcHandleType::ntHandle) {
ipcData.handle.reserved = handle;
} else if (handleType == IpcHandleType::fdHandle) {
// For fdHandle, we store the handle as an int
ipcData.handle.fd = static_cast<int>(handle);
}
}
if (usmPool) {
ipcData.poolOffset = usmPool->getOffsetInPool(addrToPtr(ptrAddress));

View File

@@ -12,9 +12,23 @@
#include "level_zero/core/source/context/context_imp.h"
#include "level_zero/core/source/device/device.h"
#include "level_zero/core/source/driver/driver_handle_imp.h"
#include <sys/prctl.h>
namespace L0 {
bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice) {
bool ContextImp::isOpaqueHandleSupported(IpcHandleType *handleType) {
bool useOpaqueHandle = contextSettings.enablePidfdOrSockets;
*handleType = IpcHandleType::fdHandle;
if (useOpaqueHandle) {
if (NEO::SysCalls::prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == -1) {
PRINT_DEBUG_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "prctl Syscall for PR_SET_PTRACER, PR_SET_PTRACER_ANY failed, using fallback mechanism for IPC handle exchange\n");
return false;
}
}
return useOpaqueHandle;
}
bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle) {
if (exportableMemory) {
return true;
}
@@ -22,31 +36,48 @@ bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory
return false;
}
void *ContextImp::getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, ze_ipc_memory_flags_t flags) {
void *ContextImp::getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, unsigned int processId, ze_ipc_memory_flags_t flags) {
auto neoDevice = Device::fromHandle(hDevice)->getNEODevice();
auto &productHelper = neoDevice->getProductHelper();
bool useOpaqueHandle = contextSettings.enablePidfdOrSockets;
uint64_t importHandle = handle;
bool pidfdOrSocket = false;
pidfdOrSocket = productHelper.isPidFdOrSocketForIpcSupported();
if (pidfdOrSocket) {
if (useOpaqueHandle) {
// With pidfd approach extract parent pid and target fd before importing handle
pid_t exporterPid = 0;
pid_t exporterPid = static_cast<pid_t>(processId);
unsigned int flags = 0u;
int pidfd = NEO::SysCalls::pidfdopen(exporterPid, flags);
if (pidfd == -1) {
PRINT_DEBUG_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "pidfd_open Syscall failed, using fallback mechanism for IPC handle exchange\n");
} else {
unsigned int flags = 0u;
int newfd = NEO::SysCalls::pidfdgetfd(pidfd, 0, flags);
int newfd = NEO::SysCalls::pidfdgetfd(pidfd, static_cast<int>(handle), flags);
if (newfd < 0) {
PRINT_DEBUG_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "pidfd_getfd Syscall failed, using fallback mechanism for IPC handle exchange\n");
} else {
importHandle = static_cast<uint64_t>(newfd);
}
}
}
NEO::SvmAllocationData allocDataInternal(neoDevice->getRootDeviceIndex());
return this->driverHandle->importFdHandle(neoDevice, flags, handle, allocationType, nullptr, nullptr, allocDataInternal);
return this->driverHandle->importFdHandle(neoDevice, flags, importHandle, allocationType, nullptr, nullptr, allocDataInternal);
}
void ContextImp::getDataFromIpcHandle(ze_device_handle_t hDevice, const ze_ipc_mem_handle_t ipcHandle, uint64_t &handle, uint8_t &type, unsigned int &processId, uint64_t &poolOffset) {
bool useOpaqueHandle = contextSettings.enablePidfdOrSockets;
if (useOpaqueHandle) {
const IpcOpaqueMemoryData *ipcData = reinterpret_cast<const IpcOpaqueMemoryData *>(ipcHandle.data);
handle = static_cast<uint64_t>(ipcData->handle.fd);
type = ipcData->memoryType;
processId = ipcData->processId;
poolOffset = ipcData->poolOffset;
} else {
const IpcMemoryData *ipcData = reinterpret_cast<const IpcMemoryData *>(ipcHandle.data);
handle = ipcData->handle;
type = ipcData->type;
poolOffset = ipcData->poolOffset;
}
}
} // namespace L0

View File

@@ -1,24 +1,50 @@
/*
* Copyright (C) 2022-2024 Intel Corporation
* Copyright (C) 2022-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/source/device/device.h"
#include "shared/source/execution_environment/execution_environment.h"
#include "shared/source/execution_environment/root_device_environment.h"
#include "shared/source/helpers/driver_model_type.h"
#include "shared/source/memory_manager/memory_manager.h"
#include "shared/source/memory_manager/unified_memory_manager.h"
#include "shared/source/os_interface/linux/sys_calls.h"
#include "shared/source/os_interface/os_interface.h"
#include "level_zero/core/source/context/context_imp.h"
#include "level_zero/core/source/device/device.h"
#include "level_zero/core/source/driver/driver_handle_imp.h"
#include <sys/prctl.h>
namespace L0 {
bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice) {
bool ContextImp::isOpaqueHandleSupported(IpcHandleType *handleType) {
bool useOpaqueHandle = contextSettings.enablePidfdOrSockets;
NEO::DriverModelType driverModelType = NEO::DriverModelType::unknown;
auto rootDeviceEnvironment = this->getDriverHandle()->getMemoryManager()->peekExecutionEnvironment().rootDeviceEnvironments[0].get();
if (rootDeviceEnvironment->osInterface) {
driverModelType = rootDeviceEnvironment->osInterface->getDriverModel()->getDriverModelType();
}
if (driverModelType == NEO::DriverModelType::wddm) {
*handleType = IpcHandleType::ntHandle;
useOpaqueHandle = true;
} else {
*handleType = IpcHandleType::fdHandle;
if (useOpaqueHandle) {
if (NEO::SysCalls::prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == -1) {
PRINT_DEBUG_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "prctl Syscall for PR_SET_PTRACER, PR_SET_PTRACER_ANY failed, using fallback mechanism for IPC handle exchange\n");
return false;
}
}
}
return useOpaqueHandle;
}
bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle) {
if (exportableMemory) {
return true;
}
@@ -35,9 +61,11 @@ bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory
void *ContextImp::getMemHandlePtr(ze_device_handle_t hDevice,
uint64_t handle,
NEO::AllocationType allocationType,
unsigned int processId,
ze_ipc_memory_flags_t flags) {
L0::Device *device = L0::Device::fromHandle(hDevice);
auto neoDevice = device->getNEODevice();
bool useOpaqueHandle = contextSettings.enablePidfdOrSockets;
NEO::DriverModelType driverType = NEO::DriverModelType::unknown;
if (neoDevice->getRootDeviceEnvironment().osInterface) {
driverType = neoDevice->getRootDeviceEnvironment().osInterface->getDriverModel()->getDriverModelType();
@@ -46,13 +74,34 @@ void *ContextImp::getMemHandlePtr(ze_device_handle_t hDevice,
if (isNTHandle) {
return this->driverHandle->importNTHandle(hDevice,
reinterpret_cast<void *>(handle),
allocationType);
allocationType,
processId);
} else if (driverType == NEO::DriverModelType::drm) {
auto neoDevice = Device::fromHandle(hDevice)->getNEODevice();
NEO::SvmAllocationData allocDataInternal(neoDevice->getRootDeviceIndex());
uint64_t importHandle = handle;
if (useOpaqueHandle) {
// With pidfd approach extract parent pid and target fd before importing handle
pid_t exporterPid = static_cast<pid_t>(processId);
unsigned int flags = 0u;
int pidfd = NEO::SysCalls::pidfdopen(exporterPid, flags);
if (pidfd == -1) {
PRINT_DEBUG_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "pidfd_open Syscall failed, using fallback mechanism for IPC handle exchange\n");
} else {
unsigned int flags = 0u;
int newfd = NEO::SysCalls::pidfdgetfd(pidfd, static_cast<int>(handle), flags);
if (newfd < 0) {
PRINT_DEBUG_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "pidfd_getfd Syscall failed, using fallback mechanism for IPC handle exchange\n");
} else {
importHandle = static_cast<uint64_t>(newfd);
}
}
}
return this->driverHandle->importFdHandle(neoDevice,
flags,
handle,
importHandle,
allocationType,
nullptr,
nullptr,
@@ -62,4 +111,30 @@ void *ContextImp::getMemHandlePtr(ze_device_handle_t hDevice,
}
}
void ContextImp::getDataFromIpcHandle(ze_device_handle_t hDevice, const ze_ipc_mem_handle_t ipcHandle, uint64_t &handle, uint8_t &type, unsigned int &processId, uint64_t &poolOffset) {
bool useOpaqueHandle = contextSettings.enablePidfdOrSockets;
L0::Device *device = L0::Device::fromHandle(hDevice);
auto neoDevice = device->getNEODevice();
NEO::DriverModelType driverModelType = NEO::DriverModelType::unknown;
if (neoDevice->getRootDeviceEnvironment().osInterface) {
driverModelType = neoDevice->getRootDeviceEnvironment().osInterface->getDriverModel()->getDriverModelType();
}
if (driverModelType == NEO::DriverModelType::wddm) {
useOpaqueHandle = true;
}
if (useOpaqueHandle) {
const IpcOpaqueMemoryData *ipcData = reinterpret_cast<const IpcOpaqueMemoryData *>(ipcHandle.data);
handle = static_cast<uint64_t>(ipcData->handle.fd);
type = ipcData->memoryType;
processId = ipcData->processId;
poolOffset = ipcData->poolOffset;
} else {
const IpcMemoryData *ipcData = reinterpret_cast<const IpcMemoryData *>(ipcHandle.data);
handle = ipcData->handle;
type = ipcData->type;
poolOffset = ipcData->poolOffset;
}
}
} // namespace L0

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2024 Intel Corporation
* Copyright (C) 2022-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -10,16 +10,33 @@
namespace L0 {
bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice) {
bool ContextImp::isOpaqueHandleSupported(IpcHandleType *handleType) {
*handleType = IpcHandleType::ntHandle;
return true;
}
bool ContextImp::isShareableMemory(const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle) {
if (exportableMemory) {
return true;
}
if (shareableWithoutNTHandle) {
return true;
}
return false;
}
void *ContextImp::getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, ze_ipc_memory_flags_t flags) {
return this->driverHandle->importNTHandle(hDevice, reinterpret_cast<void *>(handle), allocationType);
void *ContextImp::getMemHandlePtr(ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, unsigned int processId, ze_ipc_memory_flags_t flags) {
return this->driverHandle->importNTHandle(hDevice, reinterpret_cast<void *>(handle), allocationType, processId);
}
void ContextImp::getDataFromIpcHandle(ze_device_handle_t hDevice, const ze_ipc_mem_handle_t ipcHandle, uint64_t &handle, uint8_t &type, unsigned int &processId, uint64_t &poolOffset) {
const IpcOpaqueMemoryData *ipcData = reinterpret_cast<const IpcOpaqueMemoryData *>(ipcHandle.data);
handle = static_cast<uint64_t>(ipcData->handle.reserved);
type = ipcData->memoryType;
processId = ipcData->processId;
poolOffset = ipcData->poolOffset;
}
} // namespace L0

View File

@@ -886,12 +886,13 @@ NEO::GraphicsAllocation *DriverHandleImp::getPeerAllocation(Device *device,
return alloc;
}
void *DriverHandleImp::importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType) {
void *DriverHandleImp::importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType, uint32_t parentProcessId) {
auto neoDevice = Device::fromHandle(hDevice)->getNEODevice();
bool isHostIpcAllocation = (allocationType == NEO::AllocationType::bufferHostMemory) ? true : false;
NEO::MemoryManager::OsHandleData osHandleData{handle};
osHandleData.parentProcessId = parentProcessId;
NEO::AllocationProperties properties{neoDevice->getRootDeviceIndex(),
MemoryConstants::pageSize,
allocationType,

View File

@@ -14,6 +14,7 @@
#include "shared/source/os_interface/sys_calls_common.h"
#include "level_zero/api/extensions/public/ze_exp_ext.h"
#include "level_zero/core/source/context/context.h"
#include "level_zero/core/source/driver/driver_handle.h"
#include "level_zero/ze_intel_gpu.h"
@@ -27,43 +28,6 @@ struct FabricVertex;
struct FabricEdge;
struct Image;
class ExternalSemaphoreController;
#pragma pack(1)
struct IpcMemoryData {
uint64_t handle = 0;
uint64_t poolOffset = 0;
uint8_t type = 0;
};
#pragma pack()
static_assert(sizeof(IpcMemoryData) <= ZE_MAX_IPC_HANDLE_SIZE, "IpcMemoryData is bigger than ZE_MAX_IPC_HANDLE_SIZE");
enum class IpcHandleType : uint8_t {
fdHandle = 0,
maxHandle
};
struct IpcOpaqueMemoryData {
union IpcHandle {
int fd;
uint64_t reserved;
};
IpcHandle handle = {};
uint64_t poolOffset = 0;
unsigned int processId = 0;
IpcHandleType type = IpcHandleType::maxHandle;
uint8_t memoryType = 0;
};
static_assert(sizeof(IpcOpaqueMemoryData) <= ZE_MAX_IPC_HANDLE_SIZE, "IpcOpaqueMemoryData is bigger than ZE_MAX_IPC_HANDLE_SIZE");
struct IpcHandleTracking {
uint64_t refcnt = 0;
NEO::GraphicsAllocation *alloc = nullptr;
uint32_t handleId = 0;
uint64_t handle = 0;
uint64_t ptr = 0;
struct IpcMemoryData ipcData = {};
};
struct DriverHandleImp : public DriverHandle {
~DriverHandleImp() override;
DriverHandleImp();
@@ -86,7 +50,7 @@ struct DriverHandleImp : public DriverHandle {
void setMemoryManager(NEO::MemoryManager *memoryManager) override;
MOCKABLE_VIRTUAL void *importFdHandle(NEO::Device *neoDevice, ze_ipc_memory_flags_t flags, uint64_t handle, NEO::AllocationType allocationType, void *basePointer, NEO::GraphicsAllocation **pAlloc, NEO::SvmAllocationData &mappedPeerAllocData);
MOCKABLE_VIRTUAL void *importFdHandles(NEO::Device *neoDevice, ze_ipc_memory_flags_t flags, const std::vector<NEO::osHandle> &handles, void *basePointer, NEO::GraphicsAllocation **pAlloc, NEO::SvmAllocationData &mappedPeerAllocData);
MOCKABLE_VIRTUAL void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType);
MOCKABLE_VIRTUAL void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType, uint32_t parentProcessId);
NEO::SVMAllocsManager *getSvmAllocsManager() override;
NEO::StagingBufferManager *getStagingBufferManager() override;
ze_result_t initialize(std::vector<std::unique_ptr<NEO::Device>> neoDevices);

View File

@@ -85,7 +85,7 @@ struct DriverHandleGetMemHandlePtrMock : public L0::DriverHandleImp {
return &mockFd;
}
void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType) override {
void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType, uint32_t parentProcessId) override {
if (failHandleLookup) {
return nullptr;
}

View File

@@ -108,7 +108,7 @@ void MemoryExportImportTest::SetUp() {
context->deviceBitfields.insert({neoDevice->getRootDeviceIndex(), neoDevice->getDeviceBitfield()});
}
void *DriverHandleGetMemHandleMock::importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType) {
void *DriverHandleGetMemHandleMock::importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType, uint32_t parentProcessId) {
if (mockHandle == allocationHandleMap.second) {
return allocationHandleMap.first;
}
@@ -199,7 +199,7 @@ void MemoryExportImportWSLTest::TearDown() {
delete currMemoryManager;
}
void *DriverHandleGetWinHandleMock::importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType) {
void *DriverHandleGetWinHandleMock::importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType, uint32_t parentProcessId) {
if (mockHandle == allocationMap.second) {
return allocationMap.first;
}

View File

@@ -79,7 +79,7 @@ struct MemoryExportImportTest : public ::testing::Test {
};
struct DriverHandleGetMemHandleMock : public L0::DriverHandleImp {
void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType) override;
void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType, uint32_t parentProcessId) override;
void *importFdHandle(NEO::Device *neoDevice, ze_ipc_memory_flags_t flags, uint64_t handle,
NEO::AllocationType allocationType, void *basePointer,
NEO::GraphicsAllocation **pAloc, NEO::SvmAllocationData &mappedPeerAllocData) override;
@@ -128,7 +128,7 @@ struct MemoryExportImportWSLTest : public ::testing::Test {
};
struct DriverHandleGetWinHandleMock : public L0::DriverHandleImp {
void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType) override;
void *importNTHandle(ze_device_handle_t hDevice, void *handle, NEO::AllocationType allocationType, uint32_t parentProcessId) override;
uint64_t mockHandle = 57;
std::pair<void *, uint64_t> allocationMap;

View File

@@ -79,15 +79,17 @@ struct Mock<Context> : public Context {
ADDMETHOD_NOBASE(getVirtualAddressSpaceIpcHandle, ze_result_t, ZE_RESULT_SUCCESS, (ze_device_handle_t, ze_ipc_mem_handle_t *));
ADDMETHOD_NOBASE(putVirtualAddressSpaceIpcHandle, ze_result_t, ZE_RESULT_SUCCESS, (ze_ipc_mem_handle_t ipcHandle));
ADDMETHOD_NOBASE(lockMemory, ze_result_t, ZE_RESULT_SUCCESS, (ze_device_handle_t hDevice, void *ptr, size_t size));
ADDMETHOD_NOBASE(isShareableMemory, bool, true, (const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice));
ADDMETHOD_NOBASE(getMemHandlePtr, void *, nullptr, (ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, ze_ipc_memory_flags_t flags));
ADDMETHOD_NOBASE(isShareableMemory, bool, true, (const void *exportDesc, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle));
ADDMETHOD_NOBASE(isOpaqueHandleSupported, bool, true, (IpcHandleType * handleType));
ADDMETHOD_NOBASE(getMemHandlePtr, void *, nullptr, (ze_device_handle_t hDevice, uint64_t handle, NEO::AllocationType allocationType, unsigned int processId, ze_ipc_memory_flags_t flags));
ADDMETHOD_NOBASE_VOIDRETURN(getDataFromIpcHandle, (ze_device_handle_t hDevice, const ze_ipc_mem_handle_t ipcHandle, uint64_t &handle, uint8_t &type, unsigned int &processId, uint64_t &poolOffset));
ADDMETHOD_NOBASE(getPitchFor2dImage, ze_result_t, ZE_RESULT_SUCCESS, (ze_device_handle_t, size_t, size_t, unsigned int, size_t *));
ADDMETHOD_NOBASE(getContextExt, ContextExt *, nullptr, ());
};
struct ContextShareableMock : public L0::ContextImp {
ContextShareableMock(L0::DriverHandle *driverHandle) : L0::ContextImp(driverHandle) {}
bool isShareableMemory(const void *pNext, bool exportableMemory, NEO::Device *neoDevice) override {
bool isShareableMemory(const void *pNext, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle) override {
return true;
}
};

View File

@@ -8,6 +8,7 @@
#include "shared/source/gmm_helper/gmm.h"
#include "shared/source/memory_manager/gfx_partition.h"
#include "shared/source/os_interface/device_factory.h"
#include "shared/source/unified_memory/unified_memory.h"
#include "shared/source/utilities/cpu_info.h"
#include "shared/test/common/mocks/mock_bindless_heaps_helper.h"
#include "shared/test/common/mocks/mock_command_stream_receiver.h"
@@ -2797,5 +2798,267 @@ HWTEST_F(ContextTest, givenMakeImageResidentThenMakeImageResidentIsCalledWithFor
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
}
class ContextWhiteboxForIpcTesting : public ::L0::ContextImp {
public:
ContextWhiteboxForIpcTesting(L0::DriverHandle *driverHandle) : L0::ContextImp(driverHandle) {}
using ::L0::ContextImp::setIPCHandleData;
};
TEST_F(ContextTest, whenCallingSetIPCHandleDataWithIpcMemoryDataTypeThenIpcDataIsSetInHandleTracking) {
ze_context_handle_t hContext;
ze_context_desc_t desc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC, nullptr, 0};
ze_result_t res = driverHandle->createContext(&desc, 0u, nullptr, &hContext);
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
ContextWhiteboxForIpcTesting contextWhitebox(driverHandle.get());
// Create a mock graphics allocation
NEO::MockGraphicsAllocation mockAllocation;
// Set up test data
uint64_t handle = 12345;
L0::IpcMemoryData ipcData;
ipcData.handle = handle;
ipcData.type = static_cast<uint8_t>(InternalMemoryType::deviceUnifiedMemory);
ipcData.poolOffset = 0;
uint64_t ptrAddress = 0x1000;
uint8_t type = static_cast<uint8_t>(InternalMemoryType::deviceUnifiedMemory);
// Verify IPC handle map is initially empty
EXPECT_TRUE(driverHandle->getIPCHandleMap().empty());
// Call setIPCHandleData with IpcMemoryData type
contextWhitebox.setIPCHandleData<L0::IpcMemoryData>(&mockAllocation, handle, ipcData, ptrAddress, type, nullptr, L0::IpcHandleType::fdHandle);
// Verify the handle was added to the IPC handle map
auto &ipcHandleMap = driverHandle->getIPCHandleMap();
EXPECT_EQ(1u, ipcHandleMap.size());
// Verify the handle tracking entry
auto handleIterator = ipcHandleMap.find(handle);
ASSERT_NE(handleIterator, ipcHandleMap.end());
L0::IpcHandleTracking *handleTracking = handleIterator->second;
EXPECT_NE(nullptr, handleTracking);
EXPECT_EQ(&mockAllocation, handleTracking->alloc);
EXPECT_EQ(1u, handleTracking->refcnt);
EXPECT_EQ(ptrAddress, handleTracking->ptr);
EXPECT_EQ(handle, handleTracking->handle);
// Verify that the ipcData field is set
EXPECT_EQ(handle, handleTracking->ipcData.handle);
EXPECT_EQ(type, handleTracking->ipcData.type);
EXPECT_EQ(0u, handleTracking->ipcData.poolOffset);
// Clean up - remove the entry from the map to avoid issues in teardown
delete handleTracking;
driverHandle->getIPCHandleMap().clear();
ContextImp *contextImp = static_cast<ContextImp *>(L0::Context::fromHandle(hContext));
res = contextImp->destroy();
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
}
TEST_F(ContextTest, whenCallingSetIPCHandleDataWithIpcOpaqueMemoryDataTypeThenIpcDataIsNotSetInHandleTracking) {
ze_context_handle_t hContext;
ze_context_desc_t desc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC, nullptr, 0};
ze_result_t res = driverHandle->createContext(&desc, 0u, nullptr, &hContext);
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
ContextWhiteboxForIpcTesting contextWhitebox(driverHandle.get());
// Create a mock graphics allocation
NEO::MockGraphicsAllocation mockAllocation;
// Set up test data for IpcOpaqueMemoryData
uint64_t handle = 67890;
L0::IpcOpaqueMemoryData opaqueIpcData;
opaqueIpcData.handle.fd = static_cast<int>(handle);
opaqueIpcData.memoryType = static_cast<uint8_t>(InternalMemoryType::sharedUnifiedMemory);
opaqueIpcData.processId = 1234;
opaqueIpcData.type = L0::IpcHandleType::fdHandle;
opaqueIpcData.poolOffset = 0;
uint64_t ptrAddress = 0x2000;
uint8_t type = static_cast<uint8_t>(InternalMemoryType::sharedUnifiedMemory);
// Verify IPC handle map is initially empty
EXPECT_TRUE(driverHandle->getIPCHandleMap().empty());
// Call setIPCHandleData with IpcOpaqueMemoryData type
contextWhitebox.setIPCHandleData<L0::IpcOpaqueMemoryData>(&mockAllocation, handle, opaqueIpcData, ptrAddress, type, nullptr, L0::IpcHandleType::fdHandle);
// Verify the handle was added to the IPC handle map
auto &ipcHandleMap = driverHandle->getIPCHandleMap();
EXPECT_EQ(1u, ipcHandleMap.size());
// Verify the handle tracking entry
auto handleIterator = ipcHandleMap.find(handle);
ASSERT_NE(handleIterator, ipcHandleMap.end());
L0::IpcHandleTracking *handleTracking = handleIterator->second;
EXPECT_NE(nullptr, handleTracking);
EXPECT_EQ(&mockAllocation, handleTracking->alloc);
EXPECT_EQ(1u, handleTracking->refcnt);
EXPECT_EQ(ptrAddress, handleTracking->ptr);
EXPECT_EQ(handle, handleTracking->handle);
// Verify that the ipcData field is NOT set when using IpcOpaqueMemoryData
// The ipcData field should retain its default/empty values since the constexpr condition is false
EXPECT_EQ(0u, handleTracking->ipcData.handle); // Should be default value (0)
EXPECT_EQ(0u, handleTracking->ipcData.type); // Should be default value (0)
EXPECT_EQ(0u, handleTracking->ipcData.poolOffset); // Should be default value (0)
// Clean up - remove the entry from the map to avoid issues in teardown
delete handleTracking;
driverHandle->getIPCHandleMap().clear();
ContextImp *contextImp = static_cast<ContextImp *>(L0::Context::fromHandle(hContext));
res = contextImp->destroy();
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
}
TEST_F(ContextTest, whenCallingSetIPCHandleDataWithInvalidIpcHandleTypeThenHandleUnionIsNotSet) {
ze_context_handle_t hContext;
ze_context_desc_t desc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC, nullptr, 0};
ze_result_t res = driverHandle->createContext(&desc, 0u, nullptr, &hContext);
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
ContextWhiteboxForIpcTesting contextWhitebox(driverHandle.get());
// Create a mock graphics allocation
NEO::MockGraphicsAllocation mockAllocation;
// Set up test data with invalid IpcHandleType
uint64_t handle = 11111;
L0::IpcOpaqueMemoryData opaqueIpcData;
uint64_t ptrAddress = 0x3000;
uint8_t type = static_cast<uint8_t>(InternalMemoryType::deviceUnifiedMemory);
// Test with maxHandle (invalid but defined)
L0::IpcHandleType invalidHandleType1 = L0::IpcHandleType::maxHandle;
// Verify IPC handle map is initially empty
EXPECT_TRUE(driverHandle->getIPCHandleMap().empty());
// Call setIPCHandleData with invalid handle type (maxHandle)
contextWhitebox.setIPCHandleData<L0::IpcOpaqueMemoryData>(&mockAllocation, handle, opaqueIpcData, ptrAddress, type, nullptr, invalidHandleType1);
// Verify the handle was added to the IPC handle map
auto &ipcHandleMap = driverHandle->getIPCHandleMap();
EXPECT_EQ(1u, ipcHandleMap.size());
// Verify the IpcOpaqueMemoryData values
EXPECT_EQ(type, opaqueIpcData.memoryType);
EXPECT_EQ(invalidHandleType1, opaqueIpcData.type);
EXPECT_NE(0u, opaqueIpcData.processId); // Should be set to current process ID
// Clean up
auto handleIterator = ipcHandleMap.find(handle);
ASSERT_NE(handleIterator, ipcHandleMap.end());
delete handleIterator->second;
driverHandle->getIPCHandleMap().clear();
// Test with completely invalid handle type (beyond enum range)
handle = 22222;
opaqueIpcData = {}; // Reset
L0::IpcHandleType invalidHandleType2 = static_cast<L0::IpcHandleType>(255);
contextWhitebox.setIPCHandleData<L0::IpcOpaqueMemoryData>(&mockAllocation, handle, opaqueIpcData, ptrAddress, type, nullptr, invalidHandleType2);
// Verify the handle was added to the IPC handle map
EXPECT_EQ(1u, ipcHandleMap.size());
// Verify the IpcOpaqueMemoryData values
EXPECT_EQ(type, opaqueIpcData.memoryType);
EXPECT_EQ(invalidHandleType2, opaqueIpcData.type);
EXPECT_NE(0u, opaqueIpcData.processId); // Should be set to current process ID
// Clean up
handleIterator = ipcHandleMap.find(handle);
ASSERT_NE(handleIterator, ipcHandleMap.end());
delete handleIterator->second;
driverHandle->getIPCHandleMap().clear();
ContextImp *contextImp = static_cast<ContextImp *>(L0::Context::fromHandle(hContext));
res = contextImp->destroy();
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
}
TEST_F(ContextTest, whenCallingSetIPCHandleDataWithNtHandleTypeThenHandleUnionIsSetCorrectly) {
ze_context_handle_t hContext;
ze_context_desc_t desc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC, nullptr, 0};
ze_result_t res = driverHandle->createContext(&desc, 0u, nullptr, &hContext);
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
ContextWhiteboxForIpcTesting contextWhitebox(driverHandle.get());
// Create a mock graphics allocation
NEO::MockGraphicsAllocation mockAllocation;
// Set up test data for ntHandle
uint64_t handle = 98765;
L0::IpcOpaqueMemoryData opaqueIpcData;
opaqueIpcData.handle.reserved = handle;
opaqueIpcData.memoryType = static_cast<uint8_t>(InternalMemoryType::deviceUnifiedMemory);
opaqueIpcData.processId = 5678;
opaqueIpcData.type = L0::IpcHandleType::ntHandle;
opaqueIpcData.poolOffset = 0;
uint64_t ptrAddress = 0x4000;
uint8_t type = static_cast<uint8_t>(InternalMemoryType::deviceUnifiedMemory);
// Verify IPC handle map is initially empty
EXPECT_TRUE(driverHandle->getIPCHandleMap().empty());
// Call setIPCHandleData with ntHandle type
contextWhitebox.setIPCHandleData<L0::IpcOpaqueMemoryData>(&mockAllocation, handle, opaqueIpcData, ptrAddress, type, nullptr, L0::IpcHandleType::ntHandle);
// Verify the handle was added to the IPC handle map
auto &ipcHandleMap = driverHandle->getIPCHandleMap();
EXPECT_EQ(1u, ipcHandleMap.size());
// Verify the handle tracking entry
auto handleIterator = ipcHandleMap.find(handle);
ASSERT_NE(handleIterator, ipcHandleMap.end());
L0::IpcHandleTracking *handleTracking = handleIterator->second;
EXPECT_NE(nullptr, handleTracking);
EXPECT_EQ(&mockAllocation, handleTracking->alloc);
EXPECT_EQ(1u, handleTracking->refcnt);
EXPECT_EQ(ptrAddress, handleTracking->ptr);
EXPECT_EQ(handle, handleTracking->handle);
// Verify that the ipcData field is NOT set when using IpcOpaqueMemoryData
// The ipcData field should retain its default/empty values since the constexpr condition is false
EXPECT_EQ(0u, handleTracking->ipcData.handle); // Should be default value (0)
EXPECT_EQ(0u, handleTracking->ipcData.type); // Should be default value (0)
EXPECT_EQ(0u, handleTracking->ipcData.poolOffset); // Should be default value (0)
// Verify the IpcOpaqueMemoryData values are set correctly
EXPECT_EQ(type, opaqueIpcData.memoryType);
EXPECT_EQ(L0::IpcHandleType::ntHandle, opaqueIpcData.type);
uint64_t reservedValue = 0;
memcpy(&reservedValue, &opaqueIpcData.handle.reserved, sizeof(reservedValue));
EXPECT_EQ(handle, reservedValue);
unsigned int processID = 0;
memcpy(&processID, &opaqueIpcData.processId, sizeof(processID));
EXPECT_NE(0u, processID); // Should be set to current process ID
// Clean up - remove the entry from the map to avoid issues in teardown
delete handleTracking;
driverHandle->getIPCHandleMap().clear();
ContextImp *contextImp = static_cast<ContextImp *>(L0::Context::fromHandle(hContext));
res = contextImp->destroy();
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
}
} // namespace ult
} // namespace L0

View File

@@ -41,8 +41,8 @@ TEST_F(ContextIsShareable, whenCallingisSharedMemoryThenCorrectResultIsReturned)
bool exportableMemoryFalse = false;
bool exportableMemoryTrue = true;
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice));
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryTrue, neoDevice));
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice, false));
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryTrue, neoDevice, false));
res = contextImp->destroy();
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
@@ -56,7 +56,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithValidHandleThenSuccess
// Test Successfully returning fd Handle
fixtureMemoryManager->ntHandle = false;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithInvalidHandleThenNullptrIsReturned) {
@@ -68,19 +68,20 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithInvalidHandleThenNullp
// Test Failing returning fd Handle
fixtureMemoryManager->ntHandle = false;
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndSyscallsReturnSuccessThenValidHandleIsReturned) {
DebugManagerStateRestore restorer;
debugManager.flags.EnablePidFdOrSocketsForIpc.set(1);
context->contextSettings.enablePidfdOrSockets = true;
VariableBackup<decltype(SysCalls::pidfdopenCalled)> pidfdOpenCalledBackup(&NEO::SysCalls::pidfdopenCalled, 0u);
VariableBackup<decltype(SysCalls::pidfdgetfdCalled)> pidfdGetFdCalledBackup(&NEO::SysCalls::pidfdgetfdCalled, 0u);
uint64_t handle = 57;
// Test Successfully returning fd Handle
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
EXPECT_EQ(1, NEO::SysCalls::pidfdopenCalled);
EXPECT_EQ(1, NEO::SysCalls::pidfdgetfdCalled);
}
@@ -88,6 +89,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndSyscalls
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdOpenSyscallReturnFailThenPidfdGetNotCalled) {
DebugManagerStateRestore restorer;
debugManager.flags.EnablePidFdOrSocketsForIpc.set(1);
context->contextSettings.enablePidfdOrSockets = true;
VariableBackup<decltype(SysCalls::pidfdopenCalled)> pidfdOpenCalledBackup(&NEO::SysCalls::pidfdopenCalled, 0u);
VariableBackup<decltype(SysCalls::pidfdgetfdCalled)> pidfdGetFdCalledBackup(&NEO::SysCalls::pidfdgetfdCalled, 0u);
@@ -97,7 +99,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdOpe
uint64_t handle = 57;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
EXPECT_EQ(1, NEO::SysCalls::pidfdopenCalled);
EXPECT_EQ(0, NEO::SysCalls::pidfdgetfdCalled);
}
@@ -105,6 +107,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdOpe
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdGetSyscallReturnFailThenCorrectHandleIsReturned) {
DebugManagerStateRestore restorer;
debugManager.flags.EnablePidFdOrSocketsForIpc.set(1);
context->contextSettings.enablePidfdOrSockets = true;
VariableBackup<decltype(SysCalls::pidfdopenCalled)> pidfdOpenCalledBackup(&NEO::SysCalls::pidfdopenCalled, 0u);
VariableBackup<decltype(SysCalls::pidfdgetfdCalled)> pidfdGetFdCalledBackup(&NEO::SysCalls::pidfdgetfdCalled, 0u);
@@ -113,7 +116,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdGet
});
uint64_t handle = 57;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
EXPECT_EQ(1, NEO::SysCalls::pidfdopenCalled);
EXPECT_EQ(1, NEO::SysCalls::pidfdgetfdCalled);
}

View File

@@ -5,11 +5,15 @@
*
*/
#include "shared/source/os_interface/linux/sys_calls.h"
#include "shared/test/common/helpers/variable_backup.h"
#include "shared/test/common/mocks/mock_device.h"
#include "shared/test/common/mocks/mock_driver_model.h"
#include "shared/test/common/mocks/mock_memory_manager.h"
#include "shared/test/common/os_interface/linux/sys_calls_linux_ult.h"
#include "shared/test/common/test_macros/test.h"
#include "level_zero/core/source/context/context.h"
#include "level_zero/core/source/context/context_imp.h"
#include "level_zero/core/test/unit_tests/fixtures/device_fixture.h"
@@ -33,17 +37,17 @@ TEST_F(ContextIsShareable, whenCallingisSharedMemoryThenCorrectResultIsReturned)
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModel>());
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice));
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryTrue, neoDevice));
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice, false));
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryTrue, neoDevice, false));
// exportDesc set && neoDevice is NOT WDDM
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(&desc, exportableMemoryFalse, neoDevice));
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(&desc, exportableMemoryFalse, neoDevice, false));
// exportDesc unset && neoDevice is NOT WDDM
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice));
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice, false));
// exportDesc unset && neoDevice is WDDM
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelWDDM>());
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice));
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice, false));
// exportDesc is set && Exportable Memory is False && neoDevice is WDDM
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(&desc, exportableMemoryFalse, neoDevice));
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(&desc, exportableMemoryFalse, neoDevice, false));
res = contextImp->destroy();
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
@@ -57,7 +61,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithValidNTHandleThenSucce
// Test Successfully returning NT Handle
fixtureMemoryManager->ntHandle = true;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithInvalidHandleThenNullptrIsReturned) {
@@ -69,11 +73,11 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithInvalidHandleThenNullp
// Test Failing returning NT Handle
fixtureMemoryManager->ntHandle = true;
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
// Test Failing returning fd Handle
fixtureMemoryManager->ntHandle = false;
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithDRMDriverTypeWithNonNTHandleThenSuccessIsReturned) {
@@ -84,7 +88,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithDRMDriverTypeWithNonNT
// Test Successfully returning fd Handle
fixtureMemoryManager->ntHandle = false;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithWDDMDriverTypeWithNonNTHandleThenNullPtrIsReturned) {
@@ -95,7 +99,418 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithWDDMDriverTypeWithNonN
// Test Successfully returning fd Handle
fixtureMemoryManager->ntHandle = false;
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdOpenSyscallReturnFailThenPidfdGetNotCalled) {
// Enable pidfd/sockets for IPC
context->contextSettings.enablePidfdOrSockets = true;
// Set up DRM driver model to use pidfd method
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
MemoryManagerMemHandleMock *fixtureMemoryManager = static_cast<MemoryManagerMemHandleMock *>(currMemoryManager);
fixtureMemoryManager->ntHandle = false;
VariableBackup<decltype(NEO::SysCalls::pidfdopenCalled)> pidfdOpenCalledBackup(&NEO::SysCalls::pidfdopenCalled, 0u);
VariableBackup<decltype(NEO::SysCalls::pidfdgetfdCalled)> pidfdGetFdCalledBackup(&NEO::SysCalls::pidfdgetfdCalled, 0u);
VariableBackup<decltype(NEO::SysCalls::sysCallsPidfdOpen)> mockPidfdOpen(&NEO::SysCalls::sysCallsPidfdOpen, [](pid_t, unsigned int) -> int {
return -1;
});
uint64_t handle = 57;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
EXPECT_EQ(1, NEO::SysCalls::pidfdopenCalled);
EXPECT_EQ(0, NEO::SysCalls::pidfdgetfdCalled);
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdGetSyscallReturnFailThenCorrectHandleIsReturned) {
// Enable pidfd/sockets for IPC
context->contextSettings.enablePidfdOrSockets = true;
// Set up DRM driver model to use pidfd method
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
MemoryManagerMemHandleMock *fixtureMemoryManager = static_cast<MemoryManagerMemHandleMock *>(currMemoryManager);
fixtureMemoryManager->ntHandle = false;
VariableBackup<decltype(NEO::SysCalls::pidfdopenCalled)> pidfdOpenCalledBackup(&NEO::SysCalls::pidfdopenCalled, 0u);
VariableBackup<decltype(NEO::SysCalls::pidfdgetfdCalled)> pidfdGetFdCalledBackup(&NEO::SysCalls::pidfdgetfdCalled, 0u);
VariableBackup<decltype(NEO::SysCalls::sysCallsPidfdGetfd)> mockPidfdGet(&NEO::SysCalls::sysCallsPidfdGetfd, [](int, int, unsigned int) -> int {
return -1;
});
uint64_t handle = 57;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
EXPECT_EQ(1, NEO::SysCalls::pidfdopenCalled);
EXPECT_EQ(1, NEO::SysCalls::pidfdgetfdCalled);
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdGetSyscallFailsWithNegativeValueThenFallbackHandleIsUsed) {
// Enable pidfd/sockets for IPC
context->contextSettings.enablePidfdOrSockets = true;
// Set up DRM driver model to use pidfd method
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
MemoryManagerMemHandleMock *fixtureMemoryManager = static_cast<MemoryManagerMemHandleMock *>(currMemoryManager);
fixtureMemoryManager->ntHandle = false;
uint64_t originalHandle = 42;
static bool pidfdGetFdCalled = false;
pidfdGetFdCalled = false;
VariableBackup<decltype(NEO::SysCalls::pidfdopenCalled)> pidfdOpenCalledBackup(&NEO::SysCalls::pidfdopenCalled, 0u);
VariableBackup<decltype(NEO::SysCalls::pidfdgetfdCalled)> pidfdGetFdCalledBackup(&NEO::SysCalls::pidfdgetfdCalled, 0u);
// Mock pidfd_open to succeed
VariableBackup<decltype(NEO::SysCalls::sysCallsPidfdOpen)> mockPidfdOpen(&NEO::SysCalls::sysCallsPidfdOpen, [](pid_t, unsigned int) -> int {
return 100; // Valid pidfd
});
// Mock pidfd_getfd to fail with different negative values
VariableBackup<decltype(NEO::SysCalls::sysCallsPidfdGetfd)> mockPidfdGet(&NEO::SysCalls::sysCallsPidfdGetfd, [](int pidfd, int fd, unsigned int flags) -> int {
pidfdGetFdCalled = true;
EXPECT_EQ(100, pidfd); // Should receive the pidfd from pidfd_open
EXPECT_EQ(42, fd); // Should receive the original handle
EXPECT_EQ(0u, flags); // Flags should be 0
return -2; // Fail with a different negative value
});
void *result = context->getMemHandlePtr(device, originalHandle, NEO::AllocationType::buffer, 0u, 0);
EXPECT_NE(nullptr, result);
EXPECT_EQ(1, NEO::SysCalls::pidfdopenCalled);
EXPECT_EQ(1, NEO::SysCalls::pidfdgetfdCalled);
EXPECT_TRUE(pidfdGetFdCalled);
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithPidfdMethodAndPidfdGetSyscallReturnsZeroThenSuccessfulHandleIsUsed) {
// Enable pidfd/sockets for IPC
context->contextSettings.enablePidfdOrSockets = true;
// Set up DRM driver model to use pidfd method
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
MemoryManagerMemHandleMock *fixtureMemoryManager = static_cast<MemoryManagerMemHandleMock *>(currMemoryManager);
fixtureMemoryManager->ntHandle = false;
uint64_t originalHandle = 123;
static bool pidfdGetFdCalled = false;
pidfdGetFdCalled = false;
VariableBackup<decltype(NEO::SysCalls::pidfdopenCalled)> pidfdOpenCalledBackup(&NEO::SysCalls::pidfdopenCalled, 0u);
VariableBackup<decltype(NEO::SysCalls::pidfdgetfdCalled)> pidfdGetFdCalledBackup(&NEO::SysCalls::pidfdgetfdCalled, 0u);
// Mock pidfd_open to succeed
VariableBackup<decltype(NEO::SysCalls::sysCallsPidfdOpen)> mockPidfdOpen(&NEO::SysCalls::sysCallsPidfdOpen, [](pid_t, unsigned int) -> int {
return 200; // Valid pidfd
});
// Mock pidfd_getfd to return 0 (which is a valid fd but edge case)
VariableBackup<decltype(NEO::SysCalls::sysCallsPidfdGetfd)> mockPidfdGet(&NEO::SysCalls::sysCallsPidfdGetfd, [](int pidfd, int fd, unsigned int flags) -> int {
pidfdGetFdCalled = true;
EXPECT_EQ(200, pidfd);
EXPECT_EQ(123, fd);
EXPECT_EQ(0u, flags);
return 0; // Return 0 (valid fd)
});
void *result = context->getMemHandlePtr(device, originalHandle, NEO::AllocationType::buffer, 0u, 0);
EXPECT_NE(nullptr, result);
EXPECT_EQ(1, NEO::SysCalls::pidfdopenCalled);
EXPECT_EQ(1, NEO::SysCalls::pidfdgetfdCalled);
EXPECT_TRUE(pidfdGetFdCalled);
}
using GetDataFromIpcHandleTest = Test<GetMemHandlePtrTestFixture>;
TEST_F(GetDataFromIpcHandleTest, whenCallingGetDataFromIpcHandleWithOpaqueHandleEnabledThenOpaqueDataIsExtracted) {
// Enable opaque handles
context->contextSettings.enablePidfdOrSockets = true;
ze_ipc_mem_handle_t ipcHandle = {};
IpcOpaqueMemoryData *opaqueData = reinterpret_cast<IpcOpaqueMemoryData *>(ipcHandle.data);
opaqueData->handle.fd = 123;
opaqueData->memoryType = 42;
opaqueData->processId = 456;
opaqueData->poolOffset = 789;
uint64_t handle = 0;
uint8_t type = 0;
unsigned int processId = 0;
uint64_t poolOffset = 0;
context->getDataFromIpcHandle(device, ipcHandle, handle, type, processId, poolOffset);
EXPECT_EQ(123u, handle);
EXPECT_EQ(42u, type);
EXPECT_EQ(456u, processId);
EXPECT_EQ(789u, poolOffset);
}
TEST_F(GetDataFromIpcHandleTest, whenCallingGetDataFromIpcHandleWithOpaqueHandleDisabledThenRegularDataIsExtracted) {
// Disable opaque handles
context->contextSettings.enablePidfdOrSockets = false;
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
ze_ipc_mem_handle_t ipcHandle = {};
IpcMemoryData *regularData = reinterpret_cast<IpcMemoryData *>(ipcHandle.data);
regularData->handle = 987;
regularData->type = 65;
regularData->poolOffset = 321;
uint64_t handle = 0;
uint8_t type = 0;
unsigned int processId = 0;
uint64_t poolOffset = 0;
context->getDataFromIpcHandle(device, ipcHandle, handle, type, processId, poolOffset);
EXPECT_EQ(987u, handle);
EXPECT_EQ(65u, type);
EXPECT_EQ(321u, poolOffset);
// processId should remain 0 for regular data
EXPECT_EQ(0u, processId);
}
TEST_F(GetDataFromIpcHandleTest, whenCallingGetDataFromIpcHandleWithWDDMDriverThenOpaqueHandleIsForced) {
// Initially disable opaque handles
context->contextSettings.enablePidfdOrSockets = false;
// Set WDDM driver model which should force opaque handles
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelWDDM>());
ze_ipc_mem_handle_t ipcHandle = {};
IpcOpaqueMemoryData *opaqueData = reinterpret_cast<IpcOpaqueMemoryData *>(ipcHandle.data);
opaqueData->handle.fd = 555;
opaqueData->memoryType = 77;
opaqueData->processId = 888;
opaqueData->poolOffset = 999;
uint64_t handle = 0;
uint8_t type = 0;
unsigned int processId = 0;
uint64_t poolOffset = 0;
context->getDataFromIpcHandle(device, ipcHandle, handle, type, processId, poolOffset);
// Should extract opaque data even though enablePidfdOrSockets was false
EXPECT_EQ(555u, handle);
EXPECT_EQ(77u, type);
EXPECT_EQ(888u, processId);
EXPECT_EQ(999u, poolOffset);
}
TEST_F(GetDataFromIpcHandleTest, whenCallingGetDataFromIpcHandleWithNullOSInterfaceThenRegularDataIsExtracted) {
// Disable opaque handles and set null osInterface
context->contextSettings.enablePidfdOrSockets = false;
neoDevice->executionEnvironment->rootDeviceEnvironments[0]->osInterface.reset();
ze_ipc_mem_handle_t ipcHandle = {};
IpcMemoryData *regularData = reinterpret_cast<IpcMemoryData *>(ipcHandle.data);
regularData->handle = 111;
regularData->type = 22;
regularData->poolOffset = 333;
uint64_t handle = 0;
uint8_t type = 0;
unsigned int processId = 0;
uint64_t poolOffset = 0;
context->getDataFromIpcHandle(device, ipcHandle, handle, type, processId, poolOffset);
EXPECT_EQ(111u, handle);
EXPECT_EQ(22u, type);
EXPECT_EQ(333u, poolOffset);
EXPECT_EQ(0u, processId);
}
TEST_F(GetDataFromIpcHandleTest, whenCallingGetDataFromIpcHandleWithZeroValuesThenZeroValuesAreExtracted) {
context->contextSettings.enablePidfdOrSockets = true;
ze_ipc_mem_handle_t ipcHandle = {};
// All values should be zero by default
uint64_t handle = 999; // Initialize with non-zero to verify it gets overwritten
uint8_t type = 99;
unsigned int processId = 888;
uint64_t poolOffset = 777;
context->getDataFromIpcHandle(device, ipcHandle, handle, type, processId, poolOffset);
EXPECT_EQ(0u, handle);
EXPECT_EQ(0u, type);
EXPECT_EQ(0u, processId);
EXPECT_EQ(0u, poolOffset);
}
inline int mockPrctl(int option, unsigned long arg) {
return 0;
}
using IsOpaqueHandleSupportedTest = Test<GetMemHandlePtrTestFixture>;
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedWithWDDMDriverThenTrueIsReturnedAndHandleTypeIsNT) {
// Set WDDM driver model on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
executionEnvironment.rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelWDDM>());
// Disable opaque handles in settings (should be overridden by WDDM)
context->contextSettings.enablePidfdOrSockets = false;
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_TRUE(result);
EXPECT_EQ(IpcHandleType::ntHandle, handleType);
}
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedWithDRMDriverAndOpaqueEnabledThenTrueIsReturnedAndHandleTypeIsFd) {
// Set DRM driver model on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
executionEnvironment.rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
VariableBackup<decltype(NEO::SysCalls::sysCallsPrctl)> sysCallsPrctlBackup{&NEO::SysCalls::sysCallsPrctl, mockPrctl};
// Enable opaque handles in settings
context->contextSettings.enablePidfdOrSockets = true;
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_TRUE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
}
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedWithDRMDriverAndOpaqueDisabledThenFalseIsReturnedAndHandleTypeIsFd) {
// Set DRM driver model on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
executionEnvironment.rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
VariableBackup<decltype(NEO::SysCalls::sysCallsPrctl)> sysCallsPrctlBackup{&NEO::SysCalls::sysCallsPrctl, mockPrctl};
// Disable opaque handles in settings
context->contextSettings.enablePidfdOrSockets = false;
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_FALSE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
}
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedWithNullOSInterfaceThenSettingsValueIsReturnedAndHandleTypeIsFd) {
// Set null OS interface (unknown driver model) on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset();
// Test with opaque handles enabled
context->contextSettings.enablePidfdOrSockets = true;
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_TRUE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
// Test with opaque handles disabled
context->contextSettings.enablePidfdOrSockets = false;
handleType = IpcHandleType::maxHandle;
result = context->isOpaqueHandleSupported(&handleType);
EXPECT_FALSE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
}
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedWithUnknownDriverModelThenSettingsValueIsReturnedAndHandleTypeIsFd) {
// Set mock driver model (unknown type) on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
executionEnvironment.rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModel>());
// Test with opaque handles enabled
context->contextSettings.enablePidfdOrSockets = true;
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_TRUE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
// Test with opaque handles disabled
context->contextSettings.enablePidfdOrSockets = false;
handleType = IpcHandleType::maxHandle;
result = context->isOpaqueHandleSupported(&handleType);
EXPECT_FALSE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
}
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedMultipleTimesWithSameConfigurationThenConsistentResultsAreReturned) {
// Set WDDM driver model on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
executionEnvironment.rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelWDDM>());
context->contextSettings.enablePidfdOrSockets = false;
// Call multiple times and verify consistent results
for (int i = 0; i < 3; i++) {
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_TRUE(result);
EXPECT_EQ(IpcHandleType::ntHandle, handleType);
}
}
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedWithDRMDriverAndOpaqueEnabledThenPrctlIsCalledSuccessfully) {
// Set DRM driver model on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
executionEnvironment.rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
VariableBackup<decltype(NEO::SysCalls::sysCallsPrctl)> sysCallsPrctlBackup{&NEO::SysCalls::sysCallsPrctl, mockPrctl};
// Enable opaque handles in settings (this will trigger the prctl call)
context->contextSettings.enablePidfdOrSockets = true;
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_TRUE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
}
TEST_F(IsOpaqueHandleSupportedTest, whenCallingIsOpaqueHandleSupportedWithDRMDriverAndOpaqueEnabledAndPrctlFailsThenFalseIsReturnedAndHandleTypeIsFd) {
// Set DRM driver model on the execution environment that the context will actually use
auto &executionEnvironment = driverHandle->getMemoryManager()->peekExecutionEnvironment();
executionEnvironment.rootDeviceEnvironments[0]->osInterface.reset(new NEO::OSInterface());
executionEnvironment.rootDeviceEnvironments[0]->osInterface->setDriverModel(std::make_unique<NEO::MockDriverModelDRM>());
// Enable opaque handles in settings (this will trigger the prctl call)
context->contextSettings.enablePidfdOrSockets = true;
// Save original sysCallsPrctl and override to simulate failure
auto originalSysCallsPrctl = NEO::SysCalls::sysCallsPrctl;
NEO::SysCalls::sysCallsPrctl = [](int, unsigned long) -> int {
return -1; // Simulate failure
};
IpcHandleType handleType = IpcHandleType::maxHandle;
bool result = context->isOpaqueHandleSupported(&handleType);
EXPECT_FALSE(result);
EXPECT_EQ(IpcHandleType::fdHandle, handleType);
// Restore original sysCallsPrctl
NEO::SysCalls::sysCallsPrctl = originalSysCallsPrctl;
}
} // namespace ult

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Intel Corporation
* Copyright (C) 2020-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -39,8 +39,8 @@ TEST_F(ContextIsShareable, whenCallingisSharedMemoryThenCorrectResultIsReturned)
bool exportableMemoryFalse = false;
bool exportableMemoryTrue = true;
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice));
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryTrue, neoDevice));
EXPECT_EQ(exportableMemoryFalse, contextImp->isShareableMemory(nullptr, exportableMemoryFalse, neoDevice, false));
EXPECT_EQ(exportableMemoryTrue, contextImp->isShareableMemory(nullptr, exportableMemoryTrue, neoDevice, false));
res = contextImp->destroy();
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
@@ -54,7 +54,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithValidHandleThenSuccess
// Test Successfully returning NT Handle
fixtureMemoryManager->ntHandle = true;
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_NE(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithInvalidHandleThenNullptrIsReturned) {
@@ -66,7 +66,7 @@ TEST_F(GetMemHandlePtrTest, whenCallingGetMemHandlePtrWithInvalidHandleThenNullp
// Test Failing returning NT Handle
fixtureMemoryManager->ntHandle = true;
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0));
EXPECT_EQ(nullptr, context->getMemHandlePtr(device, handle, NEO::AllocationType::buffer, 0u, 0));
}
} // namespace ult

View File

@@ -358,7 +358,7 @@ HWTEST_F(ImportNTHandleWithMockMemoryManager, givenCallToImportNTHandleWithHostB
uint64_t imageHandle = 0x1;
NEO::AllocationType allocationType = NEO::AllocationType::bufferHostMemory;
void *ptr = driverHandle->importNTHandle(device->toHandle(), &imageHandle, allocationType);
void *ptr = driverHandle->importNTHandle(device->toHandle(), &imageHandle, allocationType, 0u);
EXPECT_NE(ptr, nullptr);
auto allocData = driverHandle->svmAllocsManager->getSVMAlloc(ptr);
@@ -376,7 +376,7 @@ HWTEST_F(ImportNTHandleWithMockMemoryManager, givenCallToImportNTHandleWithBuffe
uint64_t imageHandle = 0x1;
NEO::AllocationType allocationType = NEO::AllocationType::buffer;
void *ptr = driverHandle->importNTHandle(device->toHandle(), &imageHandle, allocationType);
void *ptr = driverHandle->importNTHandle(device->toHandle(), &imageHandle, allocationType, 0u);
EXPECT_NE(ptr, nullptr);
auto allocData = driverHandle->svmAllocsManager->getSVMAlloc(ptr);

View File

@@ -5879,7 +5879,7 @@ struct ContextMultiDeviceMock : public L0::ContextImp {
alignedFree(const_cast<void *>(ptr));
return ZE_RESULT_SUCCESS;
}
bool isShareableMemory(const void *pNext, bool exportableMemory, NEO::Device *neoDevice) override {
bool isShareableMemory(const void *pNext, bool exportableMemory, NEO::Device *neoDevice, bool shareableWithoutNTHandle) override {
return true;
}
};

View File

@@ -30,6 +30,7 @@ struct AllocUsmPoolMemoryTest : public ::testing::Test {
NEO::debugManager.flags.EnableHostUsmAllocationPool.set(hostUsmPoolFlag);
NEO::debugManager.flags.EnableDeviceUsmAllocationPool.set(deviceUsmPoolFlag);
NEO::debugManager.flags.ExperimentalUSMAllocationReuseVersion.set(poolingVersionFlag);
NEO::debugManager.flags.EnableShareableWithoutNTHandle.set(false);
executionEnvironment = new NEO::ExecutionEnvironment();
executionEnvironment->prepareRootDeviceEnvironments(numRootDevices);

View File

@@ -78,6 +78,7 @@ DECLARE_DEBUG_VARIABLE(bool, DisableForceToStateless, false, "Do not force state
DECLARE_DEBUG_VARIABLE(bool, ForceTheoreticalMaxWorkGroupCount, false, "Do not apply any limitation to max cooperative/concurrent work-group count queries")
DECLARE_DEBUG_VARIABLE(bool, AppendMemoryPrefetchForKmdMigratedSharedAllocations, true, "Allow prefetching shared memory to the device associated with the specified command list")
DECLARE_DEBUG_VARIABLE(bool, ForceMemoryPrefetchForKmdMigratedSharedAllocations, false, "Force prefetch of shared memory in command queue execute command lists")
DECLARE_DEBUG_VARIABLE(bool, EnableShareableWithoutNTHandle, true, "Enable creating shareable allocations without NT handle on Windows")
DECLARE_DEBUG_VARIABLE(bool, ClKhrExternalMemoryExtension, true, "Enable cl_khr_external_memory extension")
DECLARE_DEBUG_VARIABLE(bool, WaitForMemoryRelease, false, "Wait for memory release when out of memory")
DECLARE_DEBUG_VARIABLE(bool, RemoveRestrictionsOnNumberOfThreadsInGpgpuThreadGroup, 0, "0 - default disabled, 1- remove restrictions on NumberOfThreadsInGpgpuThreadGroup in INTERFACE_DESCRIPTOR_DATA")

View File

@@ -28,7 +28,8 @@ struct AllocationProperties {
uint32_t forceSystemMemory : 1;
uint32_t preferCompressed : 1;
uint32_t cantBeReadOnly : 1;
uint32_t reserved : 18;
uint32_t shareableWithoutNTHandle : 1;
uint32_t reserved : 16;
} flags;
uint32_t allFlags = 0;
};
@@ -112,7 +113,8 @@ struct AllocationData {
uint32_t isUSMDeviceMemory : 1;
uint32_t zeroMemory : 1;
uint32_t cantBeReadOnly : 1;
uint32_t reserved : 15;
uint32_t shareableWithoutNTHandle : 1;
uint32_t reserved : 14;
} flags;
uint32_t allFlags = 0;
};

View File

@@ -643,6 +643,7 @@ bool MemoryManager::getAllocationData(AllocationData &allocationData, const Allo
allocationData.forceKMDAllocation = properties.forceKMDAllocation;
allocationData.makeGPUVaDifferentThanCPUPtr = properties.makeGPUVaDifferentThanCPUPtr;
allocationData.flags.shareable = properties.flags.shareable;
allocationData.flags.shareableWithoutNTHandle = properties.flags.shareableWithoutNTHandle;
allocationData.flags.isUSMDeviceMemory = properties.flags.isUSMDeviceAllocation;
allocationData.flags.requiresCpuAccess = GraphicsAllocation::isCpuAccessRequired(properties.allocationType);
allocationData.flags.allocateMemory = properties.flags.allocateMemory;

View File

@@ -111,6 +111,7 @@ class MemoryManager {
struct OsHandleData {
osHandle handle;
uint32_t arrayIndex;
uint32_t parentProcessId = 0;
OsHandleData(uint64_t handle, uint32_t arrayIndex = 0) : handle(static_cast<osHandle>(handle)), arrayIndex(arrayIndex){};
OsHandleData(void *handle, uint32_t arrayIndex = 0) : handle(toOsHandle(handle)), arrayIndex(arrayIndex){};

View File

@@ -506,6 +506,7 @@ void *SVMAllocsManager::createUnifiedMemoryAllocation(size_t size,
unifiedMemoryProperties.alignment = alignUpNonZero<size_t>(memoryProperties.alignment, pageSizeForAlignment);
unifiedMemoryProperties.flags.isUSMDeviceAllocation = false;
unifiedMemoryProperties.flags.shareable = memoryProperties.allocationFlags.flags.shareable;
unifiedMemoryProperties.flags.shareableWithoutNTHandle = memoryProperties.allocationFlags.flags.shareableWithoutNTHandle;
unifiedMemoryProperties.cacheRegion = MemoryPropertiesHelper::getCacheRegion(memoryProperties.allocationFlags);
unifiedMemoryProperties.flags.uncacheable = memoryProperties.allocationFlags.flags.locallyUncachedResource;
unifiedMemoryProperties.flags.preferCompressed = compressionEnabled || memoryProperties.allocationFlags.flags.compressedHint;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2021 Intel Corporation
* Copyright (C) 2019-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -30,6 +30,7 @@ struct MemoryFlags {
uint32_t resource48Bit : 1;
uint32_t compressedHint : 1;
uint32_t uncompressedHint : 1;
uint32_t shareableWithoutNTHandle : 1;
};
struct MemoryAllocFlags {

View File

@@ -13,6 +13,7 @@
#include <iostream>
#include <poll.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -59,6 +60,8 @@ long sysconf(int name);
int mkfifo(const char *pathname, mode_t mode);
int pidfdopen(pid_t pid, unsigned int flags);
int pidfdgetfd(int pidfd, int targetfd, unsigned int flags);
int prctl(int option, unsigned long arg);
char **getEnviron();
} // namespace SysCalls
} // namespace NEO

View File

@@ -205,6 +205,10 @@ int pidfdgetfd(int pidfd, int targetfd, unsigned int flags) {
return static_cast<int>(retval);
}
int prctl(int option, unsigned long arg) {
return ::prctl(option, arg);
}
off_t lseek(int fd, off_t offset, int whence) noexcept {
return ::lseek(fd, offset, whence);
}

View File

@@ -36,6 +36,14 @@ unsigned int getCurrentProcessId() {
return GetCurrentProcessId();
}
BOOL duplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) {
return DuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
}
HANDLE openProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
return OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
}
unsigned long getNumThreads() {
return 1;
}

View File

@@ -45,6 +45,8 @@ BOOL findNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData);
BOOL findClose(HANDLE hFindFile);
DWORD getFileAttributesA(LPCSTR lpFileName);
DWORD setFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod);
BOOL duplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
HANDLE openProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
void setProcessPowerThrottlingState(ProcessPowerThrottlingState state);
void setThreadPriority(ThreadPriority priority);

View File

@@ -18,4 +18,8 @@ bool Wddm::getReadOnlyFlagValue(const void *cpuPtr) const {
bool Wddm::isReadOnlyFlagFallbackSupported() const {
return false;
}
HANDLE Wddm::getSharedHandle(const MemoryManager::OsHandleData &osHandleData) {
HANDLE sharedNtHandle = reinterpret_cast<HANDLE>(static_cast<uintptr_t>(osHandleData.handle));
return sharedNtHandle;
}
} // namespace NEO

View File

@@ -6,6 +6,7 @@
*/
#include "shared/source/os_interface/windows/gdi_interface.h"
#include "shared/source/os_interface/windows/sys_calls.h"
#include "shared/source/os_interface/windows/wddm/wddm.h"
namespace NEO {
@@ -23,4 +24,41 @@ bool Wddm::getReadOnlyFlagValue(const void *cpuPtr) const {
bool Wddm::isReadOnlyFlagFallbackSupported() const {
return true;
}
HANDLE Wddm::getSharedHandle(const MemoryManager::OsHandleData &osHandleData) {
HANDLE sharedNtHandle = reinterpret_cast<HANDLE>(static_cast<uintptr_t>(osHandleData.handle));
if (osHandleData.parentProcessId != 0) {
// Open the parent process handle with required access rights
HANDLE parentProcessHandle = NEO::SysCalls::openProcess(PROCESS_DUP_HANDLE, FALSE, static_cast<DWORD>(osHandleData.parentProcessId));
if (parentProcessHandle == nullptr) {
DEBUG_BREAK_IF(true);
return sharedNtHandle;
}
// Duplicate the handle from the parent process to the current process
// This is necessary to ensure that the handle can be used in the current process context
// We use GENERIC_READ | GENERIC_WRITE to ensure we can perform operations on the handle
HANDLE duplicatedHandle = nullptr;
BOOL duplicateResult = NEO::SysCalls::duplicateHandle(
parentProcessHandle,
reinterpret_cast<HANDLE>(static_cast<uintptr_t>(osHandleData.handle)),
GetCurrentProcess(),
&duplicatedHandle,
GENERIC_READ | GENERIC_WRITE,
FALSE,
0);
// Close the parent process handle as we no longer need it
// The duplicated handle will be used for further operations
NEO::SysCalls::closeHandle(parentProcessHandle);
if (!duplicateResult) {
DEBUG_BREAK_IF(true);
return sharedNtHandle;
}
sharedNtHandle = duplicatedHandle;
}
return sharedNtHandle;
}
} // namespace NEO

View File

@@ -664,6 +664,10 @@ bool Wddm::isReadOnlyFlagFallbackAvailable(const D3DKMT_CREATEALLOCATION &create
}
NTSTATUS Wddm::createAllocation(const void *cpuPtr, const Gmm *gmm, D3DKMT_HANDLE &outHandle, D3DKMT_HANDLE &outResourceHandle, uint64_t *outSharedHandle) {
return createAllocation(cpuPtr, gmm, outHandle, outResourceHandle, outSharedHandle, true);
}
NTSTATUS Wddm::createAllocation(const void *cpuPtr, const Gmm *gmm, D3DKMT_HANDLE &outHandle, D3DKMT_HANDLE &outResourceHandle, uint64_t *outSharedHandle, bool createNTHandle) {
NTSTATUS status = STATUS_UNSUCCESSFUL;
D3DDDI_ALLOCATIONINFO2 allocationInfo = {};
D3DKMT_CREATEALLOCATION createAllocation = {};
@@ -704,7 +708,7 @@ NTSTATUS Wddm::createAllocation(const void *cpuPtr, const Gmm *gmm, D3DKMT_HANDL
outHandle = allocationInfo.hAllocation;
outResourceHandle = createAllocation.hResource;
if (outSharedHandle) {
if (outSharedHandle && createNTHandle) {
HANDLE ntSharedHandle = NULL;
status = this->createNTHandle(&outResourceHandle, &ntSharedHandle);
if (status != STATUS_SUCCESS) {
@@ -922,7 +926,8 @@ bool Wddm::verifyNTHandle(HANDLE handle) {
bool Wddm::openNTHandle(const MemoryManager::OsHandleData &osHandleData, WddmAllocation *alloc) {
D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE queryResourceInfoFromNtHandle = {};
queryResourceInfoFromNtHandle.hDevice = device;
queryResourceInfoFromNtHandle.hNtHandle = reinterpret_cast<HANDLE>(static_cast<uintptr_t>(osHandleData.handle));
HANDLE sharedNtHandle = this->getSharedHandle(osHandleData);
queryResourceInfoFromNtHandle.hNtHandle = sharedNtHandle;
[[maybe_unused]] auto status = getGdi()->queryResourceInfoFromNtHandle(&queryResourceInfoFromNtHandle);
DEBUG_BREAK_IF(status != STATUS_SUCCESS);
@@ -938,7 +943,7 @@ bool Wddm::openNTHandle(const MemoryManager::OsHandleData &osHandleData, WddmAll
D3DKMT_OPENRESOURCEFROMNTHANDLE openResourceFromNtHandle = {};
openResourceFromNtHandle.hDevice = device;
openResourceFromNtHandle.hNtHandle = reinterpret_cast<HANDLE>(static_cast<uintptr_t>(osHandleData.handle));
openResourceFromNtHandle.hNtHandle = sharedNtHandle;
openResourceFromNtHandle.NumAllocations = queryResourceInfoFromNtHandle.NumAllocations;
openResourceFromNtHandle.pOpenAllocationInfo2 = allocationInfo2.get();
openResourceFromNtHandle.pTotalPrivateDriverDataBuffer = allocPrivateData.get();

View File

@@ -74,6 +74,7 @@ class Wddm : public DriverModel {
MOCKABLE_VIRTUAL uint64_t freeGmmGpuVirtualAddress(Gmm *gmm, D3DGPU_VIRTUAL_ADDRESS &gpuPtr, uint64_t size);
MOCKABLE_VIRTUAL bool freeGpuVirtualAddress(D3DGPU_VIRTUAL_ADDRESS &gpuPtr, uint64_t size);
MOCKABLE_VIRTUAL NTSTATUS createAllocation(const void *cpuPtr, const Gmm *gmm, D3DKMT_HANDLE &outHandle, D3DKMT_HANDLE &outResourceHandle, uint64_t *outSharedHandle);
MOCKABLE_VIRTUAL NTSTATUS createAllocation(const void *cpuPtr, const Gmm *gmm, D3DKMT_HANDLE &outHandle, D3DKMT_HANDLE &outResourceHandle, uint64_t *outSharedHandle, bool createNTHandle);
MOCKABLE_VIRTUAL bool createAllocation(const Gmm *gmm, D3DKMT_HANDLE &outHandle);
MOCKABLE_VIRTUAL NTSTATUS createAllocationsAndMapGpuVa(OsHandleStorage &osHandles);
MOCKABLE_VIRTUAL bool destroyAllocations(const D3DKMT_HANDLE *handles, uint32_t allocationCount, D3DKMT_HANDLE resourceHandle);
@@ -91,6 +92,7 @@ class Wddm : public DriverModel {
MOCKABLE_VIRTUAL bool destroyContext(D3DKMT_HANDLE context);
MOCKABLE_VIRTUAL bool queryAdapterInfo();
MOCKABLE_VIRTUAL NTSTATUS createNTHandle(const D3DKMT_HANDLE *resourceHandle, HANDLE *ntHandle);
MOCKABLE_VIRTUAL HANDLE getSharedHandle(const MemoryManager::OsHandleData &osHandleData);
MOCKABLE_VIRTUAL bool submit(uint64_t commandBuffer, size_t size, void *commandHeader, WddmSubmitArguments &submitArguments);
MOCKABLE_VIRTUAL bool waitFromCpu(uint64_t lastFenceValue, const MonitoredFence &monitoredFence, bool busyWait);

View File

@@ -90,6 +90,9 @@ class WddmAllocation : public GraphicsAllocation {
void setMakeResidentBeforeLockRequired(bool makeResidentBeforeLockRequired) { this->makeResidentBeforeLockRequired = makeResidentBeforeLockRequired; }
bool isAllocInFrontWindowPool() const { return allocInFrontWindowPool; }
void setAllocInFrontWindowPool(bool allocInFrontWindowPool) { this->allocInFrontWindowPool = allocInFrontWindowPool; }
bool isShareable() const { return shareable; }
bool isShareableWithoutNTHandle() const { return shareableWithoutNTHandle; }
void setShareableWithoutNTHandle(bool shareableWithoutNTHandle) { this->shareableWithoutNTHandle = shareableWithoutNTHandle; }
bool isPhysicalMemoryReservation() const { return physicalMemoryReservation; }
void setPhysicalMemoryReservation(bool physicalMemoryReservation) { this->physicalMemoryReservation = physicalMemoryReservation; }
bool isMappedPhysicalMemoryReservation() const { return mappedPhysicalMemoryReservation; }
@@ -125,5 +128,6 @@ class WddmAllocation : public GraphicsAllocation {
bool physicalMemoryReservation = false;
bool mappedPhysicalMemoryReservation = false;
bool makeResidentBeforeLockRequired = false;
bool shareableWithoutNTHandle = false;
};
} // namespace NEO

View File

@@ -147,6 +147,7 @@ GraphicsAllocation *WddmMemoryManager::allocatePhysicalDeviceMemory(const Alloca
1u, // numGmms
allocationData.type, nullptr, 0, allocationData.size, nullptr,
MemoryPool::systemCpuInaccessible, allocationData.flags.shareable, maxOsContextCount);
allocation->setShareableWithoutNTHandle(allocationData.flags.shareableWithoutNTHandle);
allocation->setDefaultGmm(gmm.get());
if (!createPhysicalAllocation(allocation.get())) {
return nullptr;
@@ -175,6 +176,7 @@ GraphicsAllocation *WddmMemoryManager::allocateMemoryByKMD(const AllocationData
1u, // numGmms
allocationData.type, nullptr, 0, allocationData.size, nullptr,
MemoryPool::systemCpuInaccessible, allocationData.flags.shareable, maxOsContextCount);
allocation->setShareableWithoutNTHandle(allocationData.flags.shareableWithoutNTHandle);
allocation->setDefaultGmm(gmm.get());
void *requiredGpuVa = nullptr;
adjustGpuPtrToHostAddressSpace(*allocation.get(), requiredGpuVa);
@@ -616,7 +618,6 @@ bool WddmMemoryManager::isNTHandle(osHandle handle, uint32_t rootDeviceIndex) {
GraphicsAllocation *WddmMemoryManager::createGraphicsAllocationFromSharedHandle(const OsHandleData &osHandleData, const AllocationProperties &properties, bool requireSpecificBitness, bool isHostIpcAllocation, bool reuseSharedAllocation, void *mapPointer) {
auto allocation = std::make_unique<WddmAllocation>(properties.rootDeviceIndex, 1u /*num gmms*/, properties.allocationType, nullptr, 0, osHandleData.handle, MemoryPool::systemCpuInaccessible, maxOsContextCount, 0llu);
bool status;
if (verifyHandle(osHandleData.handle, properties.rootDeviceIndex, false))
status = getWddm(properties.rootDeviceIndex).openSharedHandle(osHandleData, allocation.get());
@@ -1131,10 +1132,11 @@ bool WddmMemoryManager::mapMultiHandleAllocationWithRetry(WddmAllocation *alloca
bool WddmMemoryManager::createGpuAllocationsWithRetry(WddmAllocation *allocation) {
for (auto handleId = 0u; handleId < allocation->getNumGmms(); handleId++) {
auto gmm = allocation->getGmm(handleId);
auto status = getWddm(allocation->getRootDeviceIndex()).createAllocation(allocation->getUnderlyingBuffer(), gmm, allocation->getHandleToModify(handleId), allocation->getResourceHandleToModify(), allocation->getSharedHandleToModify());
bool createNTHandle = allocation->isShareable() && !allocation->isShareableWithoutNTHandle();
auto status = getWddm(allocation->getRootDeviceIndex()).createAllocation(allocation->getUnderlyingBuffer(), gmm, allocation->getHandleToModify(handleId), allocation->getResourceHandleToModify(), allocation->getSharedHandleToModify(), createNTHandle);
if (status == STATUS_GRAPHICS_NO_VIDEO_MEMORY && deferredDeleter) {
deferredDeleter->drain(true, false);
status = getWddm(allocation->getRootDeviceIndex()).createAllocation(allocation->getUnderlyingBuffer(), gmm, allocation->getHandleToModify(handleId), allocation->getResourceHandleToModify(), allocation->getSharedHandleToModify());
status = getWddm(allocation->getRootDeviceIndex()).createAllocation(allocation->getUnderlyingBuffer(), gmm, allocation->getHandleToModify(handleId), allocation->getResourceHandleToModify(), allocation->getSharedHandleToModify(), createNTHandle);
}
if (status != STATUS_SUCCESS) {
getWddm(allocation->getRootDeviceIndex()).destroyAllocations(&allocation->getHandles()[0], handleId, allocation->getResourceHandle());
@@ -1368,6 +1370,7 @@ GraphicsAllocation *WddmMemoryManager::allocatePhysicalLocalDeviceMemory(const A
auto wddmAllocation = std::make_unique<WddmAllocation>(allocationData.rootDeviceIndex, singleBankAllocation ? numGmms : numBanks,
allocationData.type, nullptr, 0, sizeAligned, nullptr, MemoryPool::localMemory, allocationData.flags.shareable, maxOsContextCount);
wddmAllocation->setShareableWithoutNTHandle(allocationData.flags.shareableWithoutNTHandle);
if (singleBankAllocation) {
if (numGmms > 1) {
splitGmmsInAllocation(gmmHelper, wddmAllocation.get(), alignment, chunkSize, const_cast<StorageInfo &>(allocationData.storageInfo));
@@ -1463,6 +1466,7 @@ GraphicsAllocation *WddmMemoryManager::allocateGraphicsMemoryInDevicePool(const
auto wddmAllocation = std::make_unique<WddmAllocation>(allocationData.rootDeviceIndex, singleBankAllocation ? numGmms : numBanks,
allocationData.type, nullptr, 0, sizeAligned, nullptr, MemoryPool::localMemory, allocationData.flags.shareable, maxOsContextCount);
wddmAllocation->setShareableWithoutNTHandle(allocationData.flags.shareableWithoutNTHandle);
if (singleBankAllocation) {
if (numGmms > 1) {
splitGmmsInAllocation(gmmHelper, wddmAllocation.get(), alignment, chunkSize, const_cast<StorageInfo &>(allocationData.storageInfo));

View File

@@ -141,6 +141,30 @@ NTSTATUS WddmMock::createAllocation(const void *alignedCpuPtr, const Gmm *gmm, D
return createAllocationStatus;
}
NTSTATUS WddmMock::createAllocation(const void *alignedCpuPtr, const Gmm *gmm, D3DKMT_HANDLE &outHandle, D3DKMT_HANDLE &outResourceHandle, uint64_t *outSharedHandle, bool createNTHandle) {
createAllocationResult.called++;
if (failCreateAllocation) {
return STATUS_NO_MEMORY;
}
if (callBaseDestroyAllocations) {
createAllocationStatus = Wddm::createAllocation(alignedCpuPtr, gmm, outHandle, outResourceHandle, outSharedHandle, createNTHandle);
createAllocationResult.success = createAllocationStatus == STATUS_SUCCESS;
if (createAllocationStatus != STATUS_SUCCESS) {
destroyAllocationResult.called++;
}
} else {
createAllocationResult.success = true;
outHandle = ALLOCATION_HANDLE;
outResourceHandle = ALLOCATION_HANDLE;
if (outSharedHandle && !createNTHandle) {
// For shared allocations without NT handle, set a special value
*outSharedHandle = 1u;
}
return createAllocationStatus;
}
return createAllocationStatus;
}
bool WddmMock::createAllocation64k(WddmAllocation *wddmAllocation) {
if (wddmAllocation) {
return createAllocation(wddmAllocation->getDefaultGmm(), wddmAllocation->getHandleToModify(0u));

View File

@@ -82,6 +82,7 @@ class WddmMock : public Wddm {
bool mapGpuVirtualAddress(WddmAllocation *allocation);
bool freeGpuVirtualAddress(D3DGPU_VIRTUAL_ADDRESS &gpuPtr, uint64_t size) override;
NTSTATUS createAllocation(const void *alignedCpuPtr, const Gmm *gmm, D3DKMT_HANDLE &outHandle, D3DKMT_HANDLE &outResource, uint64_t *outSharedHandle) override;
NTSTATUS createAllocation(const void *alignedCpuPtr, const Gmm *gmm, D3DKMT_HANDLE &outHandle, D3DKMT_HANDLE &outResource, uint64_t *outSharedHandle, bool createNTHandle) override;
bool createAllocation(const Gmm *gmm, D3DKMT_HANDLE &outHandle) override;
bool destroyAllocations(const D3DKMT_HANDLE *handles, uint32_t allocationCount, D3DKMT_HANDLE resourceHandle) override;

View File

@@ -68,6 +68,7 @@ int readdirCalled = 0;
int closedirCalled = 0;
int pidfdopenCalled = 0;
int pidfdgetfdCalled = 0;
int prctlCalled = 0;
int fsyncCalled = 0;
int fsyncArgPassed = 0;
int fsyncRetVal = 0;
@@ -116,6 +117,7 @@ int (*sysCallsClosedir)(DIR *dir) = nullptr;
int (*sysCallsGetDevicePath)(int deviceFd, char *buf, size_t &bufSize) = nullptr;
int (*sysCallsPidfdOpen)(pid_t pid, unsigned int flags) = nullptr;
int (*sysCallsPidfdGetfd)(int pidfd, int fd, unsigned int flags) = nullptr;
int (*sysCallsPrctl)(int option, unsigned long arg) = nullptr;
off_t lseekReturn = 4096u;
std::atomic<int> lseekCalledCount(0);
long sysconfReturn = 1ull << 30;
@@ -555,6 +557,15 @@ int pidfdgetfd(int pid, int targetfd, unsigned int flags) {
}
return 0;
}
int prctl(int option, unsigned long arg) {
prctlCalled++;
if (sysCallsPrctl != nullptr) {
return sysCallsPrctl(option, arg);
}
return 0;
}
char **getEnviron() {
return NEO::ULT::getCurrentEnviron();
}

View File

@@ -57,6 +57,7 @@ extern int (*sysCallsGetDevicePath)(int deviceFd, char *buf, size_t &bufSize);
extern int (*sysCallsClose)(int fileDescriptor);
extern int (*sysCallsPidfdOpen)(pid_t pid, unsigned int flags);
extern int (*sysCallsPidfdGetfd)(int pidfd, int fd, unsigned int flags);
extern int (*sysCallsPrctl)(int option, unsigned long arg);
extern bool allowFakeDevicePath;
extern int flockRetVal;
@@ -93,6 +94,7 @@ extern bool failAccess;
extern int setErrno;
extern int pidfdopenCalled;
extern int pidfdgetfdCalled;
extern int prctlCalled;
extern std::vector<void *> mmapVector;
extern std::vector<void *> mmapCapturedExtendedPointers;

View File

@@ -67,6 +67,8 @@ extern CONFIGRET (*sysCallsCmGetDeviceInterfaceListSize)(PULONG pulLen, LPGUID i
extern CONFIGRET (*sysCallsCmGetDeviceInterfaceList)(LPGUID interfaceClassGuid, DEVINSTID_W pDeviceID, PZZWSTR buffer, ULONG bufferLen, ULONG ulFlags);
extern LPVOID (*sysCallsHeapAlloc)(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
extern BOOL (*sysCallsHeapFree)(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
extern BOOL (*sysCallsDuplicateHandle)(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
extern HANDLE (*sysCallsOpenProcess)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
extern BOOL (*sysCallsGetModuleHandleExW)(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE *phModule);
extern DWORD (*sysCallsGetModuleFileNameW)(HMODULE hModule, LPWSTR lpFilename, DWORD nSize);

View File

@@ -120,6 +120,12 @@ LPVOID(*sysCallsHeapAlloc)
BOOL(*sysCallsHeapFree)
(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) = nullptr;
BOOL(*sysCallsDuplicateHandle)
(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) = nullptr;
HANDLE(*sysCallsOpenProcess)
(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) = nullptr;
void exit(int code) {
}
@@ -344,6 +350,20 @@ BOOL heapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
return HeapFree(hHeap, dwFlags, lpMem);
}
BOOL duplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) {
if (sysCallsDuplicateHandle != nullptr) {
return sysCallsDuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
}
return FALSE;
}
HANDLE openProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) {
if (sysCallsOpenProcess != nullptr) {
return sysCallsOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
}
return nullptr;
}
LSTATUS regOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) {
if (regOpenKeySuccessCount > 0) {
regOpenKeySuccessCount--;

View File

@@ -664,4 +664,5 @@ SplitBcsAggregatedEventsMode = -1
SplitBcsRequiredTileCount = -1
SplitBcsRequiredEnginesCount = -1
SplitBcsTransferDirectionMask = -1
EnableShareableWithoutNTHandle = -1
# Please don't edit below this line

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021-2024 Intel Corporation
* Copyright (C) 2021-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -73,7 +73,44 @@ TEST_F(WdmmSharedTests, WhenCreatingSharedAllocationAndGetNTHandleFailedThenAllo
Gmm gmm(executionEnvironment->rootDeviceEnvironments[0]->getGmmHelper(), nullptr, 20, 0, GMM_RESOURCE_USAGE_OCL_BUFFER, {}, gmmRequirements);
EXPECT_NE(STATUS_SUCCESS, wddm->createAllocation(nullptr, &gmm, handle, resourceHandle, &ntHandle));
EXPECT_EQ(wddm->destroyAllocationResult.called++, 1u);
EXPECT_EQ(wddm->destroyAllocationResult.called++, 2u);
EXPECT_EQ(handle, 0u);
EXPECT_EQ(resourceHandle, 0u);
}
TEST_F(WdmmSharedTests, WhenCreatingSharedAllocationWithShareableWithoutNTHandleFlagThenNTHandleIsNotCreated) {
init();
D3DKMT_HANDLE handle = 32u;
D3DKMT_HANDLE resourceHandle = 32u;
uint64_t ntHandle = 0u;
GmmRequirements gmmRequirements{};
gmmRequirements.allowLargePages = true;
gmmRequirements.preferCompressed = true;
Gmm gmm(executionEnvironment->rootDeviceEnvironments[0]->getGmmHelper(), nullptr, 20, 0, GMM_RESOURCE_USAGE_OCL_BUFFER, {}, gmmRequirements);
// Test with createNTHandle = false (shareableWithoutNTHandle = true)
EXPECT_EQ(STATUS_SUCCESS, wddm->createAllocation(nullptr, &gmm, handle, resourceHandle, &ntHandle, false));
EXPECT_NE(handle, 0u);
EXPECT_NE(resourceHandle, 0u);
EXPECT_EQ(ntHandle, 0u); // Should be set to 1 to indicate shared resource without NT handle
EXPECT_EQ(wddm->destroyAllocationResult.called, 0u);
}
TEST_F(WdmmSharedTests, WhenCreatingSharedAllocationWithNormalShareableFlagThenNTHandleCreationIsAttempted) {
init();
D3DKMT_HANDLE handle = 32u;
D3DKMT_HANDLE resourceHandle = 32u;
uint64_t ntHandle = 0u;
GmmRequirements gmmRequirements{};
gmmRequirements.allowLargePages = true;
gmmRequirements.preferCompressed = true;
Gmm gmm(executionEnvironment->rootDeviceEnvironments[0]->getGmmHelper(), nullptr, 20, 0, GMM_RESOURCE_USAGE_OCL_BUFFER, {}, gmmRequirements);
// Test with createNTHandle = true (shareableWithoutNTHandle = false)
EXPECT_NE(STATUS_SUCCESS, wddm->createAllocation(nullptr, &gmm, handle, resourceHandle, &ntHandle, true));
EXPECT_EQ(wddm->destroyAllocationResult.called, 2u); // Should be called because NT handle creation failed
EXPECT_EQ(handle, 0u);
EXPECT_EQ(resourceHandle, 0u);
}

View File

@@ -1200,6 +1200,15 @@ TEST_F(WddmTests, givenPageAlignedReadOnlyMemoryPassedToCreateAllocationsAndMapG
EXPECT_FALSE(readWriteExistingSysMemSupportedForTest);
}
TEST_F(WddmTests, givenOsHandleDataWithoutOpaqueInformationWhenGettingSharedHandleThenReturnOriginalHandle) {
uint64_t originalHandle = 0x12345678;
MemoryManager::OsHandleData osHandleData(originalHandle);
HANDLE sharedHandle = wddm->getSharedHandle(osHandleData);
EXPECT_EQ(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(originalHandle)), sharedHandle);
}
TEST_F(WddmTests, whenThereIsNoExisitngSysMemoryThenReadOnlyFallbackIsNotAvailable) {
D3DKMT_CREATEALLOCATION createAllocation{};
D3DDDI_ALLOCATIONINFO2 allocationInfo2{};
@@ -1231,4 +1240,156 @@ TEST_F(WddmTests, givenSysMemoryPointerAndReadOnlyFlagNotSetInCreateAllocationFl
auto readOnlyFallbackSupported = wddm->isReadOnlyFlagFallbackSupported();
EXPECT_EQ(readOnlyFallbackSupported, wddm->isReadOnlyFlagFallbackAvailable(createAllocation));
}
// Mock class for testing createNTHandle scenarios
class WddmCreateNTHandleMock : public WddmMock {
public:
using WddmMock::WddmMock;
NTSTATUS createNTHandle(const D3DKMT_HANDLE *resourceHandle, HANDLE *ntHandle) override {
createNTHandleCalled = true;
lastResourceHandle = resourceHandle ? *resourceHandle : 0;
if (shouldCreateNTHandleFail) {
return STATUS_UNSUCCESSFUL;
}
*ntHandle = reinterpret_cast<HANDLE>(0x12345678);
return STATUS_SUCCESS;
}
bool createNTHandleCalled = false;
D3DKMT_HANDLE lastResourceHandle = 0;
bool shouldCreateNTHandleFail = false;
};
class WddmCreateAllocationNTHandleTests : public WddmTestWithMockGdiDll {
public:
void SetUp() override {
WddmTestWithMockGdiDll::SetUp();
// Replace the wddm with our mock
mockWddm = new WddmCreateNTHandleMock(*rootDeviceEnvironment);
auto wddmMockInterface = new WddmMockInterface20(*mockWddm);
mockWddm->wddmInterface.reset(wddmMockInterface);
rootDeviceEnvironment->osInterface = std::make_unique<OSInterface>();
rootDeviceEnvironment->osInterface->setDriverModel(std::unique_ptr<DriverModel>(mockWddm));
rootDeviceEnvironment->memoryOperationsInterface = std::make_unique<WddmMemoryOperationsHandler>(mockWddm);
// Initialize the mock WDDM
mockWddm->init();
}
void initGmm() {
GmmRequirements gmmRequirements{};
gmmRequirements.allowLargePages = true;
gmmRequirements.preferCompressed = true;
gmm = std::make_unique<Gmm>(executionEnvironment->rootDeviceEnvironments[0]->getGmmHelper(),
nullptr, 20, 0, GMM_RESOURCE_USAGE_OCL_BUFFER, StorageInfo{}, gmmRequirements);
}
WddmCreateNTHandleMock *mockWddm = nullptr;
std::unique_ptr<Gmm> gmm;
};
TEST_F(WddmCreateAllocationNTHandleTests, givenOutSharedHandleAndCreateNTHandleTrueThenCreateNTHandleIsCalled) {
initGmm();
D3DKMT_HANDLE handle = 0;
D3DKMT_HANDLE resourceHandle = 0;
uint64_t sharedHandle = 0;
// Test the condition: outSharedHandle && createNTHandle (both true)
auto result = mockWddm->createAllocation(nullptr, gmm.get(), handle, resourceHandle, &sharedHandle, true);
EXPECT_EQ(STATUS_SUCCESS, result);
EXPECT_TRUE(mockWddm->createNTHandleCalled);
EXPECT_NE(0u, handle);
EXPECT_NE(0u, resourceHandle);
EXPECT_NE(0u, sharedHandle);
// Cleanup
EXPECT_TRUE(mockWddm->destroyAllocations(&handle, 1, resourceHandle));
}
TEST_F(WddmCreateAllocationNTHandleTests, givenOutSharedHandleNullAndCreateNTHandleTrueThenCreateNTHandleIsNotCalled) {
initGmm();
D3DKMT_HANDLE handle = 0;
D3DKMT_HANDLE resourceHandle = 0;
// Test the condition: outSharedHandle is null, createNTHandle is true
auto result = mockWddm->createAllocation(nullptr, gmm.get(), handle, resourceHandle, nullptr, true);
EXPECT_EQ(STATUS_SUCCESS, result);
EXPECT_FALSE(mockWddm->createNTHandleCalled);
EXPECT_NE(0u, handle);
// When outSharedHandle is null, CreateResource flag is FALSE, so resourceHandle stays 0
EXPECT_EQ(0u, resourceHandle);
// Cleanup only the handle (no resource handle to clean up)
EXPECT_TRUE(mockWddm->destroyAllocations(&handle, 1, 0));
}
TEST_F(WddmCreateAllocationNTHandleTests, givenOutSharedHandleAndCreateNTHandleFalseThenCreateNTHandleIsNotCalled) {
initGmm();
D3DKMT_HANDLE handle = 0;
D3DKMT_HANDLE resourceHandle = 0;
uint64_t sharedHandle = 0;
// Test the condition: outSharedHandle is valid, createNTHandle is false
auto result = mockWddm->createAllocation(nullptr, gmm.get(), handle, resourceHandle, &sharedHandle, false);
EXPECT_EQ(STATUS_SUCCESS, result);
EXPECT_FALSE(mockWddm->createNTHandleCalled);
EXPECT_NE(0u, handle);
EXPECT_NE(0u, resourceHandle);
EXPECT_EQ(0u, sharedHandle); // Should remain 0 since no NT handle was created
// Cleanup
EXPECT_TRUE(mockWddm->destroyAllocations(&handle, 1, resourceHandle));
}
TEST_F(WddmCreateAllocationNTHandleTests, givenCreateNTHandleFailsThenAllocationIsDestroyedAndHandlesAreReset) {
initGmm();
D3DKMT_HANDLE handle = 0;
D3DKMT_HANDLE resourceHandle = 0;
uint64_t sharedHandle = 0;
// Set up the mock to fail createNTHandle
mockWddm->shouldCreateNTHandleFail = true;
// Test the condition: outSharedHandle && createNTHandle (both true) but createNTHandle fails
auto result = mockWddm->createAllocation(nullptr, gmm.get(), handle, resourceHandle, &sharedHandle, true);
EXPECT_NE(STATUS_SUCCESS, result);
EXPECT_TRUE(mockWddm->createNTHandleCalled);
EXPECT_EQ(0u, handle); // Should be reset to NULL_HANDLE
EXPECT_EQ(0u, resourceHandle); // Should be reset to NULL_HANDLE
// sharedHandle value is not modified on failure in this path
}
TEST_F(WddmCreateAllocationNTHandleTests, givenCreateNTHandleSucceedsThenSharedHandleIsSet) {
initGmm();
D3DKMT_HANDLE handle = 0;
D3DKMT_HANDLE resourceHandle = 0;
uint64_t sharedHandle = 0;
// Test successful createNTHandle path
auto result = mockWddm->createAllocation(nullptr, gmm.get(), handle, resourceHandle, &sharedHandle, true);
EXPECT_EQ(STATUS_SUCCESS, result);
EXPECT_TRUE(mockWddm->createNTHandleCalled);
EXPECT_EQ(mockWddm->lastResourceHandle, resourceHandle);
EXPECT_NE(0u, handle);
EXPECT_NE(0u, resourceHandle);
EXPECT_EQ(0x12345678u, sharedHandle); // Should be set to the mock handle value
// Cleanup
EXPECT_TRUE(mockWddm->destroyAllocations(&handle, 1, resourceHandle));
}
} // namespace NEO

View File

@@ -33,6 +33,9 @@ extern SysCalls::ProcessPowerThrottlingState setProcessPowerThrottlingStateLastV
extern size_t setThreadPriorityCalled;
extern SysCalls::ThreadPriority setThreadPriorityLastValue;
extern MEMORY_BASIC_INFORMATION virtualQueryMemoryBasicInformation;
extern size_t closeHandleCalled;
extern BOOL (*sysCallsDuplicateHandle)(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
extern HANDLE (*sysCallsOpenProcess)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);
} // namespace SysCalls
extern uint32_t numRootDevicesToEnum;
extern bool gCreateAllocation2FailOnReadOnlyAllocation;
@@ -461,3 +464,99 @@ TEST_F(WddmTestWithMockGdiDll, whenGettingReadOnlyFlagThenReturnTrueOnlyForPageM
TEST_F(WddmTestWithMockGdiDll, whenGettingReadOnlyFlagFallbackSupportThenTrueIsReturned) {
EXPECT_TRUE(wddm->isReadOnlyFlagFallbackSupported());
}
TEST_F(WddmTestWithMockGdiDll, givenOsHandleDataWithoutParentProcessWhenGettingSharedHandleThenReturnOriginalHandle) {
uint64_t originalHandle = 0x12345678;
MemoryManager::OsHandleData osHandleData(originalHandle);
HANDLE sharedHandle = wddm->getSharedHandle(osHandleData);
EXPECT_EQ(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(originalHandle)), sharedHandle);
}
TEST_F(WddmTestWithMockGdiDll, givenOsHandleDataWithParentProcessWhenGettingSharedHandleThenDuplicateHandleFromParentProcess) {
uint64_t originalHandle = 0x12345678;
uint32_t parentProcessId = 1234;
MemoryManager::OsHandleData osHandleData(originalHandle);
osHandleData.parentProcessId = parentProcessId;
HANDLE mockDuplicatedHandle = reinterpret_cast<HANDLE>(0x8888);
// Mock openProcess to return a valid handle
SysCalls::sysCallsOpenProcess = [](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) -> HANDLE {
EXPECT_EQ(static_cast<DWORD>(PROCESS_DUP_HANDLE), dwDesiredAccess);
EXPECT_EQ(FALSE, bInheritHandle);
EXPECT_EQ(1234u, dwProcessId);
return reinterpret_cast<HANDLE>(0x9999);
};
// Mock duplicateHandle to succeed
SysCalls::sysCallsDuplicateHandle = [](HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) -> BOOL {
EXPECT_EQ(reinterpret_cast<HANDLE>(0x9999), hSourceProcessHandle);
EXPECT_EQ(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(0x12345678)), hSourceHandle);
EXPECT_EQ(GetCurrentProcess(), hTargetProcessHandle);
EXPECT_EQ(GENERIC_READ | GENERIC_WRITE, dwDesiredAccess);
EXPECT_EQ(FALSE, bInheritHandle);
EXPECT_EQ(0u, dwOptions);
*lpTargetHandle = reinterpret_cast<HANDLE>(0x8888);
return TRUE;
};
size_t closeHandleCallsBefore = SysCalls::closeHandleCalled;
HANDLE sharedHandle = wddm->getSharedHandle(osHandleData);
EXPECT_EQ(mockDuplicatedHandle, sharedHandle);
EXPECT_EQ(closeHandleCallsBefore + 1, SysCalls::closeHandleCalled); // Parent process handle should be closed
// Cleanup
SysCalls::sysCallsOpenProcess = nullptr;
SysCalls::sysCallsDuplicateHandle = nullptr;
}
TEST_F(WddmTestWithMockGdiDll, givenOsHandleDataWithParentProcessWhenOpenProcessFailsThenReturnOriginalHandle) {
uint64_t originalHandle = 0x12345678;
uint32_t parentProcessId = 1234;
MemoryManager::OsHandleData osHandleData(originalHandle);
osHandleData.parentProcessId = parentProcessId;
// Mock openProcess to fail
SysCalls::sysCallsOpenProcess = [](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) -> HANDLE {
return nullptr;
};
HANDLE sharedHandle = wddm->getSharedHandle(osHandleData);
EXPECT_EQ(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(originalHandle)), sharedHandle);
// Cleanup
SysCalls::sysCallsOpenProcess = nullptr;
}
TEST_F(WddmTestWithMockGdiDll, givenOsHandleDataWithParentProcessWhenDuplicateHandleFailsThenReturnOriginalHandle) {
uint64_t originalHandle = 0x12345678;
uint32_t parentProcessId = 1234;
MemoryManager::OsHandleData osHandleData(originalHandle);
osHandleData.parentProcessId = parentProcessId;
// Mock openProcess to succeed
SysCalls::sysCallsOpenProcess = [](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId) -> HANDLE {
return reinterpret_cast<HANDLE>(0x9999);
};
// Mock duplicateHandle to fail
SysCalls::sysCallsDuplicateHandle = [](HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) -> BOOL {
return FALSE;
};
size_t closeHandleCallsBefore = SysCalls::closeHandleCalled;
HANDLE sharedHandle = wddm->getSharedHandle(osHandleData);
EXPECT_EQ(reinterpret_cast<HANDLE>(static_cast<uintptr_t>(originalHandle)), sharedHandle);
EXPECT_EQ(closeHandleCallsBefore + 1, SysCalls::closeHandleCalled); // Parent process handle should still be closed
// Cleanup
SysCalls::sysCallsOpenProcess = nullptr;
SysCalls::sysCallsDuplicateHandle = nullptr;
}