]> git.baikalelectronics.ru Git - kernel.git/commitdiff
drm/i915: Use a ctor for TYPESAFE_BY_RCU i915_request
authorChris Wilson <chris@chris-wilson.co.uk>
Fri, 22 Nov 2019 09:49:24 +0000 (09:49 +0000)
committerChris Wilson <chris@chris-wilson.co.uk>
Fri, 22 Nov 2019 10:47:38 +0000 (10:47 +0000)
As we start peeking into requests for longer and longer, e.g.
incorporating use of spinlocks when only protected by an
rcu_read_lock(), we need to be careful in how we reset the request when
recycling and need to preserve any barriers that may still be in use as
the request is reset for reuse.

Quoting Linus Torvalds:

> If there is refcounting going on then why use SLAB_TYPESAFE_BY_RCU?

  .. because the object can be accessed (by RCU) after the refcount has
  gone down to zero, and the thing has been released.

  That's the whole and only point of SLAB_TYPESAFE_BY_RCU.

  That flag basically says:

  "I may end up accessing this object *after* it has been free'd,
  because there may be RCU lookups in flight"

  This has nothing to do with constructors. It's ok if the object gets
  reused as an object of the same type and does *not* get
  re-initialized, because we're perfectly fine seeing old stale data.

  What it guarantees is that the slab isn't shared with any other kind
  of object, _and_ that the underlying pages are free'd after an RCU
  quiescent period (so the pages aren't shared with another kind of
  object either during an RCU walk).

  And it doesn't necessarily have to have a constructor, because the
  thing that a RCU walk will care about is

    (a) guaranteed to be an object that *has* been on some RCU list (so
    it's not a "new" object)

    (b) the RCU walk needs to have logic to verify that it's still the
    *same* object and hasn't been re-used as something else.

  In contrast, a SLAB_TYPESAFE_BY_RCU memory gets free'd and re-used
  immediately, but because it gets reused as the same kind of object,
  the RCU walker can "know" what parts have meaning for re-use, in a way
  it couidn't if the re-use was random.

  That said, it *is* subtle, and people should be careful.

> So the re-use might initialize the fields lazily, not necessarily using a ctor.

  If you have a well-defined refcount, and use "atomic_inc_not_zero()"
  to guard the speculative RCU access section, and use
  "atomic_dec_and_test()" in the freeing section, then you should be
  safe wrt new allocations.

  If you have a completely new allocation that has "random stale
  content", you know that it cannot be on the RCU list, so there is no
  speculative access that can ever see that random content.

  So the only case you need to worry about is a re-use allocation, and
  you know that the refcount will start out as zero even if you don't
  have a constructor.

  So you can think of the refcount itself as always having a zero
  constructor, *BUT* you need to be careful with ordering.

  In particular, whoever does the allocation needs to then set the
  refcount to a non-zero value *after* it has initialized all the other
  fields. And in particular, it needs to make sure that it uses the
  proper memory ordering to do so.

  NOTE! One thing to be very worried about is that re-initializing
  whatever RCU lists means that now the RCU walker may be walking on the
  wrong list so the walker may do the right thing for this particular
  entry, but it may miss walking *other* entries. So then you can get
  spurious lookup failures, because the RCU walker never walked all the
  way to the end of the right list. That ends up being a much more
  subtle bug.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191122094924.629690-1-chris@chris-wilson.co.uk
drivers/gpu/drm/i915/i915_request.c
drivers/gpu/drm/i915/i915_scheduler.c
drivers/gpu/drm/i915/i915_scheduler.h
drivers/gpu/drm/i915/i915_sw_fence.c
drivers/gpu/drm/i915/i915_sw_fence.h

index 00011f9533b6638d1d2a9bcda0502817942915bb..a558f64186fa6c67b2cc613a775902015e6d51d7 100644 (file)
@@ -188,7 +188,7 @@ static void free_capture_list(struct i915_request *request)
 {
        struct i915_capture_list *capture;
 
-       capture = request->capture_list;
+       capture = fetch_and_zero(&request->capture_list);
        while (capture) {
                struct i915_capture_list *next = capture->next;
 
@@ -214,7 +214,7 @@ static void remove_from_engine(struct i915_request *rq)
                spin_lock(&engine->active.lock);
                locked = engine;
        }
-       list_del(&rq->sched.link);
+       list_del_init(&rq->sched.link);
        spin_unlock_irq(&locked->active.lock);
 }
 
@@ -586,6 +586,21 @@ out:
        return kmem_cache_alloc(global.slab_requests, gfp);
 }
 
