]> git.baikalelectronics.ru Git - kernel.git/commitdiff
drm/i915: Switch obj->mm.lock lockdep annotations on its head
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 5 Nov 2019 09:01:48 +0000 (10:01 +0100)
committerMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Thu, 7 Nov 2019 08:58:11 +0000 (09:58 +0100)
The trouble with having a plain nesting flag for locks which do not
naturally nest (unlike block devices and their partitions, which is
the original motivation for nesting levels) is that lockdep will
never spot a true deadlock if you screw up.

This patch is an attempt at trying better, by highlighting a bit more
of the actual nature of the nesting that's going on. Essentially we
have two kinds of objects:

- objects without pages allocated, which cannot be on any lru and are
  hence inaccessible to the shrinker.

- objects which have pages allocated, which are on an lru, and which
  the shrinker can decide to throw out.

For the former type of object, memory allocations while holding
obj->mm.lock are permissible. For the latter they are not. And
get/put_pages transitions between the two types of objects.

This is still not entirely fool-proof since the rules might change.
But as long as we run such a code ever at runtime lockdep should be
able to observe the inconsistency and complain (like with any other
lockdep class that we've split up in multiple classes). But there are
a few clear benefits:

- We can drop the nesting flag parameter from
  __i915_gem_object_put_pages, because that function by definition is
  never going allocate memory, and calling it on an object which
  doesn't have its pages allocated would be a bug.

- We strictly catch more bugs, since there's not only one place in the
  entire tree which is annotated with the special class. All the
  other places that had explicit lockdep nesting annotations we're now
  going to leave up to lockdep again.

- Specifically this catches stuff like calling get_pages from
  put_pages (which isn't really a good idea, if we can call get_pages
  so could the shrinker). I've seen patches do exactly that.

Of course I fully expect CI will show me for the fool I am with this
one here :-)

v2: There can only be one (lockdep only has a cache for the first
subclass, not for deeper ones, and we don't want to make these locks
even slower). Still separate enums for better documentation.

Real fix: don't forget about phys objs and pin_map(), and fix the
shrinker to have the right annotations ... silly me.

v3: Forgot usertptr too ...

v4: Improve comment for pages_pin_count, drop the IMPORTANT comment
and instead prime lockdep (Chris).

v5: Appease checkpatch, no double empty lines (Chris)

v6: More rebasing over selftest changes. Also somehow I forgot to
push this patch :-/

Also format comments consistently while at it.

v7: Fix typo in commit message (Joonas)

Also drop the priming, with the lmem merge we now have allocations
while holding the lmem lock, which wreaks the generic priming I've
done in earlier patches. Should probably be resurrected when lmem is
fixed. See

commit 232a6ebae419193f5b8da4fa869ae5089ab105c2
Author: Matthew Auld <matthew.auld@intel.com>
Date:   Tue Oct 8 17:01:14 2019 +0100

    drm/i915: introduce intel_memory_region

I'm keeping the priming patch locally so it wont get lost.

Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: "Tang, CQ" <cq.tang@intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> (v5)
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com> (v6)
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191105090148.30269-1-daniel.vetter@ffwll.ch
[mlankhorst: Fix commit typos pointed out by Michael Ruhl]

drivers/gpu/drm/i915/gem/i915_gem_object.c
drivers/gpu/drm/i915/gem/i915_gem_object.h
drivers/gpu/drm/i915/gem/i915_gem_object_types.h
drivers/gpu/drm/i915/gem/i915_gem_pages.c
drivers/gpu/drm/i915/gem/i915_gem_phys.c
drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
drivers/gpu/drm/i915/gem/i915_gem_userptr.c
drivers/gpu/drm/i915/gem/selftests/huge_pages.c
drivers/gpu/drm/i915/selftests/intel_memory_region.c

index a50296cce0d88efb56a07b349ff56b88f7b689ba..db103d3c8760f4925ed8180766b17e28863df673 100644 (file)
@@ -22,6 +22,8 @@
  *
  */
 
+#include <linux/sched/mm.h>
+
 #include "display/intel_frontbuffer.h"
 #include "gt/intel_gt.h"
 #include "i915_drv.h"
@@ -186,7 +188,7 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
                GEM_BUG_ON(!list_empty(&obj->lut_list));
 
                atomic_set(&obj->mm.pages_pin_count, 0);
