From cd1a7070487096f55d2bd4f8a6504e68a004fc7a Mon Sep 17 00:00:00 2001 From: Jihong Min Date: Tue, 21 Jul 2026 14:05:47 +0900 Subject: [PATCH] Avoid GPU work during recovery teardown Detect RM recovery state before nvidia-drm teardown and skip console restoration, display method submission, and GPU progress waits while releasing NVKMS software resources. --- kernel-open/common/inc/nvkms-kapi.h | 13 ++++ kernel-open/nvidia-drm/nvidia-drm-drv.c | 26 +++++-- .../unix/nvidia-3d/interface/nvidia-3d.h | 5 +- .../unix/nvidia-3d/src/nvidia-3d-surface.c | 8 +- .../include/nvkms-headsurface-config.h | 2 + src/nvidia-modeset/include/nvkms-types.h | 6 ++ src/nvidia-modeset/interface/nvkms-api.h | 22 ++++++ .../kapi/interface/nvkms-kapi.h | 13 ++++ src/nvidia-modeset/kapi/src/nvkms-kapi.c | 65 ++++++++++++++++ src/nvidia-modeset/src/dp/nvdp-connector.cpp | 4 +- .../src/nvkms-console-restore.c | 16 +++- src/nvidia-modeset/src/nvkms-difr.c | 6 +- src/nvidia-modeset/src/nvkms-evo.c | 26 +++++-- src/nvidia-modeset/src/nvkms-flip.c | 4 + src/nvidia-modeset/src/nvkms-headsurface-3d.c | 4 +- .../src/nvkms-headsurface-config.c | 45 +++++++++++ .../src/nvkms-headsurface-swapgroup.c | 6 +- src/nvidia-modeset/src/nvkms-headsurface.c | 3 +- src/nvidia-modeset/src/nvkms-lut.c | 2 +- src/nvidia-modeset/src/nvkms-rm.c | 78 +++++++++++-------- src/nvidia-modeset/src/nvkms-surface.c | 12 ++- src/nvidia-modeset/src/nvkms.c | 40 +++++++++- 22 files changed, 340 insertions(+), 66 deletions(-) diff --git a/kernel-open/common/inc/nvkms-kapi.h b/kernel-open/common/inc/nvkms-kapi.h index 8e838eafa9..5bae3844c5 100644 --- a/kernel-open/common/inc/nvkms-kapi.h +++ b/kernel-open/common/inc/nvkms-kapi.h @@ -1675,6 +1675,19 @@ struct NvKmsKapiFunctionsTable { struct NvKmsKapiDevice *device, const NvU32 head, struct NvKmsKapiVblankIntrCallback *pCallback); + + /* + * Return NV_TRUE when RM reports that the device requires recovery. Query + * failures other than an unsupported command are treated as requiring + * recovery so that teardown does not submit more display methods. + */ + NvBool (*deviceNeedsRecovery)(struct NvKmsKapiDevice *device); + + /* + * Disable console restoration and release modeset ownership without + * submitting display methods to a device that requires recovery. + */ + NvBool (*prepareForRecovery)(struct NvKmsKapiDevice *device); }; /** @} */ diff --git a/kernel-open/nvidia-drm/nvidia-drm-drv.c b/kernel-open/nvidia-drm/nvidia-drm-drv.c index df29f49139..e226092286 100644 --- a/kernel-open/nvidia-drm/nvidia-drm-drv.c +++ b/kernel-open/nvidia-drm/nvidia-drm-drv.c @@ -898,6 +898,7 @@ static int nv_drm_dev_load(struct drm_device *dev) static void nv_drm_dev_unload(struct drm_device *dev) { struct NvKmsKapiDevice *pDevice = NULL; + NvBool recoveryTeardown; struct nv_drm_device *nv_dev = to_nv_device(dev); @@ -907,23 +908,38 @@ static void nv_drm_dev_unload(struct drm_device *dev) return; } + recoveryTeardown = nvKms->deviceNeedsRecovery(nv_dev->pDevice); + + if (recoveryTeardown) { + NV_DRM_DEV_LOG_WARN( + nv_dev, + "Using recovery-safe teardown; skipping atomic display shutdown and console restore"); + } + /* Release modeset ownership if fbdev is enabled */ #if defined(NV_DRM_FBDEV_AVAILABLE) - if (nv_dev->hasFramebufferConsole) { + if (nv_dev->hasFramebufferConsole && !recoveryTeardown) { drm_atomic_helper_shutdown(dev); nvKms->releaseOwnership(nv_dev->pDevice); } #endif - cancel_delayed_work_sync(&nv_dev->hotplug_event_work); + /* Stop new event work before tearing down the NVKMS device. */ mutex_lock(&nv_dev->lock); + atomic_set(&nv_dev->enable_event_handling, false); + mutex_unlock(&nv_dev->lock); + cancel_delayed_work_sync(&nv_dev->hotplug_event_work); - WARN_ON(nv_dev->subOwnershipGranted); + if (recoveryTeardown && + !nvKms->prepareForRecovery(nv_dev->pDevice)) { + NV_DRM_DEV_LOG_ERR( + nv_dev, "Failed to prepare NVKMS for recovery-safe teardown"); + } - /* Disable event handling */ + mutex_lock(&nv_dev->lock); - atomic_set(&nv_dev->enable_event_handling, false); + WARN_ON(nv_dev->subOwnershipGranted); /* Clean up output polling */ diff --git a/src/common/unix/nvidia-3d/interface/nvidia-3d.h b/src/common/unix/nvidia-3d/interface/nvidia-3d.h index 372415730a..7af613031b 100644 --- a/src/common/unix/nvidia-3d/interface/nvidia-3d.h +++ b/src/common/unix/nvidia-3d/interface/nvidia-3d.h @@ -122,7 +122,10 @@ void nv3dFreeChannelObject( */ NvBool nv3dAllocChannelSurface(Nv3dChannelPtr p3dChannel); -void nv3dFreeChannelSurface(Nv3dChannelPtr p3dChannel); +/* skipChannelIdle is only safe when the channel cannot make progress. */ +void nv3dFreeChannelSurface( + Nv3dChannelPtr p3dChannel, + NvBool skipChannelIdle); /* diff --git a/src/common/unix/nvidia-3d/src/nvidia-3d-surface.c b/src/common/unix/nvidia-3d/src/nvidia-3d-surface.c index 421b09ddbf..ce61340799 100644 --- a/src/common/unix/nvidia-3d/src/nvidia-3d-surface.c +++ b/src/common/unix/nvidia-3d/src/nvidia-3d-surface.c @@ -189,7 +189,9 @@ NvBool nv3dAllocChannelSurface(Nv3dChannelPtr p3dChannel) return TRUE; } -void nv3dFreeChannelSurface(Nv3dChannelPtr p3dChannel) +void nv3dFreeChannelSurface( + Nv3dChannelPtr p3dChannel, + NvBool skipChannelIdle) { if (p3dChannel->p3dDevice == NULL) { return; @@ -201,7 +203,9 @@ void nv3dFreeChannelSurface(Nv3dChannelPtr p3dChannel) * that any methods in the channel that might reference the * gpuAddress have idled before we unmap the address. */ - nvPushIdleChannel(p3dChannel->pPushChannel); + if (!skipChannelIdle) { + nvPushIdleChannel(p3dChannel->pPushChannel); + } UnmapSurface(p3dChannel, p3dChannel->surface.gpuAddress); diff --git a/src/nvidia-modeset/include/nvkms-headsurface-config.h b/src/nvidia-modeset/include/nvkms-headsurface-config.h index 54740cf9b6..06e391d350 100644 --- a/src/nvidia-modeset/include/nvkms-headsurface-config.h +++ b/src/nvidia-modeset/include/nvkms-headsurface-config.h @@ -216,6 +216,8 @@ void nvHsConfigFreeResources( NVDevEvoRec *pDevEvo, NVHsConfig *pHsConfig); +void nvHsConfigFreeDeviceResourcesForRecovery(NVDevEvoPtr pDevEvo); + void nvHsConfigStop( NVDevEvoPtr pDevEvo, const NVHsConfig *pHsConfig); diff --git a/src/nvidia-modeset/include/nvkms-types.h b/src/nvidia-modeset/include/nvkms-types.h index b0c60a607e..27105d710f 100644 --- a/src/nvidia-modeset/include/nvkms-types.h +++ b/src/nvidia-modeset/include/nvkms-types.h @@ -1121,6 +1121,12 @@ typedef struct _NVEvoDevRec { * functions that use it. */ NvBool skipConsoleRestore : 1; + /* + * Indicates that the GPU requires recovery and teardown must not submit + * display methods or ask RM to restore the console. Unlike + * skipConsoleRestore, this remains set until the device is freed. + */ + NvBool skipConsoleRestoreOnTeardown : 1; /* * Indicates that hotplug events that occur while NVKMS is the modeset owner * should trigger console restore modesets. diff --git a/src/nvidia-modeset/interface/nvkms-api.h b/src/nvidia-modeset/interface/nvkms-api.h index 35e81ed2ba..e8b0537c94 100644 --- a/src/nvidia-modeset/interface/nvkms-api.h +++ b/src/nvidia-modeset/interface/nvkms-api.h @@ -274,6 +274,7 @@ enum NvKmsIoctlCommand { NVKMS_IOCTL_FRAMEBUFFER_CONSOLE_DISABLED, NVKMS_IOCTL_REGISTER_VBLANK_INTR_CALLBACK, NVKMS_IOCTL_UNREGISTER_VBLANK_INTR_CALLBACK, + NVKMS_IOCTL_PREPARE_FOR_RECOVERY, }; @@ -4337,6 +4338,27 @@ struct NvKmsFramebufferConsoleDisabledParams { struct NvKmsFramebufferConsoleDisabledReply reply; }; +/* + * NVKMS_IOCTL_PREPARE_FOR_RECOVERY + * + * Prepare an NVKMS device for teardown after the GPU has stopped responding. + * Console restore is disabled because it requires submitting display methods + * to the failed GPU. This IOCTL can only be used by kernel-mode clients. + */ + +struct NvKmsPrepareForRecoveryRequest { + NvKmsDeviceHandle deviceHandle; +}; + +struct NvKmsPrepareForRecoveryReply { + NvU32 padding; +}; + +struct NvKmsPrepareForRecoveryParams { + struct NvKmsPrepareForRecoveryRequest request; + struct NvKmsPrepareForRecoveryReply reply; +}; + /*! * NVKMS_IOCTL_REGISTER_VBLANK_INTR_CALLBACK: * diff --git a/src/nvidia-modeset/kapi/interface/nvkms-kapi.h b/src/nvidia-modeset/kapi/interface/nvkms-kapi.h index 8e838eafa9..5bae3844c5 100644 --- a/src/nvidia-modeset/kapi/interface/nvkms-kapi.h +++ b/src/nvidia-modeset/kapi/interface/nvkms-kapi.h @@ -1675,6 +1675,19 @@ struct NvKmsKapiFunctionsTable { struct NvKmsKapiDevice *device, const NvU32 head, struct NvKmsKapiVblankIntrCallback *pCallback); + + /* + * Return NV_TRUE when RM reports that the device requires recovery. Query + * failures other than an unsupported command are treated as requiring + * recovery so that teardown does not submit more display methods. + */ + NvBool (*deviceNeedsRecovery)(struct NvKmsKapiDevice *device); + + /* + * Disable console restoration and release modeset ownership without + * submitting display methods to a device that requires recovery. + */ + NvBool (*prepareForRecovery)(struct NvKmsKapiDevice *device); }; /** @} */ diff --git a/src/nvidia-modeset/kapi/src/nvkms-kapi.c b/src/nvidia-modeset/kapi/src/nvkms-kapi.c index 51178eeec1..ca88b4b0e2 100644 --- a/src/nvidia-modeset/kapi/src/nvkms-kapi.c +++ b/src/nvidia-modeset/kapi/src/nvkms-kapi.c @@ -416,11 +416,62 @@ static NvBool RmAllocateDevice(struct NvKmsKapiDevice *device) return NV_FALSE; } +static NvBool DeviceNeedsRecovery(struct NvKmsKapiDevice *device) +{ + NV2080_CTRL_GPU_GET_RECOVERY_ACTION_PARAMS params = { }; + NvU32 ret; + + if (device->hRmSubDevice == 0x0) { + return NV_TRUE; + } + + ret = nvRmApiControl(device->hRmClient, + device->hRmSubDevice, + NV2080_CTRL_CMD_GPU_GET_RECOVERY_ACTION, + ¶ms, + sizeof(params)); + + if (ret == NVOS_STATUS_SUCCESS) { + return params.action != NV2080_CTRL_GPU_RECOVERY_ACTION_NONE; + } + + if (ret == NV_ERR_NOT_SUPPORTED) { + return NV_FALSE; + } + + nvKmsKapiLogDeviceDebug( + device, + "Failed to query GPU recovery action (status 0x%08x); using recovery teardown", + ret); + + return NV_TRUE; +} + +static NvBool PrepareForRecovery(struct NvKmsKapiDevice *device) +{ + struct NvKmsPrepareForRecoveryParams params = { }; + + if (device->hKmsDevice == 0x0) { + return NV_TRUE; + } + + params.request.deviceHandle = device->hKmsDevice; + + return nvkms_ioctl_from_kapi(device->pKmsOpen, + NVKMS_IOCTL_PREPARE_FOR_RECOVERY, + ¶ms, sizeof(params)); +} + /* * Helper function to free NVKMS objects allocated for NvKmsKapiDevice. */ static void KmsFreeDevice(struct NvKmsKapiDevice *device) { + if (DeviceNeedsRecovery(device) && !PrepareForRecovery(device)) { + nvKmsKapiLogDeviceDebug( + device, "Failed to prepare NVKMS for GPU recovery"); + } + /* Free notifier and semaphore memory */ nvKmsKapiFreeNisoSurface(device, &device->semaphore); @@ -622,6 +673,10 @@ static NvBool KmsAllocateDevice(struct NvKmsKapiDevice *device) static void FreeDevice(struct NvKmsKapiDevice *device) { + if (device == NULL) { + return; + } + /* Free NVKMS objects allocated for NvKmsKapiDevice */ KmsFreeDevice(device); @@ -1012,6 +1067,14 @@ static void ReleaseOwnership(struct NvKmsKapiDevice *device) return; } + if (DeviceNeedsRecovery(device)) { + if (!PrepareForRecovery(device)) { + nvKmsKapiLogDeviceDebug( + device, "Failed to prepare NVKMS for GPU recovery"); + } + return; + } + paramsRelease.request.deviceHandle = device->hKmsDevice; nvkms_ioctl_from_kapi(device->pKmsOpen, @@ -4206,6 +4269,8 @@ NvBool nvKmsKapiGetFunctionsTableInternal funcsTable->registerVblankIntrCallback = RegisterVblankIntrCallback; funcsTable->unregisterVblankIntrCallback = UnregisterVblankIntrCallback; + funcsTable->deviceNeedsRecovery = DeviceNeedsRecovery; + funcsTable->prepareForRecovery = PrepareForRecovery; return NV_TRUE; } diff --git a/src/nvidia-modeset/src/dp/nvdp-connector.cpp b/src/nvidia-modeset/src/dp/nvdp-connector.cpp index 61cb523b18..c8fc2e52c8 100644 --- a/src/nvidia-modeset/src/dp/nvdp-connector.cpp +++ b/src/nvidia-modeset/src/dp/nvdp-connector.cpp @@ -922,7 +922,9 @@ void nvDPPause(NVDPLibConnectorPtr pNVDpLibConnector) return; } - if (pDevEvo->skipConsoleRestore && pNVDpLibConnector->headMask != 0) { + if (pDevEvo->skipConsoleRestore && + !pDevEvo->skipConsoleRestoreOnTeardown && + pNVDpLibConnector->headMask != 0) { /* Clear vbios DisplayPort RAD scratch registers, see bug 200471345 */ nvAssert(nvPopCount32(pNVDpLibConnector->headMask) == 1); diff --git a/src/nvidia-modeset/src/nvkms-console-restore.c b/src/nvidia-modeset/src/nvkms-console-restore.c index 0c6cc5b296..2d58e80e2e 100644 --- a/src/nvidia-modeset/src/nvkms-console-restore.c +++ b/src/nvidia-modeset/src/nvkms-console-restore.c @@ -758,12 +758,22 @@ NvBool nvEvoRestoreConsole(NVDevEvoPtr pDevEvo, const NvBool allowMST) NvBool ret = FALSE; NvU32 dispIndex; NVDispEvoPtr pDispEvo; - const NVEvoApiHandlesRec *pOpenDevSurfaceHandles = + const NVEvoApiHandlesRec *pOpenDevSurfaceHandles; + NVSurfaceEvoPtr pSurfaceEvo; + struct NvKmsSetModeParams *params; + + if (pDevEvo->skipConsoleRestoreOnTeardown) { + pDevEvo->skipConsoleRestore = TRUE; + nvkms_free_timer(pDevEvo->consoleRestoreTimer); + pDevEvo->consoleRestoreTimer = NULL; + return TRUE; + } + + pOpenDevSurfaceHandles = nvGetSurfaceHandlesFromOpenDevConst(pDevEvo->pNvKmsOpenDev); - NVSurfaceEvoPtr pSurfaceEvo = + pSurfaceEvo = nvEvoGetPointerFromApiHandle(pOpenDevSurfaceHandles, pDevEvo->fbConsoleSurfaceHandle); - struct NvKmsSetModeParams *params; /* * If this function fails to restore a console then NVKMS frees diff --git a/src/nvidia-modeset/src/nvkms-difr.c b/src/nvidia-modeset/src/nvkms-difr.c index a0c6bd5586..bf423171a5 100644 --- a/src/nvidia-modeset/src/nvkms-difr.c +++ b/src/nvidia-modeset/src/nvkms-difr.c @@ -239,8 +239,10 @@ void nvDIFRFree(NVDIFRStateEvoPtr pDifr) /* Cancel pending idle timer. */ nvkms_free_timer(pDifr->idleTimer); - /* Leave DIFR enabled (default state). */ - SetDisabledState(pDifr, FALSE); + /* Leave DIFR enabled (default state) unless the GPU requires recovery. */ + if (!pDifr->pDevEvo->skipConsoleRestoreOnTeardown) { + SetDisabledState(pDifr, FALSE); + } /* Free resources. */ FreeDIFRCopyEngine(pDifr); diff --git a/src/nvidia-modeset/src/nvkms-evo.c b/src/nvidia-modeset/src/nvkms-evo.c index 82fa6fe719..0a41ee9ace 100644 --- a/src/nvidia-modeset/src/nvkms-evo.c +++ b/src/nvidia-modeset/src/nvkms-evo.c @@ -47,6 +47,7 @@ #include "nvkms-rmapi.h" #include "nvkms-surface.h" #include "nvkms-headsurface.h" +#include "nvkms-headsurface-config.h" #include "nvkms-difr.h" #include "nvkms-vrr.h" #include "nvkms-ioctl.h" @@ -5679,17 +5680,23 @@ void nvFreeCoreChannelEvo(NVDevEvoPtr pDevEvo) nvFreeUnixRmHandle(&pDevEvo->handleAllocator, pDevEvo->displayHandle); pDevEvo->displayHandle = 0; - if (!pDevEvo->skipConsoleRestore) { - nvRmVTSwitch(pDevEvo, - NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_RESTORE_VT_STATE); - } else { - nvRmVTSwitch(pDevEvo, - NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_CONSOLE_RESTORED); + if (!pDevEvo->skipConsoleRestoreOnTeardown) { + if (!pDevEvo->skipConsoleRestore) { + nvRmVTSwitch( + pDevEvo, + NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_RESTORE_VT_STATE); + } else { + nvRmVTSwitch( + pDevEvo, + NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_CONSOLE_RESTORED); + } } } // No longer possible that NVKMS is driving any displays, allow GC6. - nvRmSetGc6Allowed(pDevEvo, TRUE); + if (!pDevEvo->skipConsoleRestoreOnTeardown) { + nvRmSetGc6Allowed(pDevEvo, TRUE); + } nvFree(pDevEvo->gpus); pDevEvo->gpus = NULL; @@ -8808,6 +8815,10 @@ NvBool nvFreeDevEvo(NVDevEvoPtr pDevEvo) pDevEvo->fbConsoleSurfaceHandle = 0; } + if (pDevEvo->skipConsoleRestoreOnTeardown) { + nvHsConfigFreeDeviceResourcesForRecovery(pDevEvo); + } + nvFreeLutSurfacesEvo(pDevEvo); nvFreeCoreChannelEvo(pDevEvo); @@ -9913,4 +9924,3 @@ NvBool nvEvoIsConsoleActive(const NVDevEvoRec *pDevEvo) return FALSE; } - diff --git a/src/nvidia-modeset/src/nvkms-flip.c b/src/nvidia-modeset/src/nvkms-flip.c index 7322ab6719..d602b65dbc 100644 --- a/src/nvidia-modeset/src/nvkms-flip.c +++ b/src/nvidia-modeset/src/nvkms-flip.c @@ -1148,6 +1148,10 @@ void nvEvoClearSurfaceUsage(NVDevEvoRec *pDevEvo, { NvU32 head; + if (pDevEvo->skipConsoleRestoreOnTeardown) { + return; + } + /* * If the core channel is no longer allocated, we don't need to * clear usage/sync. This assumes the channels are allocated/deallocated diff --git a/src/nvidia-modeset/src/nvkms-headsurface-3d.c b/src/nvidia-modeset/src/nvkms-headsurface-3d.c index 5f8fd27490..ba5083790e 100644 --- a/src/nvidia-modeset/src/nvkms-headsurface-3d.c +++ b/src/nvidia-modeset/src/nvkms-headsurface-3d.c @@ -205,7 +205,9 @@ static void FreeNv3dChannel(NVHsChannelEvoRec *pHsChannel) pDispEvo = pHsChannel->pDispEvo; pDevEvo = pDispEvo->pDevEvo; - nv3dFreeChannelSurface(&pHsChannel->nv3d.channel); + nv3dFreeChannelSurface( + &pHsChannel->nv3d.channel, + pDevEvo->skipConsoleRestoreOnTeardown); nv3dFreeChannelObject(&pHsChannel->nv3d.channel); nv3dFreeChannelState(&pHsChannel->nv3d.channel); diff --git a/src/nvidia-modeset/src/nvkms-headsurface-config.c b/src/nvidia-modeset/src/nvkms-headsurface-config.c index 98cc3d44c6..939f1a17cd 100644 --- a/src/nvidia-modeset/src/nvkms-headsurface-config.c +++ b/src/nvidia-modeset/src/nvkms-headsurface-config.c @@ -1850,6 +1850,51 @@ static void HsConfigUpdateSurfaceRefCount( pDevEvo, pChannelConfig->cursor.pSurfaceEvo, increase); } +/* + * Free the active headSurface configuration without submitting work to a GPU + * that requires recovery. The normal nvHsConfigStop() path cannot be used + * here because it flips, idles channels, and waits for display progress. + */ +void nvHsConfigFreeDeviceResourcesForRecovery(NVDevEvoPtr pDevEvo) +{ + NVDispEvoPtr pDispEvo; + NvU32 apiHead, dispIndex; + + nvAssert(pDevEvo->skipConsoleRestoreOnTeardown); + + FOR_ALL_EVO_DISPLAYS(pDispEvo, dispIndex, pDevEvo) { + for (apiHead = 0; apiHead < pDevEvo->numApiHeads; apiHead++) { + NVHsChannelEvoPtr pHsChannel = pDispEvo->pHsChannel[apiHead]; + + if (pHsChannel == NULL) { + continue; + } + + if (pHsChannel->usingRgIntrForSwapGroups) { + nvHsRemoveRgLine1Callback(pHsChannel); + } + if (pHsChannel->vBlankCallback != NULL) { + nvHsRemoveVBlankCallback(pHsChannel); + } + + nvHsFreeStatistics(pHsChannel); + nvHsDrainFlipQueue(pHsChannel); + HsConfigUpdateSurfaceRefCount( + pDevEvo, &pHsChannel->config, FALSE /* increase */); + + nvHsFreeChannel(pHsChannel); + pDispEvo->pHsChannel[apiHead] = NULL; + } + } + + for (apiHead = 0; apiHead < pDevEvo->numApiHeads; apiHead++) { + HsConfigFreeHeadSurfaceSurfaces( + pDevEvo, + &pDevEvo->apiHeadSurfaceAllDisps[apiHead], + FALSE /* surfacesReused */); + } +} + /*! * Check if flipLock should be allowed on this device. * diff --git a/src/nvidia-modeset/src/nvkms-headsurface-swapgroup.c b/src/nvidia-modeset/src/nvkms-headsurface-swapgroup.c index 28243f39da..03fcdd49a8 100644 --- a/src/nvidia-modeset/src/nvkms-headsurface-swapgroup.c +++ b/src/nvidia-modeset/src/nvkms-headsurface-swapgroup.c @@ -720,7 +720,8 @@ void nvHsLeaveSwapGroup( * If the last member of the SwapGroup is leaving, change the "needed" state * of headSurface. */ - if (pSwapGroup->nMembers == 1) { + if (pSwapGroup->nMembers == 1 && + !pDevEvo->skipConsoleRestoreOnTeardown) { if (!HsSwapGroupUpdateHeadSurfaceNeeded(pDevEvo, pSwapGroup, FALSE)) { nvAssert(!"Failed to transition out of headSurface"); /* XXX NVKMS HEADSURFACE TODO: we need to do something here... */ @@ -757,7 +758,8 @@ void nvHsLeaveSwapGroup( * group kicking off a flip and a subsequent vblank or headsurface * transition releasing the swapgroup. */ - if (!teardown && !removingReadyFifo && (pSwapGroup->nMembers != 0)) { + if (!teardown && !pDevEvo->skipConsoleRestoreOnTeardown && + !removingReadyFifo && (pSwapGroup->nMembers != 0)) { if (SwapGroupIsReady(pSwapGroup)) { FlipSwapGroup(pDevEvo, pSwapGroup); } diff --git a/src/nvidia-modeset/src/nvkms-headsurface.c b/src/nvidia-modeset/src/nvkms-headsurface.c index 20795a9f7a..3cbb5cc5ab 100644 --- a/src/nvidia-modeset/src/nvkms-headsurface.c +++ b/src/nvidia-modeset/src/nvkms-headsurface.c @@ -591,7 +591,8 @@ static void HsReleaseFlipQueueEntry( /* * If a semaphore surface was specified, we can now write its release value. */ - if (!pFlipState->syncObject.usingSyncpt && + if (!pDevEvo->skipConsoleRestoreOnTeardown && + !pFlipState->syncObject.usingSyncpt && pFlipState->syncObject.u.semaphores.releaseSurface.pSurfaceEvo != NULL) { /* diff --git a/src/nvidia-modeset/src/nvkms-lut.c b/src/nvidia-modeset/src/nvkms-lut.c index 302b71873c..2725594216 100644 --- a/src/nvidia-modeset/src/nvkms-lut.c +++ b/src/nvidia-modeset/src/nvkms-lut.c @@ -392,7 +392,7 @@ void nvFreeLutSurfacesEvo(NVDevEvoPtr pDevEvo) } /* wait for any outstanding LUT updates before freeing the surface */ - if (pDevEvo->core) { + if (pDevEvo->core && !pDevEvo->skipConsoleRestoreOnTeardown) { nvRMSyncEvoChannel(pDevEvo, pDevEvo->core, __LINE__); } diff --git a/src/nvidia-modeset/src/nvkms-rm.c b/src/nvidia-modeset/src/nvkms-rm.c index eea1fce509..439a7fb671 100644 --- a/src/nvidia-modeset/src/nvkms-rm.c +++ b/src/nvidia-modeset/src/nvkms-rm.c @@ -2037,7 +2037,8 @@ void nvRmDestroyDisplays(NVDevEvoPtr pDevEvo) const NvU32 subDevice = pDevEvo->pSubDevices[pDispEvo->displayOwner]->handle; // Before freeing anything, dump anything left in the RM's DisplayPort // AUX channel log. - if (pDispEvo->dpAuxLoggingEnabled) { + if (pDispEvo->dpAuxLoggingEnabled && + !pDevEvo->skipConsoleRestoreOnTeardown) { do { ret = nvRmQueryDpAuxLog(pDispEvo, &tmp); } while (ret && tmp); @@ -2050,12 +2051,13 @@ void nvRmDestroyDisplays(NVDevEvoPtr pDevEvo) // Disable DP-IRQ notifications for this subdevice setEventParams.event = NV2080_NOTIFIERS_DP_IRQ; setEventParams.action = NV2080_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE; - if ((ret = nvRmApiControl(nvEvoGlobal.clientHandle, - subDevice, - NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, - &setEventParams, - sizeof(setEventParams)) - != NVOS_STATUS_SUCCESS)) { + if (!pDevEvo->skipConsoleRestoreOnTeardown && + ((ret = nvRmApiControl(nvEvoGlobal.clientHandle, + subDevice, + NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, + &setEventParams, + sizeof(setEventParams))) != + NVOS_STATUS_SUCCESS)) { nvEvoLogDev(pDevEvo, EVO_LOG_WARN, "Failed to disable hotplug notifications (subdevice: %d) (error: 0x%x)", dispIndex, ret); } @@ -2075,12 +2077,13 @@ void nvRmDestroyDisplays(NVDevEvoPtr pDevEvo) // Disable hotplug notifications for this subdevice setEventParams.event = NV2080_NOTIFIERS_HOTPLUG; setEventParams.action = NV2080_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE; - if ((ret = nvRmApiControl(nvEvoGlobal.clientHandle, - subDevice, - NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, - &setEventParams, - sizeof(setEventParams)) - != NVOS_STATUS_SUCCESS)) { + if (!pDevEvo->skipConsoleRestoreOnTeardown && + ((ret = nvRmApiControl(nvEvoGlobal.clientHandle, + subDevice, + NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, + &setEventParams, + sizeof(setEventParams))) != + NVOS_STATUS_SUCCESS)) { nvEvoLogDev(pDevEvo, EVO_LOG_WARN, "Failed to disable hotplug notifications (subdevice: %d) (error: 0x%x)", dispIndex, ret); } @@ -2100,12 +2103,13 @@ void nvRmDestroyDisplays(NVDevEvoPtr pDevEvo) // Disable HDMI FRL retrain notifications for this subdevice setEventParams.event = NV2080_NOTIFIERS_HDMI_FRL_RETRAINING_REQUEST; setEventParams.action = NV2080_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE; - if ((ret = nvRmApiControl(nvEvoGlobal.clientHandle, - subDevice, - NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, - &setEventParams, - sizeof(setEventParams)) - != NVOS_STATUS_SUCCESS)) { + if (!pDevEvo->skipConsoleRestoreOnTeardown && + ((ret = nvRmApiControl(nvEvoGlobal.clientHandle, + subDevice, + NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, + &setEventParams, + sizeof(setEventParams))) != + NVOS_STATUS_SUCCESS)) { nvEvoLogDev(pDevEvo, EVO_LOG_WARN, "Failed to disable HDMI FRL retrain notifications (subdevice: %d) (error: 0x%x)", dispIndex, ret); } @@ -4156,11 +4160,13 @@ static void UnregisterNonStallInterruptCallback(NVDevEvoPtr pDevEvo) eventNotificationParams.event = NV2080_NOTIFIERS_FIFO_EVENT_MTHD; eventNotificationParams.action = NV2080_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE; - nvRmApiControl(nvEvoGlobal.clientHandle, - pDevEvo->pSubDevices[0]->handle, - NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, - &eventNotificationParams, - sizeof(eventNotificationParams)); + if (!pDevEvo->skipConsoleRestoreOnTeardown) { + nvRmApiControl(nvEvoGlobal.clientHandle, + pDevEvo->pSubDevices[0]->handle, + NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, + &eventNotificationParams, + sizeof(eventNotificationParams)); + } nvRmApiFree(nvEvoGlobal.clientHandle, pDevEvo->pSubDevices[0]->handle, @@ -4476,11 +4482,13 @@ void nvRmUnregisterDIFREventHandler(NVDevEvoPtr pDevEvo) prefetchEventParams.event = NV2080_NOTIFIERS_LPWR_DIFR_PREFETCH_REQUEST; prefetchEventParams.action = NV2080_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE; - nvRmApiControl(nvEvoGlobal.clientHandle, - pDevEvo->pSubDevices[0]->handle, - NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, - &prefetchEventParams, - sizeof(prefetchEventParams)); + if (!pDevEvo->skipConsoleRestoreOnTeardown) { + nvRmApiControl(nvEvoGlobal.clientHandle, + pDevEvo->pSubDevices[0]->handle, + NV2080_CTRL_CMD_EVENT_SET_NOTIFICATION, + &prefetchEventParams, + sizeof(prefetchEventParams)); + } nvRmApiFree(nvEvoGlobal.clientHandle, pDevEvo->pSubDevices[0]->handle, @@ -5391,11 +5399,13 @@ void nvRmUnregisterRgInterruptCallback(NVDispEvoPtr pDispEvo) params.event = NVC370_NOTIFIERS_RG_SEM_NOTIFICATION; params.action = NVC370_CTRL_EVENT_SET_NOTIFICATION_ACTION_DISABLE; - ret = nvRmApiControl(nvEvoGlobal.clientHandle, - pDevEvo->displayHandle, - NVC370_CTRL_CMD_EVENT_SET_NOTIFICATION, - ¶ms, - sizeof(params)); + ret = pDevEvo->skipConsoleRestoreOnTeardown ? + NVOS_STATUS_SUCCESS : + nvRmApiControl(nvEvoGlobal.clientHandle, + pDevEvo->displayHandle, + NVC370_CTRL_CMD_EVENT_SET_NOTIFICATION, + ¶ms, + sizeof(params)); if (ret != NVOS_STATUS_SUCCESS) { /* diff --git a/src/nvidia-modeset/src/nvkms-surface.c b/src/nvidia-modeset/src/nvkms-surface.c index f6fa4a9e80..2a9aa33a5b 100644 --- a/src/nvidia-modeset/src/nvkms-surface.c +++ b/src/nvidia-modeset/src/nvkms-surface.c @@ -833,10 +833,14 @@ ClearSurfaceUsageApply(NVDevEvoPtr pDevEvo, const NVDispEvoRec *pDispEvo = pDevEvo->pDispEvo[0]; NvU32 apiHead; const NvU32 maxApiHeads = pDevEvo->numApiHeads * pDevEvo->numSubDevices; - struct NvKmsFlipRequestOneHead *pFlipApiHead = - nvCalloc(1, sizeof(*pFlipApiHead) * maxApiHeads); + struct NvKmsFlipRequestOneHead *pFlipApiHead; NvU32 numFlipApiHeads = 0; + if (pDevEvo->skipConsoleRestoreOnTeardown) { + return; + } + + pFlipApiHead = nvCalloc(1, sizeof(*pFlipApiHead) * maxApiHeads); if (pFlipApiHead == NULL) { nvAssert(!"Failed to allocate memory"); return; @@ -1170,6 +1174,10 @@ void nvEvoDecrementSurfaceRefCntsWithSync(NVDevEvoPtr pDevEvo, NVSurfaceEvoPtr pSurfaceEvo, NvBool skipSync) { + if (pDevEvo->skipConsoleRestoreOnTeardown) { + skipSync = TRUE; + } + nvAssert(pSurfaceEvo->rmRefCnt >= 1); pSurfaceEvo->rmRefCnt--; diff --git a/src/nvidia-modeset/src/nvkms.c b/src/nvidia-modeset/src/nvkms.c index 133c38ea3d..6cc5858a04 100644 --- a/src/nvidia-modeset/src/nvkms.c +++ b/src/nvidia-modeset/src/nvkms.c @@ -1141,9 +1141,12 @@ static NvBool ReleaseModesetOwnership(struct NvKmsPerOpenDev *pOpenDev) pDevEvo->modesetOwner = NULL; pDevEvo->modesetOwnerOrSubOwnerChanged = TRUE; - pDevEvo->handleConsoleHotplugs = TRUE; + pDevEvo->handleConsoleHotplugs = + !pDevEvo->skipConsoleRestoreOnTeardown; - RestoreConsole(pDevEvo); + if (!pDevEvo->skipConsoleRestoreOnTeardown) { + RestoreConsole(pDevEvo); + } RevokePermissionsInternal(NVBIT(NV_KMS_PERMISSIONS_TYPE_FLIPPING) | NVBIT(NV_KMS_PERMISSIONS_TYPE_MODESET) | NVBIT(NV_KMS_PERMISSIONS_TYPE_SUB_OWNER), @@ -4499,7 +4502,8 @@ static void DisableAndCleanVblankSyncObject(NVDispEvoRec *pDispEvo, NVVblankSyncObjectRec *pVblankSyncObject, NVEvoUpdateState *pUpdateState) { - if (nvApiHeadIsActive(pDispEvo, apiHead)) { + if (!pDispEvo->pDevEvo->skipConsoleRestoreOnTeardown && + nvApiHeadIsActive(pDispEvo, apiHead)) { NvU32 head = nvGetPrimaryHwHead(pDispEvo, apiHead); nvAssert(head != NV_INVALID_HEAD); @@ -4946,6 +4950,35 @@ static NvBool FramebufferConsoleDisabled( return TRUE; } +static NvBool PrepareForRecovery( + struct NvKmsPerOpen *pOpen, + void *pParamsVoid) +{ + const struct NvKmsPrepareForRecoveryParams *pParams = pParamsVoid; + struct NvKmsPerOpenDev *pOpenDev = + GetPerOpenDev(pOpen, pParams->request.deviceHandle); + NVDevEvoPtr pDevEvo; + + if (pOpenDev == NULL || + pOpen->clientType != NVKMS_CLIENT_KERNEL_SPACE) { + return FALSE; + } + + pDevEvo = pOpenDev->pDevEvo; + pDevEvo->skipConsoleRestoreOnTeardown = TRUE; + pDevEvo->skipConsoleRestore = TRUE; + pDevEvo->handleConsoleHotplugs = FALSE; + + nvkms_free_timer(pDevEvo->consoleRestoreTimer); + pDevEvo->consoleRestoreTimer = NULL; + + if (pDevEvo->modesetOwner == pOpenDev) { + return ReleaseModesetOwnership(pOpenDev); + } + + return TRUE; +} + static NvBool RegisterVblankIntrCallback(struct NvKmsPerOpen *pOpen, void *pParamsVoid) { @@ -5160,6 +5193,7 @@ NvBool nvKmsIoctl( ENTRY(NVKMS_IOCTL_FRAMEBUFFER_CONSOLE_DISABLED, FramebufferConsoleDisabled), ENTRY(NVKMS_IOCTL_REGISTER_VBLANK_INTR_CALLBACK, RegisterVblankIntrCallback), ENTRY(NVKMS_IOCTL_UNREGISTER_VBLANK_INTR_CALLBACK, UnregisterVblankIntrCallback), + ENTRY(NVKMS_IOCTL_PREPARE_FOR_RECOVERY, PrepareForRecovery), }; struct NvKmsPerOpen *pOpen = pOpenVoid;