+static void __i915_request_ctor(void *arg)
+{
+       struct i915_request *rq = arg;
+
+       spin_lock_init(&rq->lock);
+       i915_sched_node_init(&rq->sched);
+       i915_sw_fence_init(&rq->submit, submit_notify);
+       i915_sw_fence_init(&rq->semaphore, semaphore_notify);
+
+       rq->file_priv = NULL;
+       rq->capture_list = NULL;
+
+       INIT_LIST_HEAD(&rq->execute_cb);
+}
+
 struct i915_request *
 __i915_request_create(struct intel_context *ce, gfp_t gfp)
 {
@@ -648,6 +663,7 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp)
        rq->engine = ce->engine;
        rq->ring = ce->ring;
        rq->execution_mask = ce->engine->mask;
+       rq->flags = 0;
 
        rcu_assign_pointer(rq->timeline, tl);
        rq->hwsp_seqno = tl->hwsp_seqno;
@@ -655,23 +671,20 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp)
 
        rq->rcustate = get_state_synchronize_rcu(); /* acts as smp_mb() */
 
-       spin_lock_init(&rq->lock);
        dma_fence_init(&rq->fence, &i915_fence_ops, &rq->lock,
                       tl->fence_context, seqno);
 
        /* We bump the ref for the fence chain */
-       i915_sw_fence_init(&i915_request_get(rq)->submit, submit_notify);
-       i915_sw_fence_init(&i915_request_get(rq)->semaphore, semaphore_notify);
+       i915_sw_fence_reinit(&i915_request_get(rq)->submit);
+       i915_sw_fence_reinit(&i915_request_get(rq)->semaphore);
 
-       i915_sched_node_init(&rq->sched);
+       i915_sched_node_reinit(&rq->sched);
 
-       /* No zalloc, must clear what we need by hand */
-       rq->file_priv = NULL;
+       /* No zalloc, everything must be cleared after use */
        rq->batch = NULL;
-       rq->capture_list = NULL;
-       rq->flags = 0;
-
-       INIT_LIST_HEAD(&rq->execute_cb);
+       GEM_BUG_ON(rq->file_priv);
+       GEM_BUG_ON(rq->capture_list);
+       GEM_BUG_ON(!list_empty(&rq->execute_cb));
 
        /*
         * Reserve space in the ring buffer for all the commands required to
@@ -1533,10 +1546,14 @@ static struct i915_global_request global = { {
 
 int __init i915_global_request_init(void)
 {
-       global.slab_requests = KMEM_CACHE(i915_request,
-                                         SLAB_HWCACHE_ALIGN |
-                                         SLAB_RECLAIM_ACCOUNT |
-                                         SLAB_TYPESAFE_BY_RCU);
+       global.slab_requests =
+               kmem_cache_create("i915_request",
+                                 sizeof(struct i915_request),
+                                 __alignof__(struct i915_request),
+                                 SLAB_HWCACHE_ALIGN |
+                                 SLAB_RECLAIM_ACCOUNT |
+                                 SLAB_TYPESAFE_BY_RCU,
+                                 __i915_request_ctor);
        if (!global.slab_requests)
                return -ENOMEM;
 
index 010d67f48ad9448f96887a22c9bb81710d976304..1937a26d412ffb125746d84c14dd75230f5468f5 100644 (file)
@@ -387,9 +387,19 @@ void i915_sched_node_init(struct i915_sched_node *node)
        INIT_LIST_HEAD(&node->signalers_list);
        INIT_LIST_HEAD(&node->waiters_list);
        INIT_LIST_HEAD(&node->link);
+
+       i915_sched_node_reinit(node);
+}
+
+void i915_sched_node_reinit(struct i915_sched_node *node)
+{
        node->attr.priority = I915_PRIORITY_INVALID;
        node->semaphores = 0;
        node->flags = 0;
+
+       GEM_BUG_ON(!list_empty(&node->signalers_list));
+       GEM_BUG_ON(!list_empty(&node->waiters_list));
+       GEM_BUG_ON(!list_empty(&node->link));
 }
 
 static struct i915_dependency *
@@ -481,6 +491,7 @@ void i915_sched_node_fini(struct i915_sched_node *node)
                if (dep->flags & I915_DEPENDENCY_ALLOC)
                        i915_dependency_free(dep);
        }
+       INIT_LIST_HEAD(&node->signalers_list);
 
        /* Remove ourselves from everyone who depends upon us */
        list_for_each_entry_safe(dep, tmp, &node->waiters_list, wait_link) {
@@ -491,6 +502,7 @@ void i915_sched_node_fini(struct i915_sched_node *node)
                if (dep->flags & I915_DEPENDENCY_ALLOC)
                        i915_dependency_free(dep);
        }
