diff --git a/shared/source/debug_settings/debug_variables_base.inl b/shared/source/debug_settings/debug_variables_base.inl index da3f6790d9..2440596d19 100644 --- a/shared/source/debug_settings/debug_variables_base.inl +++ b/shared/source/debug_settings/debug_variables_base.inl @@ -442,7 +442,7 @@ DECLARE_DEBUG_VARIABLE(int32_t, WaitForPagingFenceInController, -1, "Instead of /*FEATURE FLAGS*/ DECLARE_DEBUG_VARIABLE(bool, USMEvictAfterMigration, false, "Evict USM allocation after implicit migration to GPU") -DECLARE_DEBUG_VARIABLE(bool, RegisterPageFaultHandlerOnMigration, true, "Register handler on migration to GPU when current is not from pagefault manager") +DECLARE_DEBUG_VARIABLE(bool, RegisterPageFaultHandlerOnMigration, false, "Register handler on migration to GPU when current is not from pagefault manager") DECLARE_DEBUG_VARIABLE(bool, EnableNV12, true, "Enables NV12 extension") DECLARE_DEBUG_VARIABLE(bool, EnablePackedYuv, true, "Enables cl_packed_yuv extension") DECLARE_DEBUG_VARIABLE(bool, EnableDeferredDeleter, true, "Enables async deleter") diff --git a/shared/source/page_fault_manager/cpu_page_fault_manager.cpp b/shared/source/page_fault_manager/cpu_page_fault_manager.cpp index 62229b098b..f45cc962ca 100644 --- a/shared/source/page_fault_manager/cpu_page_fault_manager.cpp +++ b/shared/source/page_fault_manager/cpu_page_fault_manager.cpp @@ -95,14 +95,16 @@ inline void PageFaultManager::migrateStorageToGpuDomain(void *ptr, PageFaultData pageFaultData.domain = AllocationDomain::gpu; } -bool PageFaultManager::verifyPageFault(void *ptr) { +bool PageFaultManager::verifyAndHandlePageFault(void *ptr, bool handlePageFault) { std::unique_lock lock{mtx}; for (auto &alloc : this->memoryData) { auto allocPtr = alloc.first; auto &pageFaultData = alloc.second; if (ptr >= allocPtr && ptr < ptrOffset(allocPtr, pageFaultData.size)) { - this->setAubWritable(true, allocPtr, pageFaultData.unifiedMemoryManager); - gpuDomainHandler(this, allocPtr, pageFaultData); + if (handlePageFault) { + this->setAubWritable(true, allocPtr, pageFaultData.unifiedMemoryManager); + gpuDomainHandler(this, allocPtr, pageFaultData); + } return true; } } diff --git a/shared/source/page_fault_manager/cpu_page_fault_manager.h b/shared/source/page_fault_manager/cpu_page_fault_manager.h index 9abfdf8338..241441fb96 100644 --- a/shared/source/page_fault_manager/cpu_page_fault_manager.h +++ b/shared/source/page_fault_manager/cpu_page_fault_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 Intel Corporation + * Copyright (C) 2019-2024 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -59,7 +59,7 @@ class PageFaultManager : public NonCopyableOrMovableClass { virtual void evictMemoryAfterImplCopy(GraphicsAllocation *allocation, Device *device) = 0; virtual void allowCPUMemoryEvictionImpl(void *ptr, CommandStreamReceiver &csr, OSInterface *osInterface) = 0; - MOCKABLE_VIRTUAL bool verifyPageFault(void *ptr); + MOCKABLE_VIRTUAL bool verifyAndHandlePageFault(void *ptr, bool handlePageFault); MOCKABLE_VIRTUAL void transferToGpu(void *ptr, void *cmdQ); MOCKABLE_VIRTUAL void setAubWritable(bool writable, void *ptr, SVMAllocsManager *unifiedMemoryManager); MOCKABLE_VIRTUAL void setCpuAllocEvictable(bool evictable, void *ptr, SVMAllocsManager *unifiedMemoryManager); diff --git a/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.cpp b/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.cpp index e889e50ca4..734c4720d6 100644 --- a/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.cpp +++ b/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 Intel Corporation + * Copyright (C) 2019-2024 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -13,6 +13,7 @@ #include "shared/source/helpers/debug_helpers.h" #include "shared/source/memory_manager/memory_operations_handler.h" +#include #include namespace NEO { @@ -35,8 +36,9 @@ PageFaultManagerLinux::PageFaultManagerLinux() { PageFaultManagerLinux::~PageFaultManagerLinux() { if (!previousHandlerRestored) { - auto retVal = sigaction(SIGSEGV, &previousPageFaultHandler, nullptr); + auto retVal = sigaction(SIGSEGV, &previousPageFaultHandlers[0], nullptr); UNRECOVERABLE_IF(retVal != 0); + previousPageFaultHandlers.clear(); } } @@ -47,8 +49,21 @@ bool PageFaultManagerLinux::checkFaultHandlerFromPageFaultManager() { } void PageFaultManagerLinux::registerFaultHandler() { + struct sigaction previousPageFaultHandler = {}; + auto retVal = sigaction(SIGSEGV, nullptr, &previousPageFaultHandler); + UNRECOVERABLE_IF(retVal != 0); + + auto compareHandler = [&ph = previousPageFaultHandler](const struct sigaction &h) -> bool { + return (h.sa_flags & SA_SIGINFO) ? (h.sa_sigaction == ph.sa_sigaction) : (h.sa_handler == ph.sa_handler); + }; + if (std::find_if(previousPageFaultHandlers.begin(), + previousPageFaultHandlers.end(), + compareHandler) == previousPageFaultHandlers.end()) { + previousPageFaultHandlers.push_back(previousPageFaultHandler); + } + pageFaultHandler = [&](int signal, siginfo_t *info, void *context) { - if (!this->verifyPageFault(info->si_addr)) { + if (!this->verifyAndHandlePageFault(info->si_addr, this->handlerIndex == 0)) { callPreviousHandler(signal, info, context); } }; @@ -57,7 +72,7 @@ void PageFaultManagerLinux::registerFaultHandler() { pageFaultManagerHandler.sa_flags = SA_SIGINFO; pageFaultManagerHandler.sa_sigaction = pageFaultHandlerWrapper; - auto retVal = sigaction(SIGSEGV, &pageFaultManagerHandler, &previousPageFaultHandler); + retVal = sigaction(SIGSEGV, &pageFaultManagerHandler, &previousPageFaultHandler); UNRECOVERABLE_IF(retVal != 0); } @@ -76,6 +91,9 @@ void PageFaultManagerLinux::protectCPUMemoryAccess(void *ptr, size_t size) { } void PageFaultManagerLinux::callPreviousHandler(int signal, siginfo_t *info, void *context) { + handlerIndex++; + UNRECOVERABLE_IF(handlerIndex < 0 && handlerIndex >= static_cast(previousPageFaultHandlers.size())); + auto previousPageFaultHandler = previousPageFaultHandlers[previousPageFaultHandlers.size() - handlerIndex]; if (previousPageFaultHandler.sa_flags & SA_SIGINFO) { previousPageFaultHandler.sa_sigaction(signal, info, context); } else { @@ -83,12 +101,12 @@ void PageFaultManagerLinux::callPreviousHandler(int signal, siginfo_t *info, voi auto retVal = sigaction(SIGSEGV, &previousPageFaultHandler, nullptr); UNRECOVERABLE_IF(retVal != 0); previousHandlerRestored = true; - } else if (previousPageFaultHandler.sa_handler == SIG_IGN) { - return; - } else { + previousPageFaultHandlers.clear(); + } else if (previousPageFaultHandler.sa_handler != SIG_IGN) { previousPageFaultHandler.sa_handler(signal); } } + handlerIndex--; } void PageFaultManagerLinux::evictMemoryAfterImplCopy(GraphicsAllocation *allocation, Device *device) { diff --git a/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.h b/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.h index 7ca84a014e..414641bcc4 100644 --- a/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.h +++ b/shared/source/page_fault_manager/linux/cpu_page_fault_manager_linux.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 Intel Corporation + * Copyright (C) 2019-2024 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -11,6 +11,7 @@ #include #include +#include namespace NEO { class PageFaultManagerLinux : public PageFaultManager { @@ -35,8 +36,9 @@ class PageFaultManagerLinux : public PageFaultManager { static std::function pageFaultHandler; - struct sigaction previousPageFaultHandler = {}; + std::vector previousPageFaultHandlers; bool evictMemoryAfterCopy = false; + int handlerIndex = 0; }; } // namespace NEO diff --git a/shared/source/page_fault_manager/windows/cpu_page_fault_manager_windows.cpp b/shared/source/page_fault_manager/windows/cpu_page_fault_manager_windows.cpp index dab525c566..0dfd6dc200 100644 --- a/shared/source/page_fault_manager/windows/cpu_page_fault_manager_windows.cpp +++ b/shared/source/page_fault_manager/windows/cpu_page_fault_manager_windows.cpp @@ -39,7 +39,7 @@ bool PageFaultManagerWindows::checkFaultHandlerFromPageFaultManager() { void PageFaultManagerWindows::registerFaultHandler() { pageFaultHandler = [this](struct _EXCEPTION_POINTERS *exceptionInfo) { if (static_cast(exceptionInfo->ExceptionRecord->ExceptionCode) == EXCEPTION_ACCESS_VIOLATION) { - if (this->verifyPageFault(reinterpret_cast(exceptionInfo->ExceptionRecord->ExceptionInformation[1]))) { + if (this->verifyAndHandlePageFault(reinterpret_cast(exceptionInfo->ExceptionRecord->ExceptionInformation[1]), true)) { // this is our fault that we serviced, continue app execution return EXCEPTION_CONTINUE_EXECUTION; } diff --git a/shared/test/common/mocks/mock_cpu_page_fault_manager.h b/shared/test/common/mocks/mock_cpu_page_fault_manager.h index c1d69bb229..76835a2293 100644 --- a/shared/test/common/mocks/mock_cpu_page_fault_manager.h +++ b/shared/test/common/mocks/mock_cpu_page_fault_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 Intel Corporation + * Copyright (C) 2019-2024 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -23,11 +23,11 @@ class MockPageFaultManager : public PageFaultManager { using PageFaultManager::selectGpuDomainHandler; using PageFaultManager::transferAndUnprotectMemory; using PageFaultManager::unprotectAndTransferMemory; - using PageFaultManager::verifyPageFault; + using PageFaultManager::verifyAndHandlePageFault; bool checkFaultHandlerFromPageFaultManager() override { checkFaultHandlerCalled++; - return (registerFaultHandlerCalled != 0); + return isFaultHandlerFromPageFaultManager; } void registerFaultHandler() override { registerFaultHandlerCalled++; @@ -115,6 +115,7 @@ class MockPageFaultManager : public PageFaultManager { size_t protectedSize = 0; bool isAubWritable = true; bool isCpuAllocEvictable = true; + bool isFaultHandlerFromPageFaultManager = false; aub_stream::EngineType engineType = aub_stream::EngineType::NUM_ENGINES; EngineUsage engineUsage = EngineUsage::engineUsageCount; }; @@ -129,8 +130,8 @@ class MockPageFaultManagerHandlerInvoke : public T { using T::registerFaultHandler; using T::T; - bool verifyPageFault(void *ptr) override { - handlerInvoked = true; + bool verifyAndHandlePageFault(void *ptr, bool handlePageFault) override { + handlerInvoked = handlePageFault; if (allowCPUMemoryAccessOnPageFault) { this->allowCPUMemoryAccess(ptr, size); } diff --git a/shared/test/common/test_files/igdrcl.config b/shared/test/common/test_files/igdrcl.config index 30d78b8a76..9454a21714 100644 --- a/shared/test/common/test_files/igdrcl.config +++ b/shared/test/common/test_files/igdrcl.config @@ -27,7 +27,7 @@ SetCommandStreamReceiver = -1 TbxPort = 4321 TbxFrontdoorMode = 0 FlattenBatchBufferForAUBDump = 0 -RegisterPageFaultHandlerOnMigration = 1 +RegisterPageFaultHandlerOnMigration = 0 AddPatchInfoCommentsForAUBDump = 0 UseAubStream = 1 AUBDumpAllocsOnEnqueueReadOnly = 0 diff --git a/shared/test/unit_test/page_fault_manager/cpu_page_fault_manager_tests.cpp b/shared/test/unit_test/page_fault_manager/cpu_page_fault_manager_tests.cpp index 2ebd232f76..cb60996548 100644 --- a/shared/test/unit_test/page_fault_manager/cpu_page_fault_manager_tests.cpp +++ b/shared/test/unit_test/page_fault_manager/cpu_page_fault_manager_tests.cpp @@ -253,6 +253,8 @@ TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenMoveToGpuDomainThenTrans } TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenMoveToGpuDomainTwiceThenCheckFaultHandlerFromPageFaultManagerReturnsTrue) { + DebugManagerStateRestore restorer; + debugManager.flags.RegisterPageFaultHandlerOnMigration.set(true); void *cmdQ = reinterpret_cast(0xFFFF); void *alloc1 = reinterpret_cast(0x1); void *alloc2 = reinterpret_cast(0x2); @@ -262,7 +264,7 @@ TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenMoveToGpuDomainTwiceThen EXPECT_EQ(pageFaultManager->memoryData.size(), 2u); EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); - EXPECT_FALSE(pageFaultManager->checkFaultHandlerFromPageFaultManager()); + pageFaultManager->isFaultHandlerFromPageFaultManager = false; pageFaultManager->moveAllocationToGpuDomain(alloc1); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 0); @@ -274,7 +276,9 @@ TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenMoveToGpuDomainTwiceThen EXPECT_EQ(pageFaultManager->protectedSize, 10u); EXPECT_EQ(pageFaultManager->transferToGpuAddress, alloc1); - EXPECT_TRUE(pageFaultManager->checkFaultHandlerFromPageFaultManager()); + EXPECT_FALSE(pageFaultManager->checkFaultHandlerFromPageFaultManager()); + + pageFaultManager->isFaultHandlerFromPageFaultManager = true; pageFaultManager->moveAllocationToGpuDomain(alloc2); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 0); @@ -289,6 +293,44 @@ TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenMoveToGpuDomainTwiceThen EXPECT_TRUE(pageFaultManager->checkFaultHandlerFromPageFaultManager()); } +TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenMoveToGpuDomainTwiceThenRegisterFaultHandlerIsCalledTwice) { + DebugManagerStateRestore restorer; + debugManager.flags.RegisterPageFaultHandlerOnMigration.set(true); + + void *cmdQ = reinterpret_cast(0xFFFF); + void *alloc1 = reinterpret_cast(0x1); + void *alloc2 = reinterpret_cast(0x2); + + pageFaultManager->insertAllocation(alloc1, 10u, unifiedMemoryManager.get(), cmdQ, {}); + pageFaultManager->insertAllocation(alloc2, 20u, unifiedMemoryManager.get(), cmdQ, {}); + EXPECT_EQ(pageFaultManager->memoryData.size(), 2u); + EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); + + pageFaultManager->isFaultHandlerFromPageFaultManager = false; + + pageFaultManager->moveAllocationToGpuDomain(alloc1); + EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 0); + EXPECT_EQ(pageFaultManager->protectMemoryCalled, 1); + EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); + EXPECT_EQ(pageFaultManager->transferToGpuCalled, 1); + EXPECT_EQ(pageFaultManager->registerFaultHandlerCalled, 1); + EXPECT_EQ(pageFaultManager->protectedMemoryAccessAddress, alloc1); + EXPECT_EQ(pageFaultManager->protectedSize, 10u); + EXPECT_EQ(pageFaultManager->transferToGpuAddress, alloc1); + EXPECT_FALSE(pageFaultManager->checkFaultHandlerFromPageFaultManager()); + + pageFaultManager->moveAllocationToGpuDomain(alloc2); + EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 0); + EXPECT_EQ(pageFaultManager->protectMemoryCalled, 2); + EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); + EXPECT_EQ(pageFaultManager->transferToGpuCalled, 2); + EXPECT_EQ(pageFaultManager->registerFaultHandlerCalled, 2); + EXPECT_EQ(pageFaultManager->protectedMemoryAccessAddress, alloc2); + EXPECT_EQ(pageFaultManager->protectedSize, 20u); + EXPECT_EQ(pageFaultManager->transferToGpuAddress, alloc2); + EXPECT_FALSE(pageFaultManager->checkFaultHandlerFromPageFaultManager()); +} + TEST_F(PageFaultManagerTest, givenRegisterPageFaultHandlerOnMigrationDisabledWhenMoveToGpuDomainThenDoNotRegisterHandler) { DebugManagerStateRestore restorer; debugManager.flags.RegisterPageFaultHandlerOnMigration.set(false); @@ -404,7 +446,7 @@ TEST_F(PageFaultManagerTest, givenHandlerRegisteredAndUntrackedPageFaultAddressW pageFaultManager->insertAllocation(alloc2, 20, unifiedMemoryManager.get(), nullptr, {}); EXPECT_EQ(pageFaultManager->memoryData.size(), 2u); - auto retVal = pageFaultManager->verifyPageFault(alloc3); + auto retVal = pageFaultManager->verifyAndHandlePageFault(alloc3, true); EXPECT_FALSE(retVal); } @@ -417,7 +459,7 @@ TEST_F(PageFaultManagerTest, givenTrackedPageFaultAddressWhenVerifyingThenProper EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); EXPECT_EQ(pageFaultManager->memoryData.size(), 2u); - pageFaultManager->verifyPageFault(alloc1); + pageFaultManager->verifyAndHandlePageFault(alloc1, true); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 1); EXPECT_EQ(pageFaultManager->protectMemoryCalled, 0); EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); @@ -438,7 +480,7 @@ TEST_F(PageFaultManagerTest, givenInitialPlacementCpuWhenVerifyingPagefaultThenF EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 1u); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs[0], alloc); - pageFaultManager->verifyPageFault(alloc); + pageFaultManager->verifyAndHandlePageFault(alloc, true); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 1); EXPECT_EQ(pageFaultManager->protectMemoryCalled, 0); EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); @@ -464,13 +506,13 @@ TEST_F(PageFaultManagerTest, givenAllocsMovedToGpuDomainWhenVerifyingPageFaultTh pageFaultManager->moveAllocationsWithinUMAllocsManagerToGpuDomain(unifiedMemoryManager.get()); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 0u); - pageFaultManager->verifyPageFault(alloc2); + pageFaultManager->verifyAndHandlePageFault(alloc2, true); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 1u); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs[0], alloc2); pageFaultManager->gpuDomainHandler = &MockPageFaultManager::unprotectAndTransferMemory; - pageFaultManager->verifyPageFault(alloc1); + pageFaultManager->verifyAndHandlePageFault(alloc1, true); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 2u); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs[1], alloc1); } @@ -489,13 +531,13 @@ TEST_F(PageFaultManagerTest, givenAllocsFromCpuDomainWhenVerifyingPageFaultThenD EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 0u); pageFaultManager->memoryData.at(alloc2).domain = PageFaultManager::AllocationDomain::none; - pageFaultManager->verifyPageFault(alloc2); + pageFaultManager->verifyAndHandlePageFault(alloc2, true); EXPECT_EQ(pageFaultManager->memoryData.at(alloc2).domain, PageFaultManager::AllocationDomain::cpu); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 0u); pageFaultManager->gpuDomainHandler = &MockPageFaultManager::unprotectAndTransferMemory; pageFaultManager->memoryData.at(alloc1).domain = PageFaultManager::AllocationDomain::none; - pageFaultManager->verifyPageFault(alloc1); + pageFaultManager->verifyAndHandlePageFault(alloc1, true); EXPECT_EQ(pageFaultManager->memoryData.at(alloc1).domain, PageFaultManager::AllocationDomain::cpu); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 0u); } @@ -514,7 +556,7 @@ TEST_F(PageFaultManagerTest, givenTbxWhenVerifyingPagefaultThenVerifyPagefaultUn EXPECT_EQ(pageFaultManager->protectMemoryCalled, 1); EXPECT_EQ(pageFaultManager->transferToGpuCalled, 0); - pageFaultManager->verifyPageFault(alloc); + pageFaultManager->verifyAndHandlePageFault(alloc, true); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 1); EXPECT_EQ(pageFaultManager->transferToCpuCalled, 1); EXPECT_EQ(pageFaultManager->allowedMemoryAccessAddress, alloc); @@ -541,7 +583,7 @@ TEST_F(PageFaultManagerTest, whenVerifyingPagefaultWithPrintUsmSharedMigrationDe EXPECT_EQ(pageFaultManager->protectedMemoryAccessAddress, alloc); EXPECT_EQ(pageFaultManager->protectedSize, 10u); - pageFaultManager->verifyPageFault(alloc); + pageFaultManager->verifyAndHandlePageFault(alloc, true); std::string output = testing::internal::GetCapturedStdout(); // stop capturing @@ -580,7 +622,7 @@ TEST_F(PageFaultManagerTest, givenTbxWhenVerifyingPagefaultWithPrintUsmSharedMig EXPECT_EQ(pageFaultManager->protectedMemoryAccessAddress, alloc); EXPECT_EQ(pageFaultManager->protectedSize, 10u); - pageFaultManager->verifyPageFault(alloc); + pageFaultManager->verifyAndHandlePageFault(alloc, true); std::string output = testing::internal::GetCapturedStdout(); // stop capturing @@ -614,7 +656,7 @@ TEST_F(PageFaultManagerTest, givenTbxAndInitialPlacementGpuWhenVerifyingPagefaul EXPECT_EQ(pageFaultManager->protectedSize, 10u); EXPECT_EQ(pageFaultManager->memoryData.size(), 1u); - pageFaultManager->verifyPageFault(alloc); + pageFaultManager->verifyAndHandlePageFault(alloc, true); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 1); EXPECT_EQ(pageFaultManager->transferToGpuCalled, 0); @@ -670,7 +712,7 @@ TEST_F(PageFaultManagerTest, givenAllocationMovedToGpuDomainWhenVerifyingPagefau EXPECT_EQ(pageFaultManager->transferToGpuCalled, 0); EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 0u); - pageFaultManager->verifyPageFault(alloc); + pageFaultManager->verifyAndHandlePageFault(alloc, true); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 1); EXPECT_EQ(pageFaultManager->transferToCpuCalled, 1); EXPECT_EQ(pageFaultManager->transferToGpuCalled, 0); @@ -682,6 +724,35 @@ TEST_F(PageFaultManagerTest, givenAllocationMovedToGpuDomainWhenVerifyingPagefau EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs[0], alloc); } +TEST_F(PageFaultManagerTest, givenAllocationMovedToGpuDomainWhenVerifyingPagefaultWithHandlePageFaultFalseThenAllocationIsNotMovedToCpuDomain) { + void *cmdQ = reinterpret_cast(0xFFFF); + void *alloc = reinterpret_cast(0x1); + + memoryProperties.allocFlags.usmInitialPlacementGpu = 1; + pageFaultManager->insertAllocation(alloc, 10, unifiedMemoryManager.get(), cmdQ, memoryProperties); + EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); + EXPECT_EQ(pageFaultManager->memoryData.size(), 1u); + EXPECT_EQ(pageFaultManager->protectMemoryCalled, 1); + EXPECT_EQ(pageFaultManager->protectedMemoryAccessAddress, alloc); + EXPECT_EQ(pageFaultManager->protectedSize, 10u); + EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 1u); + EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs[0], alloc); + + pageFaultManager->moveAllocationToGpuDomain(alloc); + EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 0); + EXPECT_EQ(pageFaultManager->protectMemoryCalled, 1); + EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); + EXPECT_EQ(pageFaultManager->transferToGpuCalled, 0); + EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 0u); + + pageFaultManager->verifyAndHandlePageFault(alloc, false); + EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 0); + EXPECT_EQ(pageFaultManager->protectMemoryCalled, 1); + EXPECT_EQ(pageFaultManager->transferToCpuCalled, 0); + EXPECT_EQ(pageFaultManager->transferToGpuCalled, 0); + EXPECT_EQ(unifiedMemoryManager->nonGpuDomainAllocs.size(), 0u); +} + TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenSetAubWritableIsCalledThenAllocIsAubWritable) { REQUIRE_SVM_OR_SKIP(executionEnvironment.rootDeviceEnvironments[0]->getHardwareInfo()); @@ -727,7 +798,7 @@ TEST_F(PageFaultManagerTest, givenUnifiedMemoryAllocWhenMigratedBetweenCpuAndGpu EXPECT_EQ(pageFaultManager->protectedMemoryAccessAddress, ptr); EXPECT_EQ(pageFaultManager->protectedSize, 10u); - pageFaultManager->verifyPageFault(ptr); + pageFaultManager->verifyAndHandlePageFault(ptr, true); EXPECT_EQ(pageFaultManager->transferToCpuCalled, 1); EXPECT_EQ(pageFaultManager->allowMemoryAccessCalled, 1); EXPECT_EQ(pageFaultManager->setCpuAllocEvictableCalled, 2); diff --git a/shared/test/unit_test/page_fault_manager/linux/cpu_page_fault_manager_linux_tests.cpp b/shared/test/unit_test/page_fault_manager/linux/cpu_page_fault_manager_linux_tests.cpp index 62bf1e481b..f5e8846797 100644 --- a/shared/test/unit_test/page_fault_manager/linux/cpu_page_fault_manager_linux_tests.cpp +++ b/shared/test/unit_test/page_fault_manager/linux/cpu_page_fault_manager_linux_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 Intel Corporation + * Copyright (C) 2019-2024 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -125,10 +125,14 @@ class MockFailPageFaultManager : public PageFaultManagerLinux { using PageFaultManagerLinux::checkFaultHandlerFromPageFaultManager; using PageFaultManagerLinux::PageFaultManagerLinux; using PageFaultManagerLinux::previousHandlerRestored; + using PageFaultManagerLinux::previousPageFaultHandlers; using PageFaultManagerLinux::registerFaultHandler; - bool verifyPageFault(void *ptr) override { + bool verifyAndHandlePageFault(void *ptr, bool handlePageFault) override { verifyCalled = true; + if (handlePageFault) { + numPageFaultHandled++; + } return false; } @@ -136,22 +140,38 @@ class MockFailPageFaultManager : public PageFaultManagerLinux { mockCalled = true; } + static void mockPageFaultHandler2(int signal, siginfo_t *info, void *context) { + mockCalled2 = true; + pageFaultHandlerWrapper(signal, info, context); + } + static void mockPageFaultSimpleHandler(int signal) { simpleMockCalled = true; } + static void mockPageFaultSimpleHandler2(int signal) { + simpleMockCalled2 = true; + } + ~MockFailPageFaultManager() override { mockCalled = false; + mockCalled2 = false; simpleMockCalled = false; + simpleMockCalled2 = false; } static bool mockCalled; + static bool mockCalled2; static bool simpleMockCalled; + static bool simpleMockCalled2; bool verifyCalled = false; + int numPageFaultHandled = 0; }; bool MockFailPageFaultManager::mockCalled = false; +bool MockFailPageFaultManager::mockCalled2 = false; bool MockFailPageFaultManager::simpleMockCalled = false; +bool MockFailPageFaultManager::simpleMockCalled2 = false; TEST_F(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleAndSigInfoFlagSetThenSaSigactionIsCalled) { struct sigaction previousHandler = {}; @@ -184,6 +204,7 @@ TEST_F(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleThenSaHand auto mockPageFaultManager = std::make_unique(); EXPECT_FALSE(MockFailPageFaultManager::mockCalled); EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_EQ(1ul, mockPageFaultManager->previousPageFaultHandlers.size()); std::raise(SIGSEGV); EXPECT_TRUE(mockPageFaultManager->verifyCalled); @@ -243,3 +264,170 @@ TEST_F(PageFaultManagerLinuxTest, givenDefaultSaHandlerWhenOverwritingNewHandler mockPageFaultManager.reset(); sigaction(SIGSEGV, &originalHandler, nullptr); } + +TEST_F(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleWhenRegisterSimpleHandlerTwiceThenSimpleHandlerIsRegisteredOnce) { + struct sigaction previousHandler = {}; + struct sigaction previousHandler2 = {}; + struct sigaction mockHandler = {}; + struct sigaction mockHandler2 = {}; + mockHandler.sa_handler = MockFailPageFaultManager::mockPageFaultSimpleHandler; + auto retVal = sigaction(SIGSEGV, &mockHandler, &previousHandler); + EXPECT_EQ(retVal, 0); + + auto mockPageFaultManager = std::make_unique(); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_EQ(1ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_EQ(1, mockPageFaultManager->numPageFaultHandled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_TRUE(MockFailPageFaultManager::simpleMockCalled); + + MockFailPageFaultManager::simpleMockCalled = false; + + mockHandler2.sa_handler = MockFailPageFaultManager::mockPageFaultSimpleHandler; + retVal = sigaction(SIGSEGV, &mockHandler2, &previousHandler2); + EXPECT_EQ(retVal, 0); + mockPageFaultManager->registerFaultHandler(); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_EQ(2, mockPageFaultManager->numPageFaultHandled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_TRUE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_EQ(1ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + mockPageFaultManager.reset(); + sigaction(SIGSEGV, &previousHandler, nullptr); +} + +TEST_F(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleWhenRegisterTwoSimpleHandlersThenBothHandlersAreRegistered) { + struct sigaction previousHandler = {}; + struct sigaction previousHandler2 = {}; + struct sigaction mockHandler = {}; + struct sigaction mockHandler2 = {}; + mockHandler.sa_handler = MockFailPageFaultManager::mockPageFaultSimpleHandler; + auto retVal = sigaction(SIGSEGV, &mockHandler, &previousHandler); + EXPECT_EQ(retVal, 0); + + auto mockPageFaultManager = std::make_unique(); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled2); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + EXPECT_EQ(1ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled2); + EXPECT_TRUE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + + MockFailPageFaultManager::simpleMockCalled = false; + + mockHandler2.sa_handler = MockFailPageFaultManager::mockPageFaultSimpleHandler2; + retVal = sigaction(SIGSEGV, &mockHandler2, &previousHandler2); + EXPECT_EQ(retVal, 0); + mockPageFaultManager->registerFaultHandler(); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled2); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_TRUE(MockFailPageFaultManager::simpleMockCalled2); + EXPECT_EQ(2ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + mockPageFaultManager.reset(); + sigaction(SIGSEGV, &previousHandler, nullptr); +} + +TEST_F(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleWhenRegisterTwoHandlersThenBothHandlersAreRegistered) { + struct sigaction previousHandler = {}; + struct sigaction previousHandler2 = {}; + struct sigaction mockHandler = {}; + struct sigaction mockHandler2 = {}; + mockHandler.sa_flags = SA_SIGINFO; + mockHandler.sa_sigaction = MockFailPageFaultManager::mockPageFaultHandler; + auto retVal = sigaction(SIGSEGV, &mockHandler, &previousHandler); + EXPECT_EQ(retVal, 0); + + auto mockPageFaultManager = std::make_unique(); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled2); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + EXPECT_EQ(1ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_TRUE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled2); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + + MockFailPageFaultManager::mockCalled = false; + + mockHandler2.sa_flags = SA_SIGINFO; + mockHandler2.sa_sigaction = MockFailPageFaultManager::mockPageFaultHandler2; + retVal = sigaction(SIGSEGV, &mockHandler2, &previousHandler2); + EXPECT_EQ(retVal, 0); + mockPageFaultManager->registerFaultHandler(); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_TRUE(MockFailPageFaultManager::mockCalled); + EXPECT_TRUE(MockFailPageFaultManager::mockCalled2); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + EXPECT_EQ(2ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + mockPageFaultManager.reset(); + sigaction(SIGSEGV, &previousHandler, nullptr); +} + +TEST_F(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleWhenRegisterSimpleAndRegularHandlersThenBothHandlersAreRegistered) { + struct sigaction previousHandler = {}; + struct sigaction previousHandler2 = {}; + struct sigaction mockHandler = {}; + struct sigaction mockHandler2 = {}; + mockHandler.sa_handler = MockFailPageFaultManager::mockPageFaultSimpleHandler; + auto retVal = sigaction(SIGSEGV, &mockHandler, &previousHandler); + EXPECT_EQ(retVal, 0); + + auto mockPageFaultManager = std::make_unique(); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled2); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + EXPECT_EQ(1ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled2); + EXPECT_TRUE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + + MockFailPageFaultManager::simpleMockCalled = false; + + mockHandler2.sa_flags = SA_SIGINFO; + mockHandler2.sa_sigaction = MockFailPageFaultManager::mockPageFaultHandler2; + retVal = sigaction(SIGSEGV, &mockHandler2, &previousHandler2); + EXPECT_EQ(retVal, 0); + mockPageFaultManager->registerFaultHandler(); + + std::raise(SIGSEGV); + EXPECT_TRUE(mockPageFaultManager->verifyCalled); + EXPECT_FALSE(MockFailPageFaultManager::mockCalled); + EXPECT_TRUE(MockFailPageFaultManager::mockCalled2); + EXPECT_TRUE(MockFailPageFaultManager::simpleMockCalled); + EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled2); + EXPECT_EQ(2ul, mockPageFaultManager->previousPageFaultHandlers.size()); + + mockPageFaultManager.reset(); + sigaction(SIGSEGV, &previousHandler, nullptr); +} diff --git a/shared/test/unit_test/page_fault_manager/windows/cpu_page_fault_manager_windows_tests.cpp b/shared/test/unit_test/page_fault_manager/windows/cpu_page_fault_manager_windows_tests.cpp index d78b81318a..824f695a10 100644 --- a/shared/test/unit_test/page_fault_manager/windows/cpu_page_fault_manager_windows_tests.cpp +++ b/shared/test/unit_test/page_fault_manager/windows/cpu_page_fault_manager_windows_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 Intel Corporation + * Copyright (C) 2019-2024 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -70,7 +70,7 @@ class MockFailPageFaultManager : public PageFaultManagerWindows { using PageFaultManagerWindows::checkFaultHandlerFromPageFaultManager; using PageFaultManagerWindows::PageFaultManagerWindows; - bool verifyPageFault(void *ptr) override { + bool verifyAndHandlePageFault(void *ptr, bool handlePageFault) override { verifyCalled = true; return false; }