Commit 12235da8 authored by Maarten Lankhorst's avatar Maarten Lankhorst Committed by Peter Zijlstra
Browse files

kernel/locking: Add context to ww_mutex_trylock()



i915 will soon gain an eviction path that trylock a whole lot of locks
for eviction, getting dmesg failures like below:

  BUG: MAX_LOCK_DEPTH too low!
  turning off the locking correctness validator.
  depth: 48  max: 48!
  48 locks held by i915_selftest/5776:
   #0: ffff888101a79240 (&dev->mutex){....}-{3:3}, at: __driver_attach+0x88/0x160
   #1: ffffc900009778c0 (reservation_ww_class_acquire){+.+.}-{0:0}, at: i915_vma_pin.constprop.63+0x39/0x1b0 [i915]
   #2: ffff88800cf74de8 (reservation_ww_class_mutex){+.+.}-{3:3}, at: i915_vma_pin.constprop.63+0x5f/0x1b0 [i915]
   #3: ffff88810c7f9e38 (&vm->mutex/1){+.+.}-{3:3}, at: i915_vma_pin_ww+0x1c4/0x9d0 [i915]
   #4: ffff88810bad5768 (reservation_ww_class_mutex){+.+.}-{3:3}, at: i915_gem_evict_something+0x110/0x860 [i915]
   #5: ffff88810bad60e8 (reservation_ww_class_mutex){+.+.}-{3:3}, at: i915_gem_evict_something+0x110/0x860 [i915]
  ...
   #46: ffff88811964d768 (reservation_ww_class_mutex){+.+.}-{3:3}, at: i915_gem_evict_something+0x110/0x860 [i915]
   #47: ffff88811964e0e8 (reservation_ww_class_mutex){+.+.}-{3:3}, at: i915_gem_evict_something+0x110/0x860 [i915]
  INFO: lockdep is turned off.

Fixing eviction to nest into ww_class_acquire is a high priority, but
it requires a rework of the entire driver, which can only be done one
step at a time.

As an intermediate solution, add an acquire context to
ww_mutex_trylock, which allows us to do proper nesting annotations on
the trylocks, making the above lockdep splat disappear.

This is also useful in regulator_lock_nested, which may avoid dropping
regulator_nesting_mutex in the uncontended path, so use it there.

TTM may be another user for this, where we could lock a buffer in a
fastpath with list locks held, without dropping all locks we hold.

[peterz: rework actual ww_mutex_trylock() implementations]
Signed-off-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/YUBGPdDDjKlxAuXJ@hirez.programming.kicks-ass.net
parent 6880fa6c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -248,7 +248,7 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
	if (ctx->trylock_only) {
		lockdep_assert_held(&ctx->ww_ctx);

		if (!ww_mutex_trylock(&lock->mutex))
		if (!ww_mutex_trylock(&lock->mutex, NULL))
			return -EBUSY;
		else
			return 0;
+1 −1
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ static inline int regulator_lock_nested(struct regulator_dev *rdev,

	mutex_lock(&regulator_nesting_mutex);

	if (ww_ctx || !ww_mutex_trylock(&rdev->mutex)) {
	if (!ww_mutex_trylock(&rdev->mutex, ww_ctx)) {
		if (rdev->mutex_owner == current)
			rdev->ref_cnt++;
		else
+1 −1
Original line number Diff line number Diff line
@@ -173,7 +173,7 @@ static inline int dma_resv_lock_slow_interruptible(struct dma_resv *obj,
 */
static inline bool __must_check dma_resv_trylock(struct dma_resv *obj)
{
	return ww_mutex_trylock(&obj->lock);
	return ww_mutex_trylock(&obj->lock, NULL);
}

/**
+2 −13
Original line number Diff line number Diff line
@@ -28,12 +28,10 @@
#ifndef CONFIG_PREEMPT_RT
#define WW_MUTEX_BASE			mutex
#define ww_mutex_base_init(l,n,k)	__mutex_init(l,n,k)
#define ww_mutex_base_trylock(l)	mutex_trylock(l)
#define ww_mutex_base_is_locked(b)	mutex_is_locked((b))
#else
#define WW_MUTEX_BASE			rt_mutex
#define ww_mutex_base_init(l,n,k)	__rt_mutex_init(l,n,k)
#define ww_mutex_base_trylock(l)	rt_mutex_trylock(l)
#define ww_mutex_base_is_locked(b)	rt_mutex_base_is_locked(&(b)->rtmutex)
#endif

@@ -339,17 +337,8 @@ ww_mutex_lock_slow_interruptible(struct ww_mutex *lock,

extern void ww_mutex_unlock(struct ww_mutex *lock);

/**
 * ww_mutex_trylock - tries to acquire the w/w mutex without acquire context
 * @lock: mutex to lock
 *
 * Trylocks a mutex without acquire context, so no deadlock detection is
 * possible. Returns 1 if the mutex has been acquired successfully, 0 otherwise.
 */
static inline int __must_check ww_mutex_trylock(struct ww_mutex *lock)
{
	return ww_mutex_base_trylock(&lock->base);
}
extern int __must_check ww_mutex_trylock(struct ww_mutex *lock,
					 struct ww_acquire_ctx *ctx);

/***
 * ww_mutex_destroy - mark a w/w mutex unusable
+41 −0
Original line number Diff line number Diff line
@@ -94,6 +94,9 @@ static inline unsigned long __owner_flags(unsigned long owner)
	return owner & MUTEX_FLAGS;
}

/*
 * Returns: __mutex_owner(lock) on failure or NULL on success.
 */
static inline struct task_struct *__mutex_trylock_common(struct mutex *lock, bool handoff)
{
	unsigned long owner, curr = (unsigned long)current;
@@ -736,6 +739,44 @@ __ww_mutex_lock(struct mutex *lock, unsigned int state, unsigned int subclass,
	return __mutex_lock_common(lock, state, subclass, NULL, ip, ww_ctx, true);
}

/**
 * ww_mutex_trylock - tries to acquire the w/w mutex with optional acquire context
 * @ww: mutex to lock
 * @ww_ctx: optional w/w acquire context
 *
 * Trylocks a mutex with the optional acquire context; no deadlock detection is
 * possible. Returns 1 if the mutex has been acquired successfully, 0 otherwise.
 *
 * Unlike ww_mutex_lock, no deadlock handling is performed. However, if a @ctx is
 * specified, -EALREADY handling may happen in calls to ww_mutex_trylock.
 *
 * A mutex acquired with this function must be released with ww_mutex_unlock.
 */
int ww_mutex_trylock(struct ww_mutex *ww, struct ww_acquire_ctx *ww_ctx)
{
	if (!ww_ctx)
		return mutex_trylock(&ww->base);

	MUTEX_WARN_ON(ww->base.magic != &ww->base);

	/*
	 * Reset the wounded flag after a kill. No other process can
	 * race and wound us here, since they can't have a valid owner
	 * pointer if we don't have any locks held.
	 */
	if (ww_ctx->acquired == 0)
		ww_ctx->wounded = 0;

	if (__mutex_trylock(&ww->base)) {
		ww_mutex_set_context_fastpath(ww, ww_ctx);
		mutex_acquire_nest(&ww->base.dep_map, 0, 1, &ww_ctx->dep_map, _RET_IP_);
		return 1;
	}

	return 0;
}
EXPORT_SYMBOL(ww_mutex_trylock);

#ifdef CONFIG_DEBUG_LOCK_ALLOC
void __sched
mutex_lock_nested(struct mutex *lock, unsigned int subclass)
Loading