+       INIT_LIST_HEAD(&node->waiters_list);
 
        spin_unlock_irq(&schedule_lock);
 }
index 07d243acf553b2bc9f018be31d8072e5929c9a2b..d1dc4efef77b562f3b044050ddab332ed64ab2fe 100644 (file)
@@ -26,6 +26,7 @@
                                         sched.link)
 
 void i915_sched_node_init(struct i915_sched_node *node);
+void i915_sched_node_reinit(struct i915_sched_node *node);
 
 bool __i915_sched_node_add_dependency(struct i915_sched_node *node,
                                      struct i915_sched_node *signal,
index 6a88db291252bdbd1ab9575c7fa9185cf8b8ad6c..1584f34a6bf91e0527f74c0fe565ce05cf37d3f9 100644 (file)
 #include "i915_sw_fence.h"
 #include "i915_selftest.h"
 
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG)
+#define I915_SW_FENCE_BUG_ON(expr) BUG_ON(expr)
+#else
+#define I915_SW_FENCE_BUG_ON(expr) BUILD_BUG_ON_INVALID(expr)
+#endif
+
 #define I915_SW_FENCE_FLAG_ALLOC BIT(3) /* after WQ_FLAG_* for safety */
 
 static DEFINE_SPINLOCK(i915_sw_fence_lock);
@@ -218,13 +224,21 @@ void __i915_sw_fence_init(struct i915_sw_fence *fence,
 {
        BUG_ON(!fn || (unsigned long)fn & ~I915_SW_FENCE_MASK);
 
+       __init_waitqueue_head(&fence->wait, name, key);
+       fence->flags = (unsigned long)fn;
+
+       i915_sw_fence_reinit(fence);
+}
+
+void i915_sw_fence_reinit(struct i915_sw_fence *fence)
+{
        debug_fence_init(fence);
 
-       __init_waitqueue_head(&fence->wait, name, key);
        atomic_set(&fence->pending, 1);
        fence->error = 0;
 
-       fence->flags = (unsigned long)fn;
+       I915_SW_FENCE_BUG_ON(!fence->flags);
+       I915_SW_FENCE_BUG_ON(!list_empty(&fence->wait.head));
 }
 
 void i915_sw_fence_commit(struct i915_sw_fence *fence)
index ab7d58bd0b9d554843844919e23160b83b4a1b3c..1e90d9a51bd222a79629a69e853d280e85056cf9 100644 (file)
@@ -54,6 +54,8 @@ do {                                                          \
        __i915_sw_fence_init((fence), (fn), NULL, NULL)
 #endif
 
+void i915_sw_fence_reinit(struct i915_sw_fence *fence);
+
 #ifdef CONFIG_DRM_I915_SW_FENCE_DEBUG_OBJECTS
 void i915_sw_fence_fini(struct i915_sw_fence *fence);
 #else