public inbox for intel-gfx@lists.freedesktop.org
 help / color / mirror / Atom feed
* [RFC v2] locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context
@ 2017-10-13 15:42 Tvrtko Ursulin
  2017-10-13 17:23 ` ✓ Fi.CI.BAT: success for " Patchwork
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Tvrtko Ursulin @ 2017-10-13 15:42 UTC (permalink / raw)
  To: Intel-gfx
  Cc: Peter Zijlstra, Daniel Vetter, boqun.feng, david, linux-kernel,
	byungchul.park, oleg, Tejun Heo, Thomas Gleixner, Linus Torvalds,
	johannes

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

It looks like all completions as created by flush_workqueue map
into a single lock class which creates lockdep false positives.

Example of a false positive:

 [   20.805315] ======================================================
 [   20.805316] WARNING: possible circular locking dependency detected
 [   20.805319] 4.14.0-rc4-CI-CI_DRM_3225+ #1 Tainted: G     U
 [   20.805320] ------------------------------------------------------
 [   20.805322] kworker/6:1H/1438 is trying to acquire lock:
 [   20.805324]  (&mm->mmap_sem){++++}, at: [<ffffffffa01c8e01>] __i915_gem_userptr_get_pages_worker+0x141/0x240 [i915]
 [   20.805355] but now in release context of a crosslock acquired at the following:
 [   20.805357]  ((complete)&this_flusher.done){+.+.}, at: [<ffffffff8190b06d>] wait_for_completion+0x1d/0x20
 [   20.805363] which lock already depends on the new lock.
 [   20.805365]  the existing dependency chain (in reverse order) is:
 [   20.805367] -> #1 ((complete)&this_flusher.done){+.+.}:
 [   20.805372]        __lock_acquire+0x1420/0x15e0
 [   20.805374]        lock_acquire+0xb0/0x200
 [   20.805376]        wait_for_common+0x58/0x210
 [   20.805378]        wait_for_completion+0x1d/0x20
 [   20.805381]        flush_workqueue+0x1af/0x540
 [   20.805400]        i915_gem_userptr_mn_invalidate_range_start+0x13c/0x150 [i915]
 [   20.805404]        __mmu_notifier_invalidate_range_start+0x76/0xc0
 [   20.805406]        unmap_vmas+0x7d/0xa0
 [   20.805408]        unmap_region+0xae/0x110
 [   20.805410]        do_munmap+0x276/0x3f0
 [   20.805411]        vm_munmap+0x67/0x90
 [   20.805413]        SyS_munmap+0xe/0x20
 [   20.805415]        entry_SYSCALL_64_fastpath+0x1c/0xb1
 [   20.805416] -> #0 (&mm->mmap_sem){++++}:
 [   20.805419]        down_read+0x3e/0x70
 [   20.805435]        __i915_gem_userptr_get_pages_worker+0x141/0x240 [i915]
 [   20.805438]        process_one_work+0x233/0x660
 [   20.805440]        worker_thread+0x4e/0x3b0
 [   20.805441]        kthread+0x152/0x190
 [   20.805442] other info that might help us debug this:
 [   20.805445]  Possible unsafe locking scenario by crosslock:
 [   20.805447]        CPU0                    CPU1
 [   20.805448]        ----                    ----
 [   20.805449]   lock(&mm->mmap_sem);
 [   20.805451]   lock((complete)&this_flusher.done);
 [   20.805453]                                lock(&mm->mmap_sem);
 [   20.805455]                                unlock((complete)&this_flusher.done);
 [   20.805457] *** DEADLOCK ***
 [   20.805460] 2 locks held by kworker/6:1H/1438:
 [   20.805461]  #0:  (&(&pool->lock)->rlock){-.-.}, at: [<ffffffff8109c94c>] process_one_work+0x2dc/0x660
 [   20.805465]  #1:  (&x->wait#10){....}, at: [<ffffffff810cd69d>] complete+0x1d/0x60
 [   20.805469] stack backtrace:
 [   20.805472] CPU: 6 PID: 1438 Comm: kworker/6:1H Tainted: G     U          4.14.0-rc4-CI-CI_DRM_3225+ #1
 [   20.805474] Hardware name: MSI MS-7924/Z97M-G43(MS-7924), BIOS V1.12 02/15/2016
 [   20.805480] Call Trace:
 [   20.805483]  dump_stack+0x68/0x9f
 [   20.805486]  print_circular_bug+0x235/0x3c0
 [   20.805488]  ? HARDIRQ_verbose+0x10/0x10
 [   20.805490]  check_prev_add+0x430/0x840
 [   20.805492]  ? ret_from_fork+0x27/0x40
 [   20.805494]  lock_commit_crosslock+0x3ee/0x660
 [   20.805496]  ? lock_commit_crosslock+0x3ee/0x660
 [   20.805498]  complete+0x29/0x60
 [   20.805500]  pwq_dec_nr_in_flight+0x9c/0xa0
 [   20.805502]  ? _raw_spin_lock_irq+0x40/0x50
 [   20.805504]  process_one_work+0x335/0x660
 [   20.805506]  worker_thread+0x4e/0x3b0
 [   20.805508]  kthread+0x152/0x190
 [   20.805509]  ? process_one_work+0x660/0x660
 [   20.805511]  ? kthread_create_on_node+0x40/0x40
 [   20.805513]  ret_from_fork+0x27/0x40

Solution here is is to create a lockdep map for completions
together with the workqueue object, and allow the completions to
be initialized with an external (pointer) lockdep map. This map
is then used over the built-in lockdep_map structure.

This way the completion cross-release context is tied with it's
parent workqueue and should still catch all issues without being
susceptible to false positives.

v2: Simplify by always having a valid map pointer. (Chris Wilson)

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: linux-kernel@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: boqun.feng@gmail.com
Cc: byungchul.park@lge.com
Cc: david@fromorbit.com
Cc: johannes@sipsolutions.net
Cc: oleg@redhat.com
--
Borrowed the Cc list from a recent fix in the same area.
Apologies if it is too wide.
---
 include/linux/completion.h | 46 +++++++++++++++++++++++++++++-----------------
 include/linux/workqueue.h  | 26 ++++++++++++++++++++++----
 kernel/workqueue.c         | 20 +++++++++++++-------
 3 files changed, 64 insertions(+), 28 deletions(-)

diff --git a/include/linux/completion.h b/include/linux/completion.h
index cae5400022a3..de4ee32ede0f 100644
--- a/include/linux/completion.h
+++ b/include/linux/completion.h
@@ -29,24 +29,41 @@ struct completion {
 	unsigned int done;
 	wait_queue_head_t wait;
 #ifdef CONFIG_LOCKDEP_COMPLETIONS
+	struct lockdep_map_cross *pmap;
 	struct lockdep_map_cross map;
 #endif
 };
 
+/**
+ * init_completion - Initialize a dynamically allocated completion
+ * @x:  pointer to completion structure that is to be initialized
+ *
+ * This inline function will initialize a dynamically created completion
+ * structure.
+ */
+static inline void __init_completion(struct completion *x)
+{
+	x->done = 0;
+#ifdef CONFIG_LOCKDEP_COMPLETIONS
+	x->pmap = &x->map;
+#endif
+	init_waitqueue_head(&x->wait);
+}
+
 #ifdef CONFIG_LOCKDEP_COMPLETIONS
 static inline void complete_acquire(struct completion *x)
 {
-	lock_acquire_exclusive((struct lockdep_map *)&x->map, 0, 0, NULL, _RET_IP_);
+	lock_acquire_exclusive((struct lockdep_map *)x->pmap, 0, 0, NULL, _RET_IP_);
 }
 
 static inline void complete_release(struct completion *x)
 {
-	lock_release((struct lockdep_map *)&x->map, 0, _RET_IP_);
+	lock_release((struct lockdep_map *)x->pmap, 0, _RET_IP_);
 }
 
 static inline void complete_release_commit(struct completion *x)
 {
-	lock_commit_crosslock((struct lockdep_map *)&x->map);
+	lock_commit_crosslock((struct lockdep_map *)x->pmap);
 }
 
 #define init_completion(x)						\
@@ -57,8 +74,16 @@ do {									\
 			&__key, 0);					\
 	__init_completion(x);						\
 } while (0)
+
+static inline void init_completion_map(struct completion *x,
+				       struct lockdep_map_cross *map)
+{
+	__init_completion(x);
+	x->pmap = map;
+}
 #else
 #define init_completion(x) __init_completion(x)
+#define init_completion_map(x, m) __init_completion(x)
 static inline void complete_acquire(struct completion *x) {}
 static inline void complete_release(struct completion *x) {}
 static inline void complete_release_commit(struct completion *x) {}
@@ -66,7 +91,7 @@ static inline void complete_release_commit(struct completion *x) {}
 
 #ifdef CONFIG_LOCKDEP_COMPLETIONS
 #define COMPLETION_INITIALIZER(work) \
-	{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait), \
+	{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait), &(work).map, \
 	STATIC_CROSS_LOCKDEP_MAP_INIT("(complete)" #work, &(work)) }
 #else
 #define COMPLETION_INITIALIZER(work) \
@@ -107,19 +132,6 @@ static inline void complete_release_commit(struct completion *x) {}
 #endif
 
 /**
- * init_completion - Initialize a dynamically allocated completion
- * @x:  pointer to completion structure that is to be initialized
- *
- * This inline function will initialize a dynamically created completion
- * structure.
- */
-static inline void __init_completion(struct completion *x)
-{
-	x->done = 0;
-	init_waitqueue_head(&x->wait);
-}
-
-/**
  * reinit_completion - reinitialize a completion structure
  * @x:  pointer to completion structure that is to be reinitialized
  *
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 1c49431f3121..c6d801d47535 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -373,7 +373,9 @@ extern struct workqueue_struct *system_freezable_power_efficient_wq;
 
 extern struct workqueue_struct *
 __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
-	struct lock_class_key *key, const char *lock_name, ...) __printf(1, 6);
+		      struct lock_class_key *key, const char *lock_name,
+		      struct lock_class_key *c_key, const char *c_lock_name,
+		      ...) __printf(1, 8);
 
 /**
  * alloc_workqueue - allocate a workqueue
@@ -392,7 +394,21 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
  * RETURNS:
  * Pointer to the allocated workqueue on success, %NULL on failure.
  */
-#ifdef CONFIG_LOCKDEP
+#if defined(CONFIG_LOCKDEP_COMPLETIONS)
+#define alloc_workqueue(fmt, flags, max_active, args...)		\
+({									\
+	static struct lock_class_key __key, __c_key;			\
+	const char *__lock_name, *__c_lock_name;			\
+									\
+	__lock_name = #fmt#args;					\
+	__c_lock_name = #fmt#args"c";					\
+									\
+	__alloc_workqueue_key((fmt), (flags), (max_active),		\
+			      &__key, __lock_name,			\
+			      &__c_key, __c_lock_name,			\
+			      ##args);					\
+})
+#elif defined(CONFIG_LOCKDEP)
 #define alloc_workqueue(fmt, flags, max_active, args...)		\
 ({									\
 	static struct lock_class_key __key;				\
@@ -401,12 +417,14 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
 	__lock_name = #fmt#args;					\
 									\
 	__alloc_workqueue_key((fmt), (flags), (max_active),		\
-			      &__key, __lock_name, ##args);		\
+			      &__key, __lock_name,			\
+			      NULL, NULL,				\
+			      ##args);					\
 })
 #else
 #define alloc_workqueue(fmt, flags, max_active, args...)		\
 	__alloc_workqueue_key((fmt), (flags), (max_active),		\
-			      NULL, NULL, ##args)
+			      NULL, NULL, NULL, NULL, ##args)
 #endif
 
 /**
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 64d0edf428f8..c267fa7ad1ec 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -263,6 +263,9 @@ struct workqueue_struct {
 #ifdef CONFIG_LOCKDEP
 	struct lockdep_map	lockdep_map;
 #endif
+#ifdef CONFIG_LOCKDEP_COMPLETIONS
+	struct lockdep_map_cross completion_lockdep_map;
+#endif
 	char			name[WQ_NAME_LEN]; /* I: workqueue name */
 
 	/*
@@ -2611,13 +2614,14 @@ void flush_workqueue(struct workqueue_struct *wq)
 	struct wq_flusher this_flusher = {
 		.list = LIST_HEAD_INIT(this_flusher.list),
 		.flush_color = -1,
-		.done = COMPLETION_INITIALIZER_ONSTACK(this_flusher.done),
 	};
 	int next_color;
 
 	if (WARN_ON(!wq_online))
 		return;
 
+	init_completion_map(&this_flusher.done, &wq->completion_lockdep_map);
+
 	lock_map_acquire(&wq->lockdep_map);
 	lock_map_release(&wq->lockdep_map);
 
@@ -3962,11 +3966,11 @@ static int wq_clamp_max_active(int max_active, unsigned int flags,
 	return clamp_val(max_active, 1, lim);
 }
 
-struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
-					       unsigned int flags,
-					       int max_active,
-					       struct lock_class_key *key,
-					       const char *lock_name, ...)
+struct workqueue_struct *
+__alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
+		      struct lock_class_key *key, const char *lock_name,
+		      struct lock_class_key *c_key, const char *c_lock_name,
+		      ...)
 {
 	size_t tbl_size = 0;
 	va_list args;
@@ -4001,7 +4005,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
 			goto err_free_wq;
 	}
 
-	va_start(args, lock_name);
+	va_start(args, c_lock_name);
 	vsnprintf(wq->name, sizeof(wq->name), fmt, args);
 	va_end(args);
 
@@ -4019,6 +4023,8 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
 	INIT_LIST_HEAD(&wq->maydays);
 
 	lockdep_init_map(&wq->lockdep_map, lock_name, key, 0);
+	lockdep_init_map_crosslock((struct lockdep_map *)&wq->completion_lockdep_map,
+				   c_lock_name, c_key, 0);
 	INIT_LIST_HEAD(&wq->list);
 
 	if (alloc_and_link_pwqs(wq) < 0)
-- 
2.9.5

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* ✓ Fi.CI.BAT: success for locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context
  2017-10-13 15:42 [RFC v2] locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context Tvrtko Ursulin
@ 2017-10-13 17:23 ` Patchwork
  2017-10-13 23:42 ` [RFC v2] " Byungchul Park
  2017-10-14  0:58 ` ✓ Fi.CI.IGT: success for " Patchwork
  2 siblings, 0 replies; 4+ messages in thread
