[PATCH 1/4] drm/msm: don't wait for a fault devcoredump during GPU recovery
From: Nico Angelo Dela Cruz
Date: Sun Nov 01 2026 - 13:01:13 EST
adreno_fault_handler() calls reinit_completion() on
fault_coredump_done and then blocks in
msm_gpu_fault_crashstate_capture() on mutex_lock(&gpu->lock) to take
the devcoredump.
If recover_worker() is already running, it holds gpu->lock and, from
the runtime suspend in a6xx_recover() or a8xx_recover(), may time out
in a6xx_hfi_wait_for_msg_interrupt() or a6xx_gmu_set_oob(). Both loops
then fall into an unbounded wait_for_completion() on
fault_coredump_done, which has just been reinitialized and which only
the fault handler can complete. The fault handler is waiting for
gpu->lock, which recovery will not release until the wait returns, so
neither side makes progress.
A fault dump that needs gpu->lock can never complete while recovery
holds it, so waiting for it is wrong. Add a recovery_in_progress flag
to struct msm_gpu, set by recover_worker() under gpu->lock, and check
it in two places:
- the fault handler doesn't start a devcoredump capture while
recovery is running, so a fault that arrives during recovery no
longer reinitializes the completion;
- both GMU wait loops stop waiting for the completion while recovery
is running and fall through to their normal timeout handling. This
covers a fault handler that had already reinitialized the
completion when recovery took gpu->lock.
GMU waiters that don't hold gpu->lock, such as the devfreq frequency
change path, also skip the completion wait while recovery is running
and report their timeout right away; recovery is resetting the GMU
at that point anyway.
On a OnePlus Pad 3 (Adreno 830), recoveries that saw a CP page fault
within 2 s of "hangcheck recover!" were stuck 3 of 3 times, while
12 of 12 recoveries without such a fault completed. With this patch,
three natural recoveries that took a CP page fault during recovery
all completed.
Fixes: 50a0b122cfc8 ("drm/msm: Wait for MMU devcoredump when waiting for GMU")
Cc: stable@xxxxxxxxxxxxxxx
Link: https://lore.kernel.org/all/CACu1E7EApr6Ne3XxFv4cUa2JuwNW2kfo1c5wSsAUN78iWyQ2sg@xxxxxxxxxxxxxx/
Signed-off-by: Nico Angelo Dela Cruz <nicoangelodelacruz@xxxxxxxxx>
---
drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 7 +++++--
drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 7 +++++--
drivers/gpu/drm/msm/adreno/adreno_gpu.c | 3 ++-
drivers/gpu/drm/msm/msm_gpu.c | 2 ++
drivers/gpu/drm/msm/msm_gpu.h | 9 +++++++++
5 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index 662f09bec..32ed49abe 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -445,12 +445,15 @@ int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
if (!ret)
break;
- if (completion_done(&a6xx_gpu->base.fault_coredump_done))
+ if (completion_done(&a6xx_gpu->base.fault_coredump_done) ||
+ READ_ONCE(a6xx_gpu->base.base.recovery_in_progress))
break;
/* We may timeout because the GMU is temporarily wedged from
* pending faults from the GPU and we are taking a devcoredump.
- * Wait until the MMU is resumed and try again.
+ * Wait until the MMU is resumed and try again. A fault dump
+ * cannot complete while recovery holds gpu->lock, so don't
+ * wait for one then.
*/
wait_for_completion(&a6xx_gpu->base.fault_coredump_done);
} while (true);
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
index 6b34e704a..fe4e35728 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
@@ -122,12 +122,15 @@ static int a6xx_hfi_wait_for_msg_interrupt(struct a6xx_gmu *gmu, u32 id, u32 seq
if (!ret)
break;
- if (completion_done(&a6xx_gpu->base.fault_coredump_done))
+ if (completion_done(&a6xx_gpu->base.fault_coredump_done) ||
+ READ_ONCE(a6xx_gpu->base.base.recovery_in_progress))
break;
/* We may timeout because the GMU is temporarily wedged from
* pending faults from the GPU and we are taking a devcoredump.
- * Wait until the MMU is resumed and try again.
+ * Wait until the MMU is resumed and try again. A fault dump
+ * cannot complete while recovery holds gpu->lock, so don't
+ * wait for one then.
*/
wait_for_completion(&a6xx_gpu->base.fault_coredump_done);
} while (true);
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index dadfe4c69..138bebf5e 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -289,7 +289,8 @@ int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags,
struct msm_mmu *mmu = to_msm_vm(gpu->vm)->mmu;
const char *type = "UNKNOWN";
bool do_devcoredump = info && (info->fsr & ARM_SMMU_FSR_SS) &&
- !READ_ONCE(gpu->crashstate);
+ !READ_ONCE(gpu->crashstate) &&
+ !READ_ONCE(gpu->recovery_in_progress);
unsigned long irq_flags;
/*
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 0c2c35636..caa86279f 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -474,6 +474,7 @@ static void recover_worker(struct kthread_work *work)
int i;
mutex_lock(&gpu->lock);
+ WRITE_ONCE(gpu->recovery_in_progress, true);
DRM_DEV_ERROR(dev->dev, "%s: hangcheck recover!\n", gpu->name);
@@ -585,6 +586,7 @@ static void recover_worker(struct kthread_work *work)
pm_runtime_put(&gpu->pdev->dev);
out_unlock:
+ WRITE_ONCE(gpu->recovery_in_progress, false);
mutex_unlock(&gpu->lock);
msm_gpu_retire(gpu);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index d27d54bdb..5cf220f75 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -258,6 +258,15 @@ struct msm_gpu {
struct msm_gpu_state *crashstate;
+ /**
+ * @recovery_in_progress: true while recover_worker() holds @lock, so
+ * that fault capture and GMU waits don't wait on a fault devcoredump
+ * that can't complete while recovery holds @lock. Written with
+ * WRITE_ONCE() under @lock; readers (the fault handler, GMU wait
+ * loops) don't hold @lock and use READ_ONCE().
+ */
+ bool recovery_in_progress;
+
/* True if the hardware supports expanded apriv (a650 and newer) */
bool hw_apriv;
--
2.53.0