fix: support zeEventPool IPC handles from single process

For all execution paths where needed, use `BufferObjectHandleWrapper`
instances for registration of BO-handles and try to obtain shared
ownership. This allows both sides of IPC communication to be implemented
in the same process and avoid the double-free problem on a BufferObject
of the same handle.

Currently there are two pairs of such calls:
* `zeEventPoolGetIpcHandle()` + `zeEventPoolOpenIpcHandle()`
* `zeMemGetIpcHandle()` + `zeMemOpenIpcHandle()`

The capability of executing both sides from the same process is useful
for testing but not only.

Related-To: NEO-9837
Signed-off-by: Maciej Bielski <maciej.bielski@intel.com>
This commit is contained in:
Maciej Bielski
2024-01-16 13:27:23 +00:00
committed by Compute-Runtime-Automation
parent b5f698e0c5
commit 134c718a25
3 changed files with 182 additions and 7 deletions

View File

@@ -2470,18 +2470,30 @@ DrmAllocation *DrmMemoryManager::createUSMHostAllocationFromSharedHandle(osHandl
return allocation;
}
auto boHandle = static_cast<int>(openFd.handle);
auto boHandleWrapper = reuseSharedAllocation ? BufferObjectHandleWrapper{boHandle} : tryToGetBoHandleWrapperWithSharedOwnership(boHandle);
const bool useBooMmap = drm.getMemoryInfo() && properties.useMmapObject;
if (!useBooMmap) {
auto bo = new BufferObject(properties.rootDeviceIndex, &drm, patIndex, openFd.handle, properties.size, maxOsContextCount);
auto bo = new BufferObject(properties.rootDeviceIndex, &drm, patIndex, std::move(boHandleWrapper), properties.size, maxOsContextCount);
bo->setAddress(properties.gpuAddress);
auto gmmHelper = getGmmHelper(properties.rootDeviceIndex);
auto canonizedGpuAddress = gmmHelper->canonize(castToUint64(reinterpret_cast<void *>(bo->peekAddress())));
return new DrmAllocation(properties.rootDeviceIndex, properties.allocationType, bo, reinterpret_cast<void *>(bo->peekAddress()), bo->peekSize(),
handle, memoryPool, canonizedGpuAddress);
auto drmAllocation = std::make_unique<DrmAllocation>(properties.rootDeviceIndex,
properties.allocationType,
bo,
reinterpret_cast<void *>(bo->peekAddress()),
bo->peekSize(),
handle,
memoryPool,
canonizedGpuAddress);
if (!reuseSharedAllocation) {
registerSharedBoHandleAllocation(drmAllocation.get());
}
return drmAllocation.release();
}
auto boHandle = openFd.handle;
BufferObject *bo = nullptr;
if (reuseSharedAllocation) {
bo = findAndReferenceSharedBufferObject(boHandle, properties.rootDeviceIndex);
@@ -2494,7 +2506,7 @@ DrmAllocation *DrmMemoryManager::createUSMHostAllocationFromSharedHandle(osHandl
memoryPool = MemoryPool::system4KBPages;
patIndex = drm.getPatIndex(nullptr, properties.allocationType, CacheRegion::defaultRegion, CachePolicy::writeBack, false, MemoryPoolHelper::isSystemMemoryPool(memoryPool));
bo = new BufferObject(properties.rootDeviceIndex, &drm, patIndex, boHandle, size, maxOsContextCount);
bo = new BufferObject(properties.rootDeviceIndex, &drm, patIndex, std::move(boHandleWrapper), size, maxOsContextCount);
if (properties.allocationType == AllocationType::gpuTimestampDeviceBuffer) {
cpuPointer = this->mmapFunction(0, size + MemoryConstants::pageSize64k, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
@@ -2553,6 +2565,9 @@ DrmAllocation *DrmMemoryManager::createUSMHostAllocationFromSharedHandle(osHandl
return nullptr;
}
if (!reuseSharedAllocation) {
registerSharedBoHandleAllocation(drmAllocation.get());
}
return drmAllocation.release();
}

View File

@@ -3858,6 +3858,161 @@ TEST(DrmMemoryManagerFreeGraphicsMemoryUnreferenceTest,
EXPECT_EQ(expectedOutput.str(), output);
}
struct DrmMemoryManagerWithHostIpcAllocationParamTest : public DrmMemoryManagerFixture, ::testing::TestWithParam<bool> {
void SetUp() override {
DrmMemoryManagerFixture::setUp();
}
void TearDown() override {
DrmMemoryManagerFixture::tearDown();
}
};
TEST_P(DrmMemoryManagerWithHostIpcAllocationParamTest,
givenIPCBoHandleAndSharedAllocationReuseEnabledWhenAllocationCreatedThenBoHandleSharingIsNotUsed) {
const bool reuseSharedAllocation = true;
mock->ioctlExpected.primeFdToHandle = 1;
mock->ioctlExpected.gemWait = 1;
mock->ioctlExpected.gemClose = 1;
mock->outputHandle = 88u;
bool isHostIpcAllocation = GetParam();
AllocationProperties properties(rootDeviceIndex, false, 4096u, AllocationType::sharedBuffer, false, {});
osHandle handle1 = 11u;
auto gfxAllocation1 = memoryManager->createGraphicsAllocationFromSharedHandle(handle1, properties, false, isHostIpcAllocation, reuseSharedAllocation, nullptr);
DrmAllocation *drmAllocation1 = static_cast<DrmAllocation *>(gfxAllocation1);
ASSERT_NE(nullptr, drmAllocation1);
// BoHandle not registered as shared at all
auto bo1 = drmAllocation1->getBO();
EXPECT_NE(bo1, nullptr);
EXPECT_EQ(static_cast<uint32_t>(bo1->getHandle()), mock->outputHandle);
EXPECT_FALSE(bo1->isBoHandleShared());
auto boHandleWrapperIt1 = memoryManager->sharedBoHandles.find(mock->outputHandle);
EXPECT_EQ(boHandleWrapperIt1, std::end(memoryManager->sharedBoHandles));
memoryManager->freeGraphicsMemory(gfxAllocation1);
}
TEST_P(DrmMemoryManagerWithHostIpcAllocationParamTest,
givenIPCBoHandleAndSharedAllocationReuseEnabledWhenAllocationCreatedFromMultipleHandlesThenBoHandleSharingIsNotUsed) {
const bool reuseSharedAllocation = true;
mock->ioctlExpected.primeFdToHandle = 1;
mock->ioctlExpected.gemWait = 1;
mock->ioctlExpected.gemClose = 1;
mock->outputHandle = 88u;
bool isHostIpcAllocation = GetParam();
AllocationProperties properties(rootDeviceIndex, true, 4096u, AllocationType::sharedBuffer, false, {0b0010});
std::vector<osHandle> handles = {11u};
auto gfxAllocation1 = memoryManager->createGraphicsAllocationFromMultipleSharedHandles(handles, properties, false, isHostIpcAllocation, reuseSharedAllocation, nullptr);
DrmAllocation *drmAllocation1 = static_cast<DrmAllocation *>(gfxAllocation1);
ASSERT_NE(nullptr, drmAllocation1);
// BoHandle not registered as shared at all
auto bo1 = drmAllocation1->getBO();
EXPECT_NE(bo1, nullptr);
EXPECT_EQ(static_cast<uint32_t>(bo1->getHandle()), mock->outputHandle);
EXPECT_FALSE(bo1->isBoHandleShared());
auto boHandleWrapperIt1 = memoryManager->sharedBoHandles.find(mock->outputHandle);
EXPECT_EQ(boHandleWrapperIt1, std::end(memoryManager->sharedBoHandles));
memoryManager->freeGraphicsMemory(gfxAllocation1);
}
TEST_P(DrmMemoryManagerWithHostIpcAllocationParamTest,
givenIPCBoHandleAndSharedAllocationReuseDisabledWhenMultipleAllocationsCreatedFromSingleProcessThenBoHandleIsClosedOnlyOnce) {
const bool reuseSharedAllocation = false;
mock->ioctlExpected.primeFdToHandle = 2;
mock->ioctlExpected.gemWait = 2;
mock->ioctlExpected.gemClose = 1;
mock->outputHandle = 88u;
bool isHostIpcAllocation = GetParam();
AllocationProperties properties(rootDeviceIndex, false, 4096u, AllocationType::sharedBuffer, false, {});
osHandle handle1 = 11u;
auto gfxAllocation1 = memoryManager->createGraphicsAllocationFromSharedHandle(handle1, properties, false, isHostIpcAllocation, reuseSharedAllocation, nullptr);
DrmAllocation *drmAllocation1 = static_cast<DrmAllocation *>(gfxAllocation1);
ASSERT_NE(nullptr, drmAllocation1);
// BoHandle registered as shared but with WEAK ownership - GEM_CLOSE can be called on it
auto bo1 = drmAllocation1->getBO();
EXPECT_NE(bo1, nullptr);
EXPECT_EQ(static_cast<uint32_t>(bo1->getHandle()), mock->outputHandle);
EXPECT_TRUE(bo1->isBoHandleShared());
auto boHandleWrapperIt1 = memoryManager->sharedBoHandles.find(mock->outputHandle);
EXPECT_NE(boHandleWrapperIt1, std::end(memoryManager->sharedBoHandles));
EXPECT_TRUE(boHandleWrapperIt1->second.canCloseBoHandle());
osHandle handle2 = 12u;
auto gfxAllocation2 = memoryManager->createGraphicsAllocationFromSharedHandle(handle2, properties, false, isHostIpcAllocation, false, nullptr);
DrmAllocation *drmAllocation2 = static_cast<DrmAllocation *>(gfxAllocation2);
ASSERT_NE(nullptr, drmAllocation2);
// BoHandle registered as shared with SHARED ownership - GEM_CLOSE cannot be called on it
auto bo2 = drmAllocation2->getBO();
EXPECT_NE(bo2, nullptr);
EXPECT_EQ(static_cast<uint32_t>(bo2->getHandle()), mock->outputHandle);
EXPECT_TRUE(bo2->isBoHandleShared());
auto boHandleWrapperIt2 = memoryManager->sharedBoHandles.find(mock->outputHandle);
EXPECT_EQ(boHandleWrapperIt2, boHandleWrapperIt1);
EXPECT_FALSE(boHandleWrapperIt2->second.canCloseBoHandle());
memoryManager->freeGraphicsMemory(gfxAllocation2);
// GEM_CLOSE can be called on BoHandle again
EXPECT_TRUE(boHandleWrapperIt2->second.canCloseBoHandle());
memoryManager->freeGraphicsMemory(gfxAllocation1);
}
TEST_P(DrmMemoryManagerWithHostIpcAllocationParamTest,
givenIPCBoHandleAndSharedAllocationReuseDisabledWhenMultipleAllocationsCreatedFromMultipleSharedHandlesFromSingleProcessThenBoHandleIsClosedOnlyOnce) {
const bool reuseSharedAllocation = false;
mock->ioctlExpected.primeFdToHandle = 2;
mock->ioctlExpected.gemWait = 2;
mock->ioctlExpected.gemClose = 1;
mock->outputHandle = 88u;
bool isHostIpcAllocation = GetParam();
AllocationProperties properties(rootDeviceIndex, true, 4096u, AllocationType::sharedBuffer, false, {});
std::vector<osHandle> handles1 = {11u};
auto gfxAllocation1 = memoryManager->createGraphicsAllocationFromMultipleSharedHandles(handles1, properties, false, isHostIpcAllocation, reuseSharedAllocation, nullptr);
DrmAllocation *drmAllocation1 = static_cast<DrmAllocation *>(gfxAllocation1);
ASSERT_NE(nullptr, drmAllocation1);
// BoHandle registered as shared but with WEAK ownership - GEM_CLOSE can be called on it
auto bo1 = drmAllocation1->getBO();
EXPECT_NE(bo1, nullptr);
EXPECT_EQ(static_cast<uint32_t>(bo1->getHandle()), mock->outputHandle);
EXPECT_TRUE(bo1->isBoHandleShared());
auto boHandleWrapperIt1 = memoryManager->sharedBoHandles.find(mock->outputHandle);
EXPECT_NE(boHandleWrapperIt1, std::end(memoryManager->sharedBoHandles));
EXPECT_TRUE(boHandleWrapperIt1->second.canCloseBoHandle());
std::vector<osHandle> handles2 = {12u};
auto gfxAllocation2 = memoryManager->createGraphicsAllocationFromMultipleSharedHandles(handles2, properties, false, isHostIpcAllocation, false, nullptr);
DrmAllocation *drmAllocation2 = static_cast<DrmAllocation *>(gfxAllocation2);
ASSERT_NE(nullptr, drmAllocation2);
// BoHandle registered as shared with SHARED ownership - GEM_CLOSE cannot be called on it
auto bo2 = drmAllocation2->getBO();
EXPECT_NE(bo2, nullptr);
EXPECT_EQ(static_cast<uint32_t>(bo2->getHandle()), mock->outputHandle);
EXPECT_TRUE(bo2->isBoHandleShared());
auto boHandleWrapperIt2 = memoryManager->sharedBoHandles.find(mock->outputHandle);
EXPECT_EQ(boHandleWrapperIt2, boHandleWrapperIt1);
EXPECT_FALSE(boHandleWrapperIt2->second.canCloseBoHandle());
memoryManager->freeGraphicsMemory(gfxAllocation2);
// GEM_CLOSE can be called on BoHandle again
EXPECT_TRUE(boHandleWrapperIt2->second.canCloseBoHandle());
memoryManager->freeGraphicsMemory(gfxAllocation1);
}
INSTANTIATE_TEST_CASE_P(HostIpcAllocationFlag,
DrmMemoryManagerWithHostIpcAllocationParamTest,
::testing::Values(false, true));
TEST(DrmMemoryManagerFreeGraphicsMemoryUnreferenceTest,
givenCallToCreateSharedAllocationWithReuseSharedAllocationThenAllocationsSuccedAndAddressesAreTheSame) {
MockExecutionEnvironment executionEnvironment(defaultHwInfo.get());