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

Reply via email to