@@ -4873,13 +4873,28 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file)
return ret;
}
-static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
+static bool
+i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
{
- if (!mutex_is_locked(mutex))
- return false;
+ *unlock = true;
+ if (mutex_trylock(&dev->struct_mutex))
+ return true;
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES)
- return mutex->owner == task;
+ if (dev->struct_mutex.owner == current) {
+ if (to_i915(dev)->mm.shrinker_no_lock_stealing)
+ return false;
+
+ *unlock = false;
+ } else {
+ /* lockdep classifies this as an ABBA deadlock,
+ * but given the lock stealing above this is
+ * safe. So treat this a separate subclass.
+ */
+ mutex_lock_nested(&dev->struct_mutex, 1);
+ }
+
+ return true;
#else
/* Since UP may be pre-empted, we cannot assume that we own the lock */
return false;
@@ -4908,18 +4923,11 @@ i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
mm.inactive_shrinker);
struct drm_device *dev = dev_priv->dev;
struct drm_i915_gem_object *obj;
- bool unlock = true;
unsigned long count;
+ bool unlock;
- if (!mutex_trylock(&dev->struct_mutex)) {
- if (!mutex_is_locked_by(&dev->struct_mutex, current))
- return 0;
-
- if (dev_priv->mm.shrinker_no_lock_stealing)
- return 0;
-
- unlock = false;
- }
+ if (!i915_gem_shrinker_lock(dev, &unlock))
+ return 0;
count = 0;
list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list)
@@ -5012,17 +5020,10 @@ i915_gem_inactive_scan(struct shrinker *shrinker, struct shrink_control *sc)
mm.inactive_shrinker);
struct drm_device *dev = dev_priv->dev;
unsigned long freed;
- bool unlock = true;
-
- if (!mutex_trylock(&dev->struct_mutex)) {
- if (!mutex_is_locked_by(&dev->struct_mutex, current))
- return SHRINK_STOP;
+ bool unlock;
- if (dev_priv->mm.shrinker_no_lock_stealing)
- return SHRINK_STOP;
-
- unlock = false;
- }
+ if (!i915_gem_shrinker_lock(dev, &unlock))
+ return SHRINK_STOP;
freed = i915_gem_purge(dev_priv, sc->nr_to_scan);
if (freed < sc->nr_to_scan)