aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2016-10-07 11:43:51 -0400
committerIngo Molnar <mingo@kernel.org>2016-11-15 08:19:55 -0500
commit0f5225b024d4bffd682aab008c35862e8fdc1865 (patch)
treea5202d5cdb071b3060975c4bd4c1c3f56c792edc
parent83f06168ef15da5dc735c7ea14fae67609ed9538 (diff)
locking/mutex, drm: Introduce mutex_trylock_recursive()
By popular DRM demand, introduce mutex_trylock_recursive() to fix up the two GEM users. Without this it is very easy for these drivers to get stuck in low-memory situations and trigger OOM. Work is in progress to remove the need for this in at least i915. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: David Airlie <airlied@linux.ie> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Ding Tianhong <dingtianhong@huawei.com> Cc: Imre Deak <imre.deak@intel.com> Cc: Jason Low <jason.low2@hpe.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Paul E. McKenney <paulmck@us.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rob Clark <robdclark@gmail.com> Cc: Terry Rudd <terry.rudd@hpe.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tim Chen <tim.c.chen@linux.intel.com> Cc: Will Deacon <Will.Deacon@arm.com> Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--drivers/gpu/drm/i915/i915_gem_shrinker.c15
-rw-r--r--drivers/gpu/drm/msm/msm_gem_shrinker.c16
-rw-r--r--include/linux/mutex.h31
-rwxr-xr-xscripts/checkpatch.pl6
4 files changed, 61 insertions, 7 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c
index e9bd2a81d03a..c450076d2f9b 100644
--- a/drivers/gpu/drm/i915/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c
@@ -227,11 +227,20 @@ unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv)
227 227
228static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock) 228static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
229{ 229{
230 if (!mutex_trylock(&dev->struct_mutex)) 230 switch (mutex_trylock_recursive(&dev->struct_mutex)) {
231 case MUTEX_TRYLOCK_FAILED:
231 return false; 232 return false;
232 233
233 *unlock = true; 234 case MUTEX_TRYLOCK_SUCCESS:
234 return true; 235 *unlock = true;
236 return true;
237
238 case MUTEX_TRYLOCK_RECURSIVE:
239 *unlock = false;
240 return true;
241 }
242
243 BUG();
235} 244}
236 245
237static unsigned long 246static unsigned long
diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c
index 6d2e885bd58e..b77bca75bb5f 100644
--- a/drivers/gpu/drm/msm/msm_gem_shrinker.c
+++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c
@@ -20,13 +20,21 @@
20 20
21static bool msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock) 21static bool msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
22{ 22{
23 if (!mutex_trylock(&dev->struct_mutex)) 23 switch (mutex_trylock_recursive(&dev->struct_mutex)) {
24 case MUTEX_TRYLOCK_FAILED:
24 return false; 25 return false;
25 26
26 *unlock = true; 27 case MUTEX_TRYLOCK_SUCCESS:
27 return true; 28 *unlock = true;
28} 29 return true;
30
31 case MUTEX_TRYLOCK_RECURSIVE:
32 *unlock = false;
33 return true;
34 }
29 35
36 BUG();
37}
30 38
31static unsigned long 39static unsigned long
32msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) 40msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index 4d3bccabbea5..6a902f0a2148 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -189,4 +189,35 @@ extern void mutex_unlock(struct mutex *lock);
189 189
190extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); 190extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
191 191
192/*
193 * These values are chosen such that FAIL and SUCCESS match the
194 * values of the regular mutex_trylock().
195 */
196enum mutex_trylock_recursive_enum {
197 MUTEX_TRYLOCK_FAILED = 0,
198 MUTEX_TRYLOCK_SUCCESS = 1,
199 MUTEX_TRYLOCK_RECURSIVE,
200};
201
202/**
203 * mutex_trylock_recursive - trylock variant that allows recursive locking
204 * @lock: mutex to be locked
205 *
206 * This function should not be used, _ever_. It is purely for hysterical GEM
207 * raisins, and once those are gone this will be removed.
208 *
209 * Returns:
210 * MUTEX_TRYLOCK_FAILED - trylock failed,
211 * MUTEX_TRYLOCK_SUCCESS - lock acquired,
212 * MUTEX_TRYLOCK_RECURSIVE - we already owned the lock.
213 */
214static inline __deprecated __must_check enum mutex_trylock_recursive_enum
215mutex_trylock_recursive(struct mutex *lock)
216{
217 if (unlikely(__mutex_owner(lock) == current))
218 return MUTEX_TRYLOCK_RECURSIVE;
219
220 return mutex_trylock(lock);
221}
222
192#endif /* __LINUX_MUTEX_H */ 223#endif /* __LINUX_MUTEX_H */
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index a8368d1c4348..23f462f64a3f 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -6076,6 +6076,12 @@ sub process {
6076 } 6076 }
6077 } 6077 }
6078 6078
6079# check for mutex_trylock_recursive usage
6080 if ($line =~ /mutex_trylock_recursive/) {
6081 ERROR("LOCKING",
6082 "recursive locking is bad, do not use this ever.\n" . $herecurr);
6083 }
6084
6079# check for lockdep_set_novalidate_class 6085# check for lockdep_set_novalidate_class
6080 if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || 6086 if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
6081 $line =~ /__lockdep_no_validate__\s*\)/ ) { 6087 $line =~ /__lockdep_no_validate__\s*\)/ ) {