The final intel_context_put() may drop the last reference to a signaler
context. GuC parallel child contexts are freed immediately by
__guc_context_destroy() rather than via call_rcu(), so reading
ce->signal_link.next to advance the RCU iterator afterwards is a
use-after-free. Cache the next context before the loop body instead.

Fixes: c744d50363b7 ("drm/i915/gt: Split the breadcrumb spinlock between global 
and contexts")
Cc: [email protected]
Signed-off-by: Wentao Liang <[email protected]>
---
 drivers/gpu/drm/i915/gt/intel_breadcrumbs.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c 
b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
index c10ac0ab3bfa..f840e98792cd 100644
--- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
@@ -174,7 +174,7 @@ static void signal_irq_work(struct irq_work *work)
        struct intel_breadcrumbs *b = container_of(work, typeof(*b), irq_work);
        const ktime_t timestamp = ktime_get();
        struct llist_node *signal, *sn;
-       struct intel_context *ce;
+       struct intel_context *ce, *cn;
 
        signal = NULL;
        if (unlikely(!llist_empty(&b->signaled_requests)))
@@ -210,9 +210,19 @@ static void signal_irq_work(struct irq_work *work)
 
        rcu_read_lock();
        atomic_inc(&b->signaler_active);
-       list_for_each_entry_rcu(ce, &b->signalers, signal_link) {
+       for (ce = list_first_or_null_rcu(&b->signalers, typeof(*ce), 
signal_link);
+            ce;
+            ce = cn) {
                struct i915_request *rq;
 
+               /*
+                * Grab the next signaler up front, as dropping the final
+                * reference below may free the current one before we
+                * advance the iterator.
+                */
+               cn = list_next_or_null_rcu(&b->signalers, &ce->signal_link,
+                                          typeof(*cn), signal_link);
+
                list_for_each_entry_rcu(rq, &ce->signals, signal_link) {
                        bool release;
 
-- 
2.34.1

Reply via email to