-               __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+               __i915_gem_object_put_pages(obj);
                GEM_BUG_ON(i915_gem_object_has_pages(obj));
                bitmap_free(obj->bit_17);
 
index 458cd51331f1826d0a479c0356b1c85c0ca959d0..edaf7126a84d17a2b54170fa567d85b65611c851 100644 (file)
@@ -319,11 +319,22 @@ i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
 
 enum i915_mm_subclass { /* lockdep subclass for obj->mm.lock/struct_mutex */
        I915_MM_NORMAL = 0,
-       I915_MM_SHRINKER /* called "recursively" from direct-reclaim-esque */
+       /*
+        * Only used by struct_mutex, when called "recursively" from
+        * direct-reclaim-esque. Safe because there is only every one
+        * struct_mutex in the entire system.
+        */
+       I915_MM_SHRINKER = 1,
+       /*
+        * Used for obj->mm.lock when allocating pages. Safe because the object
+        * isn't yet on any LRU, and therefore the shrinker can't deadlock on
+        * it. As soon as the object has pages, obj->mm.lock nests within
+        * fs_reclaim.
+        */
+       I915_MM_GET_PAGES = 1,
 };
 
-int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj,
-                               enum i915_mm_subclass subclass);
+int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
 void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
 void i915_gem_object_writeback(struct drm_i915_gem_object *obj);
 
index 96008374a4120480baee67ad656d7eb9472c1e07..15f8297dc34e4639f1dc0229e2c2340bc2780d1d 100644 (file)
@@ -162,7 +162,11 @@ struct drm_i915_gem_object {
        atomic_t bind_count;
 
        struct {
-               struct mutex lock; /* protects the pages and their use */
+               /*
+                * Protects the pages and their use. Do not use directly, but
+                * instead go through the pin/unpin interfaces.
+                */
+               struct mutex lock;
                atomic_t pages_pin_count;
                atomic_t shrink_pin;
 
index 29f4c28507458f09e2139b29d1dcf919003e3dea..f402c2c415c26cf58374e41cf377ac03932448f9 100644 (file)
@@ -106,7 +106,7 @@ int __i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
 {
        int err;
 
-       err = mutex_lock_interruptible(&obj->mm.lock);
+       err = mutex_lock_interruptible_nested(&obj->mm.lock, I915_MM_GET_PAGES);
        if (err)
                return err;
 
@@ -190,8 +190,7 @@ __i915_gem_object_unset_pages(struct drm_i915_gem_object *obj)
        return pages;
 }
 
-int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj,
-                               enum i915_mm_subclass subclass)
+int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
 {
        struct sg_table *pages;
        int err;
@@ -202,7 +201,7 @@ int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj,
        GEM_BUG_ON(atomic_read(&obj->bind_count));
 
        /* May be called by shrinker from within get_pages() (on another bo) */
-       mutex_lock_nested(&obj->mm.lock, subclass);
+       mutex_lock(&obj->mm.lock);
        if (unlikely(atomic_read(&obj->mm.pages_pin_count))) {
                err = -EBUSY;
                goto unlock;
@@ -308,7 +307,7 @@ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
        if (!i915_gem_object_type_has(obj, flags))
                return ERR_PTR(-ENXIO);
 
-       err = mutex_lock_interruptible(&obj->mm.lock);
+       err = mutex_lock_interruptible_nested(&obj->mm.lock, I915_MM_GET_PAGES);
        if (err)
                return ERR_PTR(err);
 
index 8043ff63d73f744efc73e377394af4d5e8f010a0..b1b7c1b3038aaa41b8bbdc1c5c7cdfef13d4e2d0 100644 (file)
@@ -164,7 +164,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
        if (err)
                return err;
 
-       mutex_lock(&obj->mm.lock);
+       mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
 
        if (obj->mm.madv != I915_MADV_WILLNEED) {
                err = -EFAULT;
index fd3ce6da8497d7d56743f3da4564c62dcfd17752..066b3df677e870fe82430c9fb5402e2a141edf5d 100644 (file)
@@ -57,7 +57,7 @@ static bool unsafe_drop_pages(struct drm_i915_gem_object *obj,
                flags = I915_GEM_OBJECT_UNBIND_ACTIVE;
 
        if (i915_gem_object_unbind(obj, flags) == 0)
-               __i915_gem_object_put_pages(obj, I915_MM_SHRINKER);
+               __i915_gem_object_put_pages(obj);
 
        return !i915_gem_object_has_pages(obj);
 }
@@ -209,8 +209,7 @@ i915_gem_shrink(struct drm_i915_private *i915,
 
                        if (unsafe_drop_pages(obj, shrink)) {
                                /* May arrive from get_pages on another bo */
-                               mutex_lock_nested(&obj->mm.lock,
-                                                 I915_MM_SHRINKER);
+                               mutex_lock(&obj->mm.lock);
                                if (!i915_gem_object_has_pages(obj)) {
                                        try_to_writeback(obj, shrink);
                                        count += obj->base.size >> PAGE_SHIFT;
index 1e045c3370444021de0dd88b94e99cf009bc2145..ee65c6acf0e22e7f1e9389126242f1032e8af2e3 100644 (file)
@@ -131,7 +131,7 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
                ret = i915_gem_object_unbind(obj,
                                             I915_GEM_OBJECT_UNBIND_ACTIVE);
                if (ret == 0)
-                       ret = __i915_gem_object_put_pages(obj, I915_MM_SHRINKER);
+                       ret = __i915_gem_object_put_pages(obj);
                i915_gem_object_put(obj);
                if (ret)
                        return ret;
@@ -483,7 +483,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
                }
        }
 
-       mutex_lock(&obj->mm.lock);
+       mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
        if (obj->userptr.work == &work->work) {
                struct sg_table *pages = ERR_PTR(ret);
 
index 688c49a24f327fde1d4ff7491bf83db6ffb59536..5c9583349077f5d2fca53aa76f8cb8accaf05f21 100644 (file)
@@ -517,7 +517,7 @@ static int igt_mock_memory_region_huge_pages(void *arg)
                        i915_vma_unpin(vma);
                        i915_vma_close(vma);
 
-                       __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+                       __i915_gem_object_put_pages(obj);
                        i915_gem_object_put(obj);
                }
        }
@@ -650,7 +650,7 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
                i915_vma_close(vma);
 
                i915_gem_object_unpin_pages(obj);
-               __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+               __i915_gem_object_put_pages(obj);
                i915_gem_object_put(obj);
        }
 
@@ -678,7 +678,7 @@ static void close_object_list(struct list_head *objects,
 
                list_del(&obj->st_link);
                i915_gem_object_unpin_pages(obj);
-               __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+               __i915_gem_object_put_pages(obj);
                i915_gem_object_put(obj);
        }
 }
@@ -948,7 +948,7 @@ static int igt_mock_ppgtt_64K(void *arg)
                        i915_vma_close(vma);
 
                        i915_gem_object_unpin_pages(obj);
-                       __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+                       __i915_gem_object_put_pages(obj);
                        i915_gem_object_put(obj);
                }
        }