From: Patchwork @ 2017-10-13 17:23 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

== Series Details ==

Series: locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context
URL   : https://patchwork.freedesktop.org/series/31937/
State : success

== Summary ==

Series 31937v1 locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context
https://patchwork.freedesktop.org/api/1.0/series/31937/revisions/1/mbox/

Test chamelium:
        Subgroup dp-crc-fast:
                fail       -> PASS       (fi-kbl-7500u) fdo#102514
Test kms_cursor_legacy:
        Subgroup basic-busy-flip-before-cursor-legacy:
                pass       -> FAIL       (fi-gdg-551) fdo#102618
Test kms_pipe_crc_basic:
        Subgroup suspend-read-crc-pipe-b:
                pass       -> DMESG-WARN (fi-byt-n2820) fdo#101705

fdo#102514 https://bugs.freedesktop.org/show_bug.cgi?id=102514
fdo#102618 https://bugs.freedesktop.org/show_bug.cgi?id=102618
fdo#101705 https://bugs.freedesktop.org/show_bug.cgi?id=101705

fi-bdw-5557u     total:289  pass:268  dwarn:0   dfail:0   fail:0   skip:21  time:457s
fi-bdw-gvtdvm    total:289  pass:265  dwarn:0   dfail:0   fail:0   skip:24  time:474s
fi-blb-e6850     total:289  pass:223  dwarn:1   dfail:0   fail:0   skip:65  time:393s
fi-bsw-n3050     total:289  pass:243  dwarn:0   dfail:0   fail:0   skip:46  time:571s
fi-bwr-2160      total:289  pass:183  dwarn:0   dfail:0   fail:0   skip:106 time:285s
fi-bxt-dsi       total:289  pass:259  dwarn:0   dfail:0   fail:0   skip:30  time:525s
fi-bxt-j4205     total:289  pass:260  dwarn:0   dfail:0   fail:0   skip:29  time:528s
fi-byt-j1900     total:289  pass:253  dwarn:1   dfail:0   fail:0   skip:35  time:535s
fi-byt-n2820     total:289  pass:249  dwarn:1   dfail:0   fail:0   skip:39  time:522s
fi-cfl-s         total:289  pass:253  dwarn:4   dfail:0   fail:0   skip:32  time:565s
fi-elk-e7500     total:289  pass:229  dwarn:0   dfail:0   fail:0   skip:60  time:435s
fi-gdg-551       total:289  pass:177  dwarn:1   dfail:0   fail:2   skip:109 time:281s
fi-glk-1         total:289  pass:261  dwarn:0   dfail:0   fail:0   skip:28  time:604s
fi-hsw-4770r     total:289  pass:262  dwarn:0   dfail:0   fail:0   skip:27  time:436s
fi-ilk-650       total:289  pass:228  dwarn:0   dfail:0   fail:0   skip:61  time:467s
fi-ivb-3520m     total:289  pass:260  dwarn:0   dfail:0   fail:0   skip:29  time:501s
fi-ivb-3770      total:289  pass:260  dwarn:0   dfail:0   fail:0   skip:29  time:473s
fi-kbl-7500u     total:289  pass:264  dwarn:1   dfail:0   fail:0   skip:24  time:504s
fi-kbl-7567u     total:289  pass:269  dwarn:0   dfail:0   fail:0   skip:20  time:491s
fi-kbl-r         total:289  pass:262  dwarn:0   dfail:0   fail:0   skip:27  time:595s
fi-pnv-d510      total:289  pass:222  dwarn:1   dfail:0   fail:0   skip:66  time:651s
fi-skl-6260u     total:289  pass:269  dwarn:0   dfail:0   fail:0   skip:20  time:464s
fi-skl-6700hq    total:289  pass:263  dwarn:0   dfail:0   fail:0   skip:26  time:663s
fi-skl-6700k     total:289  pass:265  dwarn:0   dfail:0   fail:0   skip:24  time:533s
fi-skl-6770hq    total:289  pass:269  dwarn:0   dfail:0   fail:0   skip:20  time:524s
fi-skl-gvtdvm    total:289  pass:266  dwarn:0   dfail:0   fail:0   skip:23  time:470s
fi-snb-2520m     total:289  pass:250  dwarn:0   dfail:0   fail:0   skip:39  time:596s
fi-snb-2600      total:289  pass:249  dwarn:0   dfail:0   fail:0   skip:40  time:427s

