* [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