diff --git a/shared/source/os_interface/linux/drm_buffer_object.cpp b/shared/source/os_interface/linux/drm_buffer_object.cpp index e6a44ce149..d84cd03bcb 100644 --- a/shared/source/os_interface/linux/drm_buffer_object.cpp +++ b/shared/source/os_interface/linux/drm_buffer_object.cpp @@ -147,24 +147,26 @@ int BufferObject::exec(uint32_t used, size_t startOffset, unsigned int flags, bo int ret = ioctlHelper->execBuffer(&execbuf, completionGpuAddress, completionValue); if (ret != 0) { - int err = this->drm->getErrno(); - if (err == EOPNOTSUPP) { - PRINT_DEBUG_STRING(DebugManager.flags.PrintDebugMessages.get(), stderr, "ioctl(I915_GEM_EXECBUFFER2) failed with %d. errno=%d(%s)\n", ret, err, strerror(err)); - return err; - } + do { + int err = this->drm->getErrno(); + if (err == EOPNOTSUPP) { + PRINT_DEBUG_STRING(DebugManager.flags.PrintDebugMessages.get(), stderr, "ioctl(I915_GEM_EXECBUFFER2) failed with %d. errno=%d(%s)\n", ret, err, strerror(err)); + return err; + } - evictUnusedAllocations(false, true); - ret = ioctlHelper->execBuffer(&execbuf, completionGpuAddress, completionValue); - } + evictUnusedAllocations(false, true); + ret = ioctlHelper->execBuffer(&execbuf, completionGpuAddress, completionValue); - if (ret != 0) { - const auto status = evictUnusedAllocations(true, true); - if (status == MemoryOperationsStatus::GPU_HANG_DETECTED_DURING_OPERATION) { - PRINT_DEBUG_STRING(DebugManager.flags.PrintDebugMessages.get(), stderr, "Error! GPU hang detected in BufferObject::exec(). Returning %d\n", gpuHangDetected); - return gpuHangDetected; - } + if (ret != 0) { + const auto status = evictUnusedAllocations(true, true); + if (status == MemoryOperationsStatus::GPU_HANG_DETECTED_DURING_OPERATION) { + PRINT_DEBUG_STRING(DebugManager.flags.PrintDebugMessages.get(), stderr, "Error! GPU hang detected in BufferObject::exec(). Returning %d\n", gpuHangDetected); + return gpuHangDetected; + } - ret = ioctlHelper->execBuffer(&execbuf, completionGpuAddress, completionValue); + ret = ioctlHelper->execBuffer(&execbuf, completionGpuAddress, completionValue); + } + } while (ret != 0 && this->drm->getErrno() == ENXIO); } if (ret == 0) { diff --git a/shared/source/os_interface/linux/drm_neo.cpp b/shared/source/os_interface/linux/drm_neo.cpp index 1b6f19efbb..7b0f7e3ea9 100644 --- a/shared/source/os_interface/linux/drm_neo.cpp +++ b/shared/source/os_interface/linux/drm_neo.cpp @@ -1315,13 +1315,13 @@ uint64_t Drm::getPatIndex(Gmm *gmm, AllocationType allocationType, CacheRegion c return patIndex; } -int changeBufferObjectBinding(Drm *drm, OsContext *osContext, uint32_t vmHandleId, BufferObject *bo, bool bind) { - auto vmId = drm->getVirtualMemoryAddressSpace(vmHandleId); - auto ioctlHelper = drm->getIoctlHelper(); +int Drm::changeBufferObjectBinding(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo, bool bind) { + auto vmId = this->getVirtualMemoryAddressSpace(vmHandleId); + auto ioctlHelper = this->getIoctlHelper(); uint64_t flags = 0u; - if (drm->isPerContextVMRequired()) { + if (this->isPerContextVMRequired()) { auto osContextLinux = static_cast(osContext); UNRECOVERABLE_IF(osContextLinux->getDrmVmIds().size() <= vmHandleId); vmId = osContextLinux->getDrmVmIds()[vmHandleId]; @@ -1336,7 +1336,7 @@ int changeBufferObjectBinding(Drm *drm, OsContext *osContext, uint32_t vmHandleI bool bindCapture = bo->isMarkedForCapture(); bool bindImmediate = bo->isImmediateBindingRequired(); bool bindMakeResident = false; - if (drm->useVMBindImmediate()) { + if (this->useVMBindImmediate()) { bindMakeResident = bo->isExplicitResidencyRequired(); bindImmediate = true; } @@ -1368,7 +1368,7 @@ int changeBufferObjectBinding(Drm *drm, OsContext *osContext, uint32_t vmHandleI VmBindExtSetPatT vmBindExtSetPat{}; - if (drm->isVmBindPatIndexProgrammingSupported()) { + if (this->isVmBindPatIndexProgrammingSupported()) { UNRECOVERABLE_IF(bo->peekPatIndex() == CommonConstants::unsupportedPatIndex); ioctlHelper->fillVmBindExtSetPat(vmBindExtSetPat, bo->peekPatIndex(), castToUint64(extensions.get())); vmBind.extensions = castToUint64(vmBindExtSetPat); @@ -1381,13 +1381,13 @@ int changeBufferObjectBinding(Drm *drm, OsContext *osContext, uint32_t vmHandleI VmBindExtUserFenceT vmBindExtUserFence{}; - if (drm->useVMBindImmediate()) { - lock = drm->lockBindFenceMutex(); + if (this->useVMBindImmediate()) { + lock = this->lockBindFenceMutex(); - if (!drm->hasPageFaultSupport() || bo->isExplicitResidencyRequired()) { + if (!this->hasPageFaultSupport() || bo->isExplicitResidencyRequired()) { auto nextExtension = vmBind.extensions; - auto address = castToUint64(drm->getFenceAddr(vmHandleId)); - auto value = drm->getNextFenceVal(vmHandleId); + auto address = castToUint64(this->getFenceAddr(vmHandleId)); + auto value = this->getNextFenceVal(vmHandleId); ioctlHelper->fillVmBindExtUserFence(vmBindExtUserFence, address, value, nextExtension); vmBind.extensions = castToUint64(vmBindExtUserFence); @@ -1400,7 +1400,7 @@ int changeBufferObjectBinding(Drm *drm, OsContext *osContext, uint32_t vmHandleI break; } - drm->setNewResourceBoundToVM(vmHandleId); + this->setNewResourceBoundToVM(vmHandleId); } else { vmBind.handle = 0u; ret = ioctlHelper->vmUnbind(vmBind); @@ -1415,16 +1415,18 @@ int changeBufferObjectBinding(Drm *drm, OsContext *osContext, uint32_t vmHandleI } int Drm::bindBufferObject(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo) { - auto ret = changeBufferObjectBinding(this, osContext, vmHandleId, bo, true); + auto ret = changeBufferObjectBinding(osContext, vmHandleId, bo, true); if (ret != 0) { - static_cast(this->rootDeviceEnvironment.memoryOperationsInterface.get())->evictUnusedAllocations(false, false); - ret = changeBufferObjectBinding(this, osContext, vmHandleId, bo, true); + do { + static_cast(this->rootDeviceEnvironment.memoryOperationsInterface.get())->evictUnusedAllocations(false, false); + ret = changeBufferObjectBinding(osContext, vmHandleId, bo, true); + } while (ret != 0 && getErrno() == ENXIO); } return ret; } int Drm::unbindBufferObject(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo) { - return changeBufferObjectBinding(this, osContext, vmHandleId, bo, false); + return changeBufferObjectBinding(osContext, vmHandleId, bo, false); } int Drm::createDrmVirtualMemory(uint32_t &drmVmId) { diff --git a/shared/source/os_interface/linux/drm_neo.h b/shared/source/os_interface/linux/drm_neo.h index e6d9c0e2ba..2a1c9d24ce 100644 --- a/shared/source/os_interface/linux/drm_neo.h +++ b/shared/source/os_interface/linux/drm_neo.h @@ -129,6 +129,7 @@ class Drm : public DriverModel { uint32_t getVirtualMemoryAddressSpace(uint32_t vmId) const; MOCKABLE_VIRTUAL int bindBufferObject(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo); MOCKABLE_VIRTUAL int unbindBufferObject(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo); + MOCKABLE_VIRTUAL int changeBufferObjectBinding(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo, bool bind); int setupHardwareInfo(const DeviceDescriptor *, bool); void setupSystemInfo(HardwareInfo *hwInfo, SystemInfo *sysInfo); void setupCacheInfo(const HardwareInfo &hwInfo); diff --git a/shared/test/unit_test/os_interface/linux/drm_buffer_object_tests.cpp b/shared/test/unit_test/os_interface/linux/drm_buffer_object_tests.cpp index cd2a3e6d5c..cb8b942e4a 100644 --- a/shared/test/unit_test/os_interface/linux/drm_buffer_object_tests.cpp +++ b/shared/test/unit_test/os_interface/linux/drm_buffer_object_tests.cpp @@ -55,14 +55,14 @@ TEST_F(DrmBufferObjectTest, GivenDetectedGpuHangDuringEvictUnusedAllocationsWhen } TEST_F(DrmBufferObjectTest, WhenSettingTilingThenCallSucceeds) { - mock->ioctl_expected.total = 1; //set_tiling + mock->ioctl_expected.total = 1; // set_tiling auto tilingY = mock->getIoctlHelper()->getDrmParamValue(DrmParam::TilingY); auto ret = bo->setTiling(tilingY, 0); EXPECT_TRUE(ret); } TEST_F(DrmBufferObjectTest, WhenSettingSameTilingThenCallSucceeds) { - mock->ioctl_expected.total = 0; //set_tiling + mock->ioctl_expected.total = 0; // set_tiling auto tilingY = mock->getIoctlHelper()->getDrmParamValue(DrmParam::TilingY); bo->tilingMode = tilingY; auto ret = bo->setTiling(tilingY, 0); @@ -70,7 +70,7 @@ TEST_F(DrmBufferObjectTest, WhenSettingSameTilingThenCallSucceeds) { } TEST_F(DrmBufferObjectTest, GivenInvalidTilingWhenSettingTilingThenCallFails) { - mock->ioctl_expected.total = 1; //set_tiling + mock->ioctl_expected.total = 1; // set_tiling auto tilingY = mock->getIoctlHelper()->getDrmParamValue(DrmParam::TilingY); mock->ioctl_res = -1; auto ret = bo->setTiling(tilingY, 0); @@ -91,7 +91,7 @@ TEST_F(DrmBufferObjectTest, givenAddressThatWhenSizeIsAddedCrosses32BitBoundaryW bo->setAddress(((uint64_t)1u << 32) - 0x1000u); bo->setSize(0x1000); bo->fillExecObject(execObject, osContext.get(), 0, 1); - //base address + size > size of 32bit address space + // base address + size > size of 32bit address space EXPECT_TRUE(execObject.has48BAddressSupportFlag()); } @@ -102,7 +102,7 @@ TEST_F(DrmBufferObjectTest, givenAddressThatWhenSizeIsAddedWithin32BitBoundaryWh bo->setAddress(((uint64_t)1u << 32) - 0x1000u); bo->setSize(0xFFF); bo->fillExecObject(execObject, osContext.get(), 0, 1); - //base address + size < size of 32bit address space + // base address + size < size of 32bit address space EXPECT_TRUE(execObject.has48BAddressSupportFlag()); } @@ -465,6 +465,136 @@ TEST(DrmBufferObject, givenPrintBOBindingResultWhenBOBindAndUnbindSucceedsThenPr EXPECT_STREQ(unbindOutput.c_str(), "unbind BO-0 from VM 0, drmVmId = 1, range: 0 - 0, size: 0, result: 0\n"); } +TEST(DrmBufferObject, givenBufferObjectWhenFirstChangeBufferObjectBindingReturnSuccessThenItsNotCalledAnyMore) { + struct DrmMockToSucceedBindBufferObject : public DrmMock { + DrmMockToSucceedBindBufferObject(RootDeviceEnvironment &rootDeviceEnvironment) + : DrmMock(rootDeviceEnvironment) {} + int changeBufferObjectBinding(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo, bool bind) override { + changeBufferObjectBindingCalled++; + return changeBufferObjectBindingCalled >= 1 ? 0 : -1; + } + uint32_t changeBufferObjectBindingCalled = 0; + }; + auto executionEnvironment = std::make_unique(); + executionEnvironment->setDebuggingEnabled(); + executionEnvironment->prepareRootDeviceEnvironments(1); + executionEnvironment->rootDeviceEnvironments[0]->setHwInfo(defaultHwInfo.get()); + executionEnvironment->rootDeviceEnvironments[0]->initGmm(); + executionEnvironment->calculateMaxOsContextCount(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface = std::make_unique(); + auto drm = std::make_unique(*executionEnvironment->rootDeviceEnvironments[0]); + auto drmPtr = drm.get(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::unique_ptr(drm.get())); + executionEnvironment->rootDeviceEnvironments[0]->memoryOperationsInterface = DrmMemoryOperationsHandler::create(*drm.release(), 0u); + std::unique_ptr device(MockDevice::createWithExecutionEnvironment(defaultHwInfo.get(), executionEnvironment.release(), 0)); + auto osContextCount = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount(); + MockBufferObject bo(drmPtr, 3, 0, 0, osContextCount); + auto contextId = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount() / 2; + auto osContext = device->getExecutionEnvironment()->memoryManager->getRegisteredEngines()[contextId].osContext; + auto ret = drmPtr->bindBufferObject(osContext, 0, &bo); + EXPECT_EQ(ret, 0); + EXPECT_EQ(drmPtr->changeBufferObjectBindingCalled, 1u); +} + +TEST(DrmBufferObject, givenBufferObjectWhenSecondChangeBufferObjectBindingReturnSuccessThenItsNotCalledAnyMore) { + struct DrmMockToSucceedBindBufferObject : public DrmMock { + DrmMockToSucceedBindBufferObject(RootDeviceEnvironment &rootDeviceEnvironment) + : DrmMock(rootDeviceEnvironment) {} + int changeBufferObjectBinding(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo, bool bind) override { + changeBufferObjectBindingCalled++; + return changeBufferObjectBindingCalled >= 2 ? 0 : -1; + } + uint32_t changeBufferObjectBindingCalled = 0; + }; + auto executionEnvironment = std::make_unique(); + executionEnvironment->setDebuggingEnabled(); + executionEnvironment->prepareRootDeviceEnvironments(1); + executionEnvironment->rootDeviceEnvironments[0]->setHwInfo(defaultHwInfo.get()); + executionEnvironment->rootDeviceEnvironments[0]->initGmm(); + executionEnvironment->calculateMaxOsContextCount(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface = std::make_unique(); + auto drm = std::make_unique(*executionEnvironment->rootDeviceEnvironments[0]); + auto drmPtr = drm.get(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::unique_ptr(drm.get())); + executionEnvironment->rootDeviceEnvironments[0]->memoryOperationsInterface = DrmMemoryOperationsHandler::create(*drm.release(), 0u); + std::unique_ptr device(MockDevice::createWithExecutionEnvironment(defaultHwInfo.get(), executionEnvironment.release(), 0)); + auto osContextCount = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount(); + MockBufferObject bo(drmPtr, 3, 0, 0, osContextCount); + auto contextId = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount() / 2; + auto osContext = device->getExecutionEnvironment()->memoryManager->getRegisteredEngines()[contextId].osContext; + auto ret = drmPtr->bindBufferObject(osContext, 0, &bo); + EXPECT_EQ(ret, 0); + EXPECT_EQ(drmPtr->changeBufferObjectBindingCalled, 2u); +} + +TEST(DrmBufferObject, givenBufferObjectWhenSecondChangeBufferObjectBindingReturnFailAndErrnoIsNotENXIOThenItsNotCalledAnyMore) { + struct DrmMockToSucceedBindBufferObject : public DrmMock { + DrmMockToSucceedBindBufferObject(RootDeviceEnvironment &rootDeviceEnvironment) + : DrmMock(rootDeviceEnvironment) {} + int changeBufferObjectBinding(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo, bool bind) override { + changeBufferObjectBindingCalled++; + return changeBufferObjectBindingCalled >= 3 ? 0 : -1; + } + int getErrno() override { + return EINTR; + } + uint32_t changeBufferObjectBindingCalled = 0; + }; + auto executionEnvironment = std::make_unique(); + executionEnvironment->setDebuggingEnabled(); + executionEnvironment->prepareRootDeviceEnvironments(1); + executionEnvironment->rootDeviceEnvironments[0]->setHwInfo(defaultHwInfo.get()); + executionEnvironment->rootDeviceEnvironments[0]->initGmm(); + executionEnvironment->calculateMaxOsContextCount(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface = std::make_unique(); + auto drm = std::make_unique(*executionEnvironment->rootDeviceEnvironments[0]); + auto drmPtr = drm.get(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::unique_ptr(drm.get())); + executionEnvironment->rootDeviceEnvironments[0]->memoryOperationsInterface = DrmMemoryOperationsHandler::create(*drm.release(), 0u); + std::unique_ptr device(MockDevice::createWithExecutionEnvironment(defaultHwInfo.get(), executionEnvironment.release(), 0)); + auto osContextCount = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount(); + MockBufferObject bo(drmPtr, 3, 0, 0, osContextCount); + auto contextId = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount() / 2; + auto osContext = device->getExecutionEnvironment()->memoryManager->getRegisteredEngines()[contextId].osContext; + auto ret = drmPtr->bindBufferObject(osContext, 0, &bo); + EXPECT_EQ(ret, -1); + EXPECT_EQ(drmPtr->changeBufferObjectBindingCalled, 2u); +} + +TEST(DrmBufferObject, givenBufferObjectWhenSecondChangeBufferObjectBindingReturnFailAndErrnoIsENXIOThenItsCalledUntillSuccess) { + struct DrmMockToSucceedBindBufferObject : public DrmMock { + DrmMockToSucceedBindBufferObject(RootDeviceEnvironment &rootDeviceEnvironment) + : DrmMock(rootDeviceEnvironment) {} + int changeBufferObjectBinding(OsContext *osContext, uint32_t vmHandleId, BufferObject *bo, bool bind) override { + changeBufferObjectBindingCalled++; + return changeBufferObjectBindingCalled >= 3 ? 0 : -1; + } + int getErrno() override { + return ENXIO; + } + uint32_t changeBufferObjectBindingCalled = 0; + }; + auto executionEnvironment = std::make_unique(); + executionEnvironment->setDebuggingEnabled(); + executionEnvironment->prepareRootDeviceEnvironments(1); + executionEnvironment->rootDeviceEnvironments[0]->setHwInfo(defaultHwInfo.get()); + executionEnvironment->rootDeviceEnvironments[0]->initGmm(); + executionEnvironment->calculateMaxOsContextCount(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface = std::make_unique(); + auto drm = std::make_unique(*executionEnvironment->rootDeviceEnvironments[0]); + auto drmPtr = drm.get(); + executionEnvironment->rootDeviceEnvironments[0]->osInterface->setDriverModel(std::unique_ptr(drm.get())); + executionEnvironment->rootDeviceEnvironments[0]->memoryOperationsInterface = DrmMemoryOperationsHandler::create(*drm.release(), 0u); + std::unique_ptr device(MockDevice::createWithExecutionEnvironment(defaultHwInfo.get(), executionEnvironment.release(), 0)); + auto osContextCount = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount(); + MockBufferObject bo(drmPtr, 3, 0, 0, osContextCount); + auto contextId = device->getExecutionEnvironment()->memoryManager->getRegisteredEnginesCount() / 2; + auto osContext = device->getExecutionEnvironment()->memoryManager->getRegisteredEngines()[contextId].osContext; + auto ret = drmPtr->bindBufferObject(osContext, 0, &bo); + EXPECT_EQ(ret, 0); + EXPECT_EQ(drmPtr->changeBufferObjectBindingCalled, 3u); +} + TEST(DrmBufferObject, givenPrintBOBindingResultWhenBOBindAndUnbindFailsThenPrintDebugInformationAboutBOBindingResultWithErrno) { struct DrmMockToFailBindBufferObject : public DrmMock { DrmMockToFailBindBufferObject(RootDeviceEnvironment &rootDeviceEnvironment) @@ -619,3 +749,65 @@ TEST_F(DrmBufferObjectTest, whenBoRequiresExplicitResidencyThenTheCorrespondingQ EXPECT_EQ(required, bo.isExplicitResidencyRequired()); } } + +template +class MyDrmMockCustom : public DrmMockCustom { + public: + MyDrmMockCustom(RootDeviceEnvironment &rootDeviceEnvironment) : DrmMockCustom(rootDeviceEnvironment) { + errnoValue = errnoToReturn; + } + int ioctl(DrmIoctl request, void *arg) override { + ioctlCalledTimes++; + return ioctlCalledTimes >= callsToSuccess ? 0 : -1; + }; + int ioctlCalledTimes = 0; +}; +using MyDrmMockBufferObjectFixture = DrmBufferObjectFixture>; +using MyDrmBufferObjectTest = Test; + +TEST_F(MyDrmBufferObjectTest, givenDrmWhenFirstIoctlReturnSuccessThenDoNotRetry) { + ExecObject execObjectsStorage = {}; + auto ret = bo->exec(0, 0, 0, false, osContext.get(), 0, 1, nullptr, 0u, &execObjectsStorage, 0, 0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(mock->ioctlCalledTimes, 1); +} + +using MyDrmMockBufferObjectFixtureRetryOnce = DrmBufferObjectFixture>; +using MyDrmBufferObjectTestRetryOnce = Test; + +TEST_F(MyDrmBufferObjectTestRetryOnce, givenDrmWhenFirstIoctlReturnFailThenRetry) { + ExecObject execObjectsStorage = {}; + auto ret = bo->exec(0, 0, 0, false, osContext.get(), 0, 1, nullptr, 0u, &execObjectsStorage, 0, 0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(mock->ioctlCalledTimes, 2); +} + +using MyDrmMockBufferObjectFixtureRetryTwice = DrmBufferObjectFixture>; +using MyDrmBufferObjectTestRetryTwice = Test; + +TEST_F(MyDrmBufferObjectTestRetryTwice, givenDrmWhenSecondIoctlReturnFailThenRetry) { + ExecObject execObjectsStorage = {}; + auto ret = bo->exec(0, 0, 0, false, osContext.get(), 0, 1, nullptr, 0u, &execObjectsStorage, 0, 0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(mock->ioctlCalledTimes, 3); +} + +using MyDrmMockBufferObjectFixtureThreeTimes = DrmBufferObjectFixture>; +using MyDrmBufferObjectTestRetryThreeTimes = Test; + +TEST_F(MyDrmBufferObjectTestRetryThreeTimes, givenDrmWhenThirdIoctlReturnFailAndErrnoIsNoENXIOThenDoNotRetry) { + ExecObject execObjectsStorage = {}; + auto ret = bo->exec(0, 0, 0, false, osContext.get(), 0, 1, nullptr, 0u, &execObjectsStorage, 0, 0); + EXPECT_EQ(ret, ESRCH); + EXPECT_EQ(mock->ioctlCalledTimes, 3); +} + +using MyDrmMockBufferObjectFixtureThreeTimesWithENXIO = DrmBufferObjectFixture>; +using MyDrmBufferObjectTestRetryThreeTimesWithENXIO = Test; + +TEST_F(MyDrmBufferObjectTestRetryThreeTimesWithENXIO, givenDrmWhenThirdIoctlReturnFailAndErrnoIsENXIOThenDoNotRetry) { + ExecObject execObjectsStorage = {}; + auto ret = bo->exec(0, 0, 0, false, osContext.get(), 0, 1, nullptr, 0u, &execObjectsStorage, 0, 0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(mock->ioctlCalledTimes, 4); +} \ No newline at end of file