<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class="">Hi Rob,</div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69); min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class="">I can see how we can trigger the shrinker on objB while holding objA->lock. So, the nested lock with class SHRINKER makes sense.</div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class="">However, I’m trying to figure how the get_pages/vmap/fault path on an objA can end up triggering the shrinker on objA itself. As objA itself would not be purgeable (msm_obj->sgt would not be set)/vunmappable (msm_obj->vaddr would not be set) yet at that point, we would never end up calling msm_gem_purge or msm_gem_vunmap on objA itself right? If that is the case, we may not need the (msm_obj->madv == MSM_MADV_WILLNEED) check? Or am I missing something here?</div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69); min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class="">I think shrinker_vmap would also need the nested SHRINKER lock before it calls msm_gem_vunmap because a vmap on objA could trigger msm_gem_vunmap on objB.</div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69); min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class="">I really like the idea of protecting priv->inactive_list with a separate lock as that is pretty much why the shrinker needs to hold struct_mutex.</div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69); min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class="">Thanks,</div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class="">Sushmita</div><div style="margin: 0px; line-height: normal; font-family: 'Helvetica Neue'; color: rgb(69, 69, 69);" class=""><br class=""></div><div style=""><blockquote type="cite" class=""><div class="">On Jun 15, 2017, at 7:20 AM, Rob Clark <<a href="mailto:robdclark@gmail.com" class="">robdclark@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">---<br class="">This is roughly based on Chris's suggestion, in particular the part<br class="">about using mutex_lock_nested().  It's not *exactly* the same, in<br class="">particular msm_obj->lock protects a bit more than just backing store<br class="">and we don't currently track a pin_count.  (Instead we currently<br class="">keep pages pinned until the object is purged or freed.)<br class=""><br class="">Instead of making msm_obj->lock only cover backing store, it is<br class="">easier to split out madv, which is still protected by struct_mutex,<br class="">which is still held by the shrinker, so the shrinker does not need<br class="">to grab msm_obj->lock until it purges an object.  We avoid going<br class="">down any path that could trigger shrinker by ensuring that<br class="">msm_obj->madv == WILLNEED.  To synchronize access to msm_obj->madv<br class="">it is protected by msm_obj->lock inside struct_mutex.<br class=""><br class="">This seems to keep lockdep happy in my testing so far.<br class=""><br class=""> drivers/gpu/drm/msm/msm_gem.c          | 54 ++++++++++++++++++++++++++++++++--<br class=""> drivers/gpu/drm/msm/msm_gem.h          |  1 +<br class=""> drivers/gpu/drm/msm/msm_gem_shrinker.c | 12 ++++++++<br class=""> 3 files changed, 65 insertions(+), 2 deletions(-)<br class=""><br class="">diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c<br class="">index e132548..f5d1f84 100644<br class="">--- a/drivers/gpu/drm/msm/msm_gem.c<br class="">+++ b/drivers/gpu/drm/msm/msm_gem.c<br class="">@@ -26,6 +26,22 @@<br class=""> #include "msm_gpu.h"<br class=""> #include "msm_mmu.h"<br class=""><br class="">+/* The shrinker can be triggered while we hold objA->lock, and need<br class="">+ * to grab objB->lock to purge it.  Lockdep just sees these as a single<br class="">+ * class of lock, so we use subclasses to teach it the difference.<br class="">+ *<br class="">+ * OBJ_LOCK_NORMAL is implicit (ie. normal mutex_lock() call), and<br class="">+ * OBJ_LOCK_SHRINKER is used in msm_gem_purge().<br class="">+ *<br class="">+ * It is *essential* that we never go down paths that could trigger the<br class="">+ * shrinker for a purgable object.  This is ensured by checking that<br class="">+ * msm_obj->madv == MSM_MADV_WILLNEED.<br class="">+ */<br class="">+enum {<br class="">+<span class="Apple-tab-span" style="white-space:pre">  </span>OBJ_LOCK_NORMAL,<br class="">+<span class="Apple-tab-span" style="white-space:pre">      </span>OBJ_LOCK_SHRINKER,<br class="">+};<br class="">+<br class=""> static dma_addr_t physaddr(struct drm_gem_object *obj)<br class=""> {<br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>struct msm_gem_object *msm_obj = to_msm_bo(obj);<br class="">@@ -150,6 +166,12 @@ struct page **msm_gem_get_pages(struct drm_gem_object *obj)<br class=""> <span class="Apple-tab-span" style="white-space:pre"> </span>struct page **p;<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre"> </span>mutex_lock(&msm_obj->lock);<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space:pre">      </span>if (WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED)) {<br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span><span class="Apple-tab-span" style="white-space:pre">    </span>mutex_unlock(&msm_obj->lock);<br class="">+<span class="Apple-tab-span" style="white-space:pre">  </span><span class="Apple-tab-span" style="white-space:pre">    </span>return ERR_PTR(-EBUSY);<br class="">+<span class="Apple-tab-span" style="white-space:pre">       </span>}<br class="">+<br class=""> <span class="Apple-tab-span" style="white-space:pre">       </span>p = get_pages(obj);<br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>mutex_unlock(&msm_obj->lock);<br class=""> <span class="Apple-tab-span" style="white-space:pre">  </span>return p;<br class="">@@ -220,6 +242,11 @@ int msm_gem_fault(struct vm_fault *vmf)<br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span>if (ret)<br class=""> <span class="Apple-tab-span" style="white-space:pre">      </span><span class="Apple-tab-span" style="white-space:pre">    </span>goto out;<br class=""><br class="">+<span class="Apple-tab-span" style="white-space:pre">        </span>if (WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED)) {<br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span><span class="Apple-tab-span" style="white-space:pre">    </span>mutex_unlock(&msm_obj->lock);<br class="">+<span class="Apple-tab-span" style="white-space:pre">  </span><span class="Apple-tab-span" style="white-space:pre">    </span>return VM_FAULT_SIGBUS;<br class="">+<span class="Apple-tab-span" style="white-space:pre">       </span>}<br class="">+<br class=""> <span class="Apple-tab-span" style="white-space:pre">       </span>/* make sure we have pages attached now */<br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span>pages = get_pages(obj);<br class=""> <span class="Apple-tab-span" style="white-space:pre">       </span>if (IS_ERR(pages)) {<br class="">@@ -358,6 +385,11 @@ int msm_gem_get_iova(struct drm_gem_object *obj,<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>mutex_lock(&msm_obj->lock);<br class=""><br class="">+<span class="Apple-tab-span" style="white-space:pre">       </span>if (WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED)) {<br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span><span class="Apple-tab-span" style="white-space:pre">    </span>mutex_unlock(&msm_obj->lock);<br class="">+<span class="Apple-tab-span" style="white-space:pre">  </span><span class="Apple-tab-span" style="white-space:pre">    </span>return -EBUSY;<br class="">+<span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">+<br class=""> <span class="Apple-tab-span" style="white-space:pre">       </span>vma = lookup_vma(obj, aspace);<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>if (!vma) {<br class="">@@ -454,6 +486,12 @@ void *msm_gem_get_vaddr(struct drm_gem_object *obj)<br class=""> <span class="Apple-tab-span" style="white-space:pre">      </span>struct msm_gem_object *msm_obj = to_msm_bo(obj);<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre"> </span>mutex_lock(&msm_obj->lock);<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space:pre">      </span>if (WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED)) {<br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span><span class="Apple-tab-span" style="white-space:pre">    </span>mutex_unlock(&msm_obj->lock);<br class="">+<span class="Apple-tab-span" style="white-space:pre">  </span><span class="Apple-tab-span" style="white-space:pre">    </span>return ERR_PTR(-EBUSY);<br class="">+<span class="Apple-tab-span" style="white-space:pre">       </span>}<br class="">+<br class=""> <span class="Apple-tab-span" style="white-space:pre">       </span>if (!msm_obj->vaddr) {<br class=""> <span class="Apple-tab-span" style="white-space:pre">     </span><span class="Apple-tab-span" style="white-space:pre">    </span>struct page **pages = get_pages(obj);<br class=""> <span class="Apple-tab-span" style="white-space:pre"> </span><span class="Apple-tab-span" style="white-space:pre">    </span>if (IS_ERR(pages)) {<br class="">@@ -489,12 +527,18 @@ int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv)<br class=""> {<br class=""> <span class="Apple-tab-span" style="white-space:pre">  </span>struct msm_gem_object *msm_obj = to_msm_bo(obj);<br class=""><br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span>mutex_lock(&msm_obj->lock);<br class="">+<br class=""> <span class="Apple-tab-span" style="white-space:pre">      </span>WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span>if (msm_obj->madv != __MSM_MADV_PURGED)<br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span><span class="Apple-tab-span" style="white-space:pre">    </span>msm_obj->madv = madv;<br class=""><br class="">-<span class="Apple-tab-span" style="white-space:pre"> </span>return (msm_obj->madv != __MSM_MADV_PURGED);<br class="">+<span class="Apple-tab-span" style="white-space:pre">       </span>madv = msm_obj->madv;<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space:pre">        </span>mutex_unlock(&msm_obj->lock);<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space:pre">    </span>return (madv != __MSM_MADV_PURGED);<br class=""> }<br class=""><br class=""> void msm_gem_purge(struct drm_gem_object *obj)<br class="">@@ -506,6 +550,8 @@ void msm_gem_purge(struct drm_gem_object *obj)<br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span>WARN_ON(!is_purgeable(msm_obj));<br class=""> <span class="Apple-tab-span" style="white-space:pre">      </span>WARN_ON(obj->import_attach);<br class=""><br class="">+<span class="Apple-tab-span" style="white-space:pre">  </span>mutex_lock_nested(&msm_obj->lock, OBJ_LOCK_SHRINKER);<br class="">+<br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span>put_iova(obj);<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>msm_gem_vunmap(obj);<br class="">@@ -526,6 +572,8 @@ void msm_gem_purge(struct drm_gem_object *obj)<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre">      </span>invalidate_mapping_pages(file_inode(obj->filp)->i_mapping,<br class=""> <span class="Apple-tab-span" style="white-space:pre">      </span><span class="Apple-tab-span" style="white-space:pre">    </span><span class="Apple-tab-span" style="white-space:pre">    </span>0, (loff_t)-1);<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span>mutex_unlock(&msm_obj->lock);<br class=""> }<br class=""><br class=""> void msm_gem_vunmap(struct drm_gem_object *obj)<br class="">@@ -660,7 +708,7 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)<br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>uint64_t off = drm_vma_node_start(&obj->vma_node);<br class=""> <span class="Apple-tab-span" style="white-space:pre">     </span>const char *madv;<br class=""><br class="">-<span class="Apple-tab-span" style="white-space:pre">        </span>WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));<br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span>mutex_lock(&msm_obj->lock);<br class=""><br class=""> <span class="Apple-tab-span" style="white-space:pre">       </span>switch (msm_obj->madv) {<br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>case __MSM_MADV_PURGED:<br class="">@@ -701,6 +749,8 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)<br class=""> <span class="Apple-tab-span" style="white-space:pre"> </span>if (fence)<br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span><span class="Apple-tab-span" style="white-space:pre">    </span>describe_fence(fence, "Exclusive", m);<br class=""> <span class="Apple-tab-span" style="white-space:pre">      </span>rcu_read_unlock();<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space:pre">      </span>mutex_unlock(&msm_obj->lock);<br class=""> }<br class=""><br class=""> void msm_gem_describe_objects(struct list_head *list, struct seq_file *m)<br class="">diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h<br class="">index 9ad5ba4c..2b9b8e9 100644<br class="">--- a/drivers/gpu/drm/msm/msm_gem.h<br class="">+++ b/drivers/gpu/drm/msm/msm_gem.h<br class="">@@ -101,6 +101,7 @@ static inline bool is_active(struct msm_gem_object *msm_obj)<br class=""><br class=""> static inline bool is_purgeable(struct msm_gem_object *msm_obj)<br class=""> {<br class="">+<span class="Apple-tab-span" style="white-space:pre">       </span>WARN_ON(!mutex_is_locked(&msm_obj->base.dev->struct_mutex));<br class=""> <span class="Apple-tab-span" style="white-space:pre">        </span>return (msm_obj->madv == MSM_MADV_DONTNEED) && msm_obj->sgt &&<br class=""> <span class="Apple-tab-span" style="white-space:pre">  </span><span class="Apple-tab-span" style="white-space:pre">    </span><span class="Apple-tab-span" style="white-space:pre">    </span>!msm_obj->base.dma_buf && !msm_obj->base.import_attach;<br class=""> }<br class="">diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c<br class="">index ab1dd02..e1db4ad 100644<br class="">--- a/drivers/gpu/drm/msm/msm_gem_shrinker.c<br class="">+++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c<br class="">@@ -20,6 +20,18 @@<br class=""><br class=""> static bool msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock)<br class=""> {<br class="">+<span class="Apple-tab-span" style="white-space:pre">     </span>/* NOTE: we are *closer* to being able to get rid of<br class="">+<span class="Apple-tab-span" style="white-space:pre">  </span> * mutex_trylock_recursive().. the msm_gem code itself does<br class="">+<span class="Apple-tab-span" style="white-space:pre">   </span> * not need struct_mutex, although codepaths that can trigger<br class="">+<span class="Apple-tab-span" style="white-space:pre"> </span> * shrinker are still called in code-paths that hold the<br class="">+<span class="Apple-tab-span" style="white-space:pre">      </span> * struct_mutex.<br class="">+<span class="Apple-tab-span" style="white-space:pre">      </span> *<br class="">+<span class="Apple-tab-span" style="white-space:pre">    </span> * Also, msm_obj->madv is protected by struct_mutex.<br class="">+<span class="Apple-tab-span" style="white-space:pre">       </span> *<br class="">+<span class="Apple-tab-span" style="white-space:pre">    </span> * The next step is probably split out a seperate lock for<br class="">+<span class="Apple-tab-span" style="white-space:pre">    </span> * protecting inactive_list, so that shrinker does not need<br class="">+<span class="Apple-tab-span" style="white-space:pre">   </span> * struct_mutex.<br class="">+<span class="Apple-tab-span" style="white-space:pre">      </span> */<br class=""> <span class="Apple-tab-span" style="white-space:pre">   </span>switch (mutex_trylock_recursive(&dev->struct_mutex)) {<br class=""> <span class="Apple-tab-span" style="white-space:pre"> </span>case MUTEX_TRYLOCK_FAILED:<br class=""> <span class="Apple-tab-span" style="white-space:pre">    </span><span class="Apple-tab-span" style="white-space:pre">    </span>return false;<br class="">-- <br class="">2.9.4<br class=""><br class=""></div></div></blockquote></div><br class=""></body></html>