Commit 5f08eee9c3fb ("drm/radeon: restore hardware polling in
fence_is_signaled to fix performance regression") restored hardware
polling by calling radeon_fence_activity() inside the dma-fence
.signaled callback.During the review of that patch, the automated kernel reviewer flagged a [High] severity issue: radeon_fence_activity() in the .signaled callback consumes hardware state without waking up wait queues. Because last_seq is advanced there, a later interrupt-driven radeon_fence_process() sees no new activity (seq <= last_seq), returns false and skips wake_up_all(). This steals the wake event and can leave tasks waiting on fences hanging indefinitely. Reproduced on Radeon HD 5450 (Cedar), where interrupts can be missed under heavy 2D redraw load: the screen freezes while the cursor keeps moving, and recovery requires new GPU work (e.g. glxgears) or a modeset to trigger a fresh interrupt. Fix this by scheduling a deferred work item when radeon_fence_activity() reports new completions inside radeon_fence_is_signaled(). The worker calls wake_up_all() unconditionally from process context, which restores the stolen wake event without re-introducing the self-deadlock on the fence lock, and without rw_semaphore manipulation in atomic context. schedule_work() is idempotent, so repeated polling is cheap. Assisted-by: LLM Link: https://gitlab.freedesktop.org/drm/amd/-/issues/5955 Fixes: 5f08eee9c3fb ("drm/radeon: restore hardware polling in fence_is_signaled to fix performance regression") Signed-off-by: Bogdan Metla <[email protected]> --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_fence.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 527b9d19d7..61b01df87f 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -370,6 +370,7 @@ struct radeon_fence_driver { atomic64_t last_seq; bool initialized, delayed_irq; struct delayed_work lockup_work; + struct work_struct wake_work; }; struct radeon_fence { diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 2998655692..5016dd0cbc 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -155,6 +155,15 @@ int radeon_fence_emit(struct radeon_device *rdev, return 0; } +static void radeon_fence_wake_work(struct work_struct *work) +{ + struct radeon_fence_driver *fence_drv = + container_of(work, struct radeon_fence_driver, wake_work); + struct radeon_device *rdev = fence_drv->rdev; + + wake_up_all(&rdev->fence_queue); +} + /* * radeon_fence_check_signaled - callback from fence_queue * @@ -361,7 +370,8 @@ static bool radeon_fence_is_signaled(struct dma_fence *f) return true; if (down_read_trylock(&rdev->exclusive_lock)) { - radeon_fence_activity(rdev, ring); + if (radeon_fence_activity(rdev, ring)) + schedule_work(&rdev->fence_drv[ring].wake_work); up_read(&rdev->exclusive_lock); if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) @@ -826,6 +836,7 @@ static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring) rdev->fence_drv[ring].initialized = false; INIT_DELAYED_WORK(&rdev->fence_drv[ring].lockup_work, radeon_fence_check_lockup); + INIT_WORK(&rdev->fence_drv[ring].wake_work, radeon_fence_wake_work); rdev->fence_drv[ring].rdev = rdev; } @@ -873,6 +884,7 @@ void radeon_fence_driver_fini(struct radeon_device *rdev) radeon_fence_driver_force_completion(rdev, ring); } cancel_delayed_work_sync(&rdev->fence_drv[ring].lockup_work); + cancel_work_sync(&rdev->fence_drv[ring].wake_work); wake_up_all(&rdev->fence_queue); radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); rdev->fence_drv[ring].initialized = false; -- 2.53.0