@@ -1301,7 +1301,7 @@ static int igt_ppgtt_exhaust_huge(void *arg)
                        }
 
                        i915_gem_object_unpin_pages(obj);
-                       __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+                       __i915_gem_object_put_pages(obj);
                        i915_gem_object_put(obj);
                }
        }
@@ -1442,7 +1442,7 @@ try_again:
                }
 out_unpin:
                i915_gem_object_unpin_pages(obj);
-               __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+               __i915_gem_object_put_pages(obj);
 out_put:
                i915_gem_object_put(obj);
 
@@ -1530,7 +1530,7 @@ static int igt_ppgtt_sanity_check(void *arg)
                        err = igt_write_huge(ctx, obj);
 
                        i915_gem_object_unpin_pages(obj);
-                       __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+                       __i915_gem_object_put_pages(obj);
                        i915_gem_object_put(obj);
 
                        if (err) {
index 19e1cca8f14365d53a2cd0dcdb2268bdd4214c80..95d609abd39b70da1954da1db92e2bad5c72aa4e 100644 (file)
@@ -32,7 +32,7 @@ static void close_objects(struct intel_memory_region *mem,
                if (i915_gem_object_has_pinned_pages(obj))
                        i915_gem_object_unpin_pages(obj);
                /* No polluting the memory region between tests */
-               __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+               __i915_gem_object_put_pages(obj);
                list_del(&obj->st_link);
                i915_gem_object_put(obj);
        }
@@ -122,7 +122,7 @@ put:
 static void igt_object_release(struct drm_i915_gem_object *obj)
 {
        i915_gem_object_unpin_pages(obj);
-       __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
+       __i915_gem_object_put_pages(obj);
        list_del(&obj->st_link);
        i915_gem_object_put(obj);
 }