drm/i915: Take a reference whilst processing the signaler request
authorChris Wilson <chris@chris-wilson.co.uk>
Thu, 23 Feb 2017 07:44:15 +0000 (07:44 +0000)
committerChris Wilson <chris@chris-wilson.co.uk>
Thu, 23 Feb 2017 14:49:34 +0000 (14:49 +0000)
The plan in the near-future is to allow requests to be removed from the
signaler. We can no longer then rely on holding a reference to the
request for the duration it is in the signaling tree, and instead must
obtain a reference to the request for the current operation using RCU.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170223074422.4125-10-chris@chris-wilson.co.uk
drivers/gpu/drm/i915/intel_breadcrumbs.c
drivers/gpu/drm/i915/intel_ringbuffer.h

index d5bf4c0b2debb63750cdb0545724a67bb2af7fa6..a35d59d00bfba5db13fb8b6f2449076760aebb2a 100644 (file)
@@ -496,7 +496,11 @@ static int intel_breadcrumbs_signaler(void *arg)
                 * need to wait for a new interrupt from the GPU or for
                 * a new client.
                 */
-               request = READ_ONCE(b->first_signal);
+               rcu_read_lock();
+               request = rcu_dereference(b->first_signal);
+               if (request)
+                       request = i915_gem_request_get_rcu(request);
+               rcu_read_unlock();
                if (signal_complete(request)) {
                        local_bh_disable();
                        dma_fence_signal(&request->fence);
@@ -515,24 +519,28 @@ static int intel_breadcrumbs_signaler(void *arg)
                         * the oldest before picking the next one.
                         */
                        spin_lock_irq(&b->lock);
-                       if (request == b->first_signal) {
+                       if (request == rcu_access_pointer(b->first_signal)) {
                                struct rb_node *rb =
                                        rb_next(&request->signaling.node);
-                               b->first_signal = rb ? to_signaler(rb) : NULL;
+                               rcu_assign_pointer(b->first_signal,
+                                                  rb ? to_signaler(rb) : NULL);
                        }
                        rb_erase(&request->signaling.node, &b->signals);
                        spin_unlock_irq(&b->lock);
 
                        i915_gem_request_put(request);
                } else {
-                       if (kthread_should_stop())
+                       if (kthread_should_stop()) {
+                               GEM_BUG_ON(request);
                                break;
+                       }
 
                        schedule();
 
                        if (kthread_should_park())
                                kthread_parkme();
                }
+               i915_gem_request_put(request);
        } while (1);
        __set_current_state(TASK_RUNNING);
 
@@ -597,7 +605,7 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request)
        rb_link_node(&request->signaling.node, parent, p);
        rb_insert_color(&request->signaling.node, &b->signals);
        if (first)
-               smp_store_mb(b->first_signal, request);
+               rcu_assign_pointer(b->first_signal, request);
 
        spin_unlock(&b->lock);
 
@@ -670,7 +678,7 @@ void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine)
        /* The engines should be idle and all requests accounted for! */
        WARN_ON(READ_ONCE(b->first_wait));
        WARN_ON(!RB_EMPTY_ROOT(&b->waiters));
-       WARN_ON(READ_ONCE(b->first_signal));
+       WARN_ON(rcu_access_pointer(b->first_signal));
        WARN_ON(!RB_EMPTY_ROOT(&b->signals));
 
        if (!IS_ERR_OR_NULL(b->signaler))
@@ -691,7 +699,7 @@ bool intel_breadcrumbs_busy(struct intel_engine_cs *engine)
                busy |= intel_engine_flag(engine);
        }
 
-       if (b->first_signal) {
+       if (rcu_access_pointer(b->first_signal)) {
                wake_up_process(b->signaler);
                busy |= intel_engine_flag(engine);
        }
index 04e7d5a9ee3ab791060e4755f746dcb4a3a0d2a6..fbf34af7bc77f7f325693f90827d029550d321e7 100644 (file)
@@ -242,7 +242,7 @@ struct intel_engine_cs {
                struct rb_root signals; /* sorted by retirement */
                struct intel_wait *first_wait; /* oldest waiter by retirement */
                struct task_struct *signaler; /* used for fence signalling */
-               struct drm_i915_gem_request *first_signal;
+               struct drm_i915_gem_request __rcu *first_signal;
                struct timer_list fake_irq; /* used after a missed interrupt */
                struct timer_list hangcheck; /* detect missed interrupts */