005c15a2795854ab64b6ce63dcb099d2eea4a889 drm-tip: 2017y-10m-13d-15h-39m-54s UTC integration manifest
82265cc46333 locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_6027/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC v2] locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context
  2017-10-13 15:42 [RFC v2] locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context Tvrtko Ursulin
  2017-10-13 17:23 ` ✓ Fi.CI.BAT: success for " Patchwork
@ 2017-10-13 23:42 ` Byungchul Park
  2017-10-14  0:58 ` ✓ Fi.CI.IGT: success for " Patchwork
  2 siblings, 0 replies; 4+ messages in thread
From: Byungchul Park @ 2017-10-13 23:42 UTC (permalink / raw)
  To: Tvrtko Ursulin
  Cc: Intel-gfx, Tvrtko Ursulin, Daniel Vetter, Chris Wilson,
	linux-kernel@vger.kernel.org, Tejun Heo, Linus Torvalds,
	Peter Zijlstra, Thomas Gleixner, Boqun Feng,
	byungchul.park@lge.com, david, Johannes Berg, oleg, kernel-team

On Sat, Oct 14, 2017 at 12:42 AM, Tvrtko Ursulin <tursulin@ursulin.net> wrote:
> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>
> It looks like all completions as created by flush_workqueue map
> into a single lock class which creates lockdep false positives.

Hello,

It's already in progress. Please take a look at the following.

   https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1487015.html

Peter said he has no time to look at it atm. I believe that's why the
review has been
dragging on. We need to wait for him to be available.

Thanks,
Byungchul

> Example of a false positive:
>
>  [   20.805315] ======================================================
>  [   20.805316] WARNING: possible circular locking dependency detected
>  [   20.805319] 4.14.0-rc4-CI-CI_DRM_3225+ #1 Tainted: G     U
>  [   20.805320] ------------------------------------------------------
>  [   20.805322] kworker/6:1H/1438 is trying to acquire lock:
>  [   20.805324]  (&mm->mmap_sem){++++}, at: [<ffffffffa01c8e01>] __i915_gem_userptr_get_pages_worker+0x141/0x240 [i915]
>  [   20.805355] but now in release context of a crosslock acquired at the following:
>  [   20.805357]  ((complete)&this_flusher.done){+.+.}, at: [<ffffffff8190b06d>] wait_for_completion+0x1d/0x20
>  [   20.805363] which lock already depends on the new lock.
>  [   20.805365]  the existing dependency chain (in reverse order) is:
>  [   20.805367] -> #1 ((complete)&this_flusher.done){+.+.}:
>  [   20.805372]        __lock_acquire+0x1420/0x15e0
>  [   20.805374]        lock_acquire+0xb0/0x200
>  [   20.805376]        wait_for_common+0x58/0x210
>  [   20.805378]        wait_for_completion+0x1d/0x20
>  [   20.805381]        flush_workqueue+0x1af/0x540
>  [   20.805400]        i915_gem_userptr_mn_invalidate_range_start+0x13c/0x150 [i915]
>  [   20.805404]        __mmu_notifier_invalidate_range_start+0x76/0xc0
>  [   20.805406]        unmap_vmas+0x7d/0xa0
>  [   20.805408]        unmap_region+0xae/0x110
>  [   20.805410]        do_munmap+0x276/0x3f0
>  [   20.805411]        vm_munmap+0x67/0x90
>  [   20.805413]        SyS_munmap+0xe/0x20
>  [   20.805415]        entry_SYSCALL_64_fastpath+0x1c/0xb1
>  [   20.805416] -> #0 (&mm->mmap_sem){++++}:
>  [   20.805419]        down_read+0x3e/0x70
>  [   20.805435]        __i915_gem_userptr_get_pages_worker+0x141/0x240 [i915]
>  [   20.805438]        process_one_work+0x233/0x660
>  [   20.805440]        worker_thread+0x4e/0x3b0
>  [   20.805441]        kthread+0x152/0x190
>  [   20.805442] other info that might help us debug this:
>  [   20.805445]  Possible unsafe locking scenario by crosslock:
>  [   20.805447]        CPU0                    CPU1
>  [   20.805448]        ----                    ----
>  [   20.805449]   lock(&mm->mmap_sem);
>  [   20.805451]   lock((complete)&this_flusher.done);
>  [   20.805453]                                lock(&mm->mmap_sem);
>  [   20.805455]                                unlock((complete)&this_flusher.done);
>  [   20.805457] *** DEADLOCK ***
>  [   20.805460] 2 locks held by kworker/6:1H/1438:
>  [   20.805461]  #0:  (&(&pool->lock)->rlock){-.-.}, at: [<ffffffff8109c94c>] process_one_work+0x2dc/0x660
>  [   20.805465]  #1:  (&x->wait#10){....}, at: [<ffffffff810cd69d>] complete+0x1d/0x60
>  [   20.805469] stack backtrace:
>  [   20.805472] CPU: 6 PID: 1438 Comm: kworker/6:1H Tainted: G     U          4.14.0-rc4-CI-CI_DRM_3225+ #1
>  [   20.805474] Hardware name: MSI MS-7924/Z97M-G43(MS-7924), BIOS V1.12 02/15/2016
>  [   20.805480] Call Trace:
>  [   20.805483]  dump_stack+0x68/0x9f
>  [   20.805486]  print_circular_bug+0x235/0x3c0
>  [   20.805488]  ? HARDIRQ_verbose+0x10/0x10
>  [   20.805490]  check_prev_add+0x430/0x840
>  [   20.805492]  ? ret_from_fork+0x27/0x40
>  [   20.805494]  lock_commit_crosslock+0x3ee/0x660
>  [   20.805496]  ? lock_commit_crosslock+0x3ee/0x660
>  [   20.805498]  complete+0x29/0x60
>  [   20.805500]  pwq_dec_nr_in_flight+0x9c/0xa0
>  [   20.805502]  ? _raw_spin_lock_irq+0x40/0x50
>  [   20.805504]  process_one_work+0x335/0x660
>  [   20.805506]  worker_thread+0x4e/0x3b0
>  [   20.805508]  kthread+0x152/0x190
>  [   20.805509]  ? process_one_work+0x660/0x660
>  [   20.805511]  ? kthread_create_on_node+0x40/0x40
>  [   20.805513]  ret_from_fork+0x27/0x40
>
> Solution here is is to create a lockdep map for completions
> together with the workqueue object, and allow the completions to
> be initialized with an external (pointer) lockdep map. This map
> is then used over the built-in lockdep_map structure.
>
> This way the completion cross-release context is tied with it's
> parent workqueue and should still catch all issues without being
> susceptible to false positives.
>
> v2: Simplify by always having a valid map pointer. (Chris Wilson)
>
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: linux-kernel@vger.kernel.org
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: boqun.feng@gmail.com
> Cc: byungchul.park@lge.com
> Cc: david@fromorbit.com
> Cc: johannes@sipsolutions.net
> Cc: oleg@redhat.com
> --
> Borrowed the Cc list from a recent fix in the same area.
> Apologies if it is too wide.
> ---
>  include/linux/completion.h | 46 +++++++++++++++++++++++++++++-----------------
>  include/linux/workqueue.h  | 26 ++++++++++++++++++++++----
>  kernel/workqueue.c         | 20 +++++++++++++-------
>  3 files changed, 64 insertions(+), 28 deletions(-)
>
> diff --git a/include/linux/completion.h b/include/linux/completion.h
> index cae5400022a3..de4ee32ede0f 100644
> --- a/include/linux/completion.h
> +++ b/include/linux/completion.h
> @@ -29,24 +29,41 @@ struct completion {
>         unsigned int done;
>         wait_queue_head_t wait;
>  #ifdef CONFIG_LOCKDEP_COMPLETIONS
> +       struct lockdep_map_cross *pmap;
>         struct lockdep_map_cross map;
>  #endif
>  };
>
> +/**
> + * init_completion - Initialize a dynamically allocated completion
> + * @x:  pointer to completion structure that is to be initialized
> + *
> + * This inline function will initialize a dynamically created completion
> + * structure.
> + */
> +static inline void __init_completion(struct completion *x)
> +{
> +       x->done = 0;
> +#ifdef CONFIG_LOCKDEP_COMPLETIONS
> +       x->pmap = &x->map;
> +#endif
> +       init_waitqueue_head(&x->wait);
> +}
> +
>  #ifdef CONFIG_LOCKDEP_COMPLETIONS
>  static inline void complete_acquire(struct completion *x)
>  {
> -       lock_acquire_exclusive((struct lockdep_map *)&x->map, 0, 0, NULL, _RET_IP_);
> +       lock_acquire_exclusive((struct lockdep_map *)x->pmap, 0, 0, NULL, _RET_IP_);
>  }
>
>  static inline void complete_release(struct completion *x)
>  {
> -       lock_release((struct lockdep_map *)&x->map, 0, _RET_IP_);
> +       lock_release((struct lockdep_map *)x->pmap, 0, _RET_IP_);
>  }
>
>  static inline void complete_release_commit(struct completion *x)
>  {
> -       lock_commit_crosslock((struct lockdep_map *)&x->map);
> +       lock_commit_crosslock((struct lockdep_map *)x->pmap);
>  }
>
>  #define init_completion(x)                                             \
> @@ -57,8 +74,16 @@ do {                                                                 \
>                         &__key, 0);                                     \
>         __init_completion(x);                                           \
>  } while (0)
> +
> +static inline void init_completion_map(struct completion *x,
> +                                      struct lockdep_map_cross *map)
> +{
> +       __init_completion(x);
> +       x->pmap = map;
> +}
>  #else
>  #define init_completion(x) __init_completion(x)
> +#define init_completion_map(x, m) __init_completion(x)
>  static inline void complete_acquire(struct completion *x) {}
>  static inline void complete_release(struct completion *x) {}
>  static inline void complete_release_commit(struct completion *x) {}
> @@ -66,7 +91,7 @@ static inline void complete_release_commit(struct completion *x) {}
>
>  #ifdef CONFIG_LOCKDEP_COMPLETIONS
>  #define COMPLETION_INITIALIZER(work) \
> -       { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait), \
> +       { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait), &(work).map, \
>         STATIC_CROSS_LOCKDEP_MAP_INIT("(complete)" #work, &(work)) }
>  #else
>  #define COMPLETION_INITIALIZER(work) \
> @@ -107,19 +132,6 @@ static inline void complete_release_commit(struct completion *x) {}
>  #endif
>
>  /**
> - * init_completion - Initialize a dynamically allocated completion
> - * @x:  pointer to completion structure that is to be initialized
> - *
> - * This inline function will initialize a dynamically created completion
> - * structure.
> - */
> -static inline void __init_completion(struct completion *x)
> -{
> -       x->done = 0;
> -       init_waitqueue_head(&x->wait);
> -}
> -
> -/**
>   * reinit_completion - reinitialize a completion structure
>   * @x:  pointer to completion structure that is to be reinitialized
>   *
> diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
> index 1c49431f3121..c6d801d47535 100644
> --- a/include/linux/workqueue.h
> +++ b/include/linux/workqueue.h
> @@ -373,7 +373,9 @@ extern struct workqueue_struct *system_freezable_power_efficient_wq;
>
>  extern struct workqueue_struct *
>  __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
> -       struct lock_class_key *key, const char *lock_name, ...) __printf(1, 6);
> +                     struct lock_class_key *key, const char *lock_name,
> +                     struct lock_class_key *c_key, const char *c_lock_name,
> +                     ...) __printf(1, 8);
>
>  /**
>   * alloc_workqueue - allocate a workqueue
> @@ -392,7 +394,21 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
>   * RETURNS:
>   * Pointer to the allocated workqueue on success, %NULL on failure.
>   */
> -#ifdef CONFIG_LOCKDEP
> +#if defined(CONFIG_LOCKDEP_COMPLETIONS)
> +#define alloc_workqueue(fmt, flags, max_active, args...)               \
> +({                                                                     \
> +       static struct lock_class_key __key, __c_key;                    \
> +       const char *__lock_name, *__c_lock_name;                        \
> +                                                                       \
> +       __lock_name = #fmt#args;                                        \
> +       __c_lock_name = #fmt#args"c";                                   \
> +                                                                       \
> +       __alloc_workqueue_key((fmt), (flags), (max_active),             \
> +                             &__key, __lock_name,                      \
> +                             &__c_key, __c_lock_name,                  \
> +                             ##args);                                  \
> +})
> +#elif defined(CONFIG_LOCKDEP)
>  #define alloc_workqueue(fmt, flags, max_active, args...)               \
>  ({                                                                     \
>         static struct lock_class_key __key;                             \
> @@ -401,12 +417,14 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
>         __lock_name = #fmt#args;                                        \
>                                                                         \
>         __alloc_workqueue_key((fmt), (flags), (max_active),             \
> -                             &__key, __lock_name, ##args);             \
> +                             &__key, __lock_name,                      \
> +                             NULL, NULL,                               \
> +                             ##args);                                  \
>  })
>  #else
>  #define alloc_workqueue(fmt, flags, max_active, args...)               \
>         __alloc_workqueue_key((fmt), (flags), (max_active),             \
> -                             NULL, NULL, ##args)
> +                             NULL, NULL, NULL, NULL, ##args)
>  #endif
>
>  /**
> diff --git a/kernel/workqueue.c b/kernel/workqueue.c
> index 64d0edf428f8..c267fa7ad1ec 100644
> --- a/kernel/workqueue.c
> +++ b/kernel/workqueue.c
> @@ -263,6 +263,9 @@ struct workqueue_struct {
>  #ifdef CONFIG_LOCKDEP
>         struct lockdep_map      lockdep_map;
>  #endif
> +#ifdef CONFIG_LOCKDEP_COMPLETIONS
> +       struct lockdep_map_cross completion_lockdep_map;
> +#endif
>         char                    name[WQ_NAME_LEN]; /* I: workqueue name */
>
>         /*
> @@ -2611,13 +2614,14 @@ void flush_workqueue(struct workqueue_struct *wq)
>         struct wq_flusher this_flusher = {
>                 .list = LIST_HEAD_INIT(this_flusher.list),
>                 .flush_color = -1,
> -               .done = COMPLETION_INITIALIZER_ONSTACK(this_flusher.done),
>         };
>         int next_color;
>
>         if (WARN_ON(!wq_online))
>                 return;
>
> +       init_completion_map(&this_flusher.done, &wq->completion_lockdep_map);
> +
>         lock_map_acquire(&wq->lockdep_map);
>         lock_map_release(&wq->lockdep_map);
>
> @@ -3962,11 +3966,11 @@ static int wq_clamp_max_active(int max_active, unsigned int flags,
>         return clamp_val(max_active, 1, lim);
>  }
>
> -struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
> -                                              unsigned int flags,
> -                                              int max_active,
> -                                              struct lock_class_key *key,
> -                                              const char *lock_name, ...)
> +struct workqueue_struct *
> +__alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
> +                     struct lock_class_key *key, const char *lock_name,
> +                     struct lock_class_key *c_key, const char *c_lock_name,
> +                     ...)
>  {
>         size_t tbl_size = 0;
>         va_list args;
> @@ -4001,7 +4005,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
>                         goto err_free_wq;
>         }
>
> -       va_start(args, lock_name);
> +       va_start(args, c_lock_name);
>         vsnprintf(wq->name, sizeof(wq->name), fmt, args);
>         va_end(args);
>
> @@ -4019,6 +4023,8 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
>         INIT_LIST_HEAD(&wq->maydays);
>
>         lockdep_init_map(&wq->lockdep_map, lock_name, key, 0);
> +       lockdep_init_map_crosslock((struct lockdep_map *)&wq->completion_lockdep_map,
> +                                  c_lock_name, c_key, 0);
>         INIT_LIST_HEAD(&wq->list);
>
>         if (alloc_and_link_pwqs(wq) < 0)
> --
> 2.9.5
>



-- 
Thanks,
Byungchul

^ permalink raw reply	[flat|nested] 4+ messages in thread

* ✓ Fi.CI.IGT: success for locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context
  2017-10-13 15:42 [RFC v2] locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context Tvrtko Ursulin
  2017-10-13 17:23 ` ✓ Fi.CI.BAT: success for " Patchwork
  2017-10-13 23:42 ` [RFC v2] " Byungchul Park
@ 2017-10-14  0:58 ` Patchwork
  2 siblings, 0 replies; 4+ messages in thread
From: Patchwork @ 2017-10-14  0:58 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

== Series Details ==

Series: locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context
URL   : https://patchwork.freedesktop.org/series/31937/
State : success

== Summary ==

shard-hsw        total:2553 pass:1441 dwarn:1   dfail:0   fail:8   skip:1103 time:9677s

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_6027/shards.html
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2017-10-14  0:58 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-10-13 15:42 [RFC v2] locking/lockdep: Don't bunch up all flush_workqueue callers into a single completion cross-release context Tvrtko Ursulin
2017-10-13 17:23 ` ✓ Fi.CI.BAT: success for " Patchwork
2017-10-13 23:42 ` [RFC v2] " Byungchul Park
2017-10-14  0:58 ` ✓ Fi.CI.IGT: success for " Patchwork

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox