Skip to content

Commit 669de8b

Browse files
bvanasscheIngo Molnar
authored andcommitted
kernel/workqueue: Use dynamic lockdep keys for workqueues
The following commit: 87915ad ("workqueue: re-add lockdep dependencies for flushing") improved deadlock checking in the workqueue implementation. Unfortunately that patch also introduced a few false positive lockdep complaints. This patch suppresses these false positives by allocating the workqueue mutex lockdep key dynamically. An example of a false positive lockdep complaint suppressed by this patch can be found below. The root cause of the lockdep complaint shown below is that the direct I/O code can call alloc_workqueue() from inside a work item created by another alloc_workqueue() call and that both workqueues share the same lockdep key. This patch avoids that that lockdep complaint is triggered by allocating the work queue lockdep keys dynamically. In other words, this patch guarantees that a unique lockdep key is associated with each work queue mutex. ====================================================== WARNING: possible circular locking dependency detected 4.19.0-dbg+ #1 Not tainted fio/4129 is trying to acquire lock: 00000000a01cfe1a ((wq_completion)"dio/%s"sb->s_id){+.+.}, at: flush_workqueue+0xd0/0x970 but task is already holding lock: 00000000a0acecf9 (&sb->s_type->i_mutex_key#14){+.+.}, at: ext4_file_write_iter+0x154/0x710 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (&sb->s_type->i_mutex_key#14){+.+.}: down_write+0x3d/0x80 __generic_file_fsync+0x77/0xf0 ext4_sync_file+0x3c9/0x780 vfs_fsync_range+0x66/0x100 dio_complete+0x2f5/0x360 dio_aio_complete_work+0x1c/0x20 process_one_work+0x481/0x9f0 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30 -> #1 ((work_completion)(&dio->complete_work)){+.+.}: process_one_work+0x447/0x9f0 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30 -> #0 ((wq_completion)"dio/%s"sb->s_id){+.+.}: lock_acquire+0xc5/0x200 flush_workqueue+0xf3/0x970 drain_workqueue+0xec/0x220 destroy_workqueue+0x23/0x350 sb_init_dio_done_wq+0x6a/0x80 do_blockdev_direct_IO+0x1f33/0x4be0 __blockdev_direct_IO+0x79/0x86 ext4_direct_IO+0x5df/0xbb0 generic_file_direct_write+0x119/0x220 __generic_file_write_iter+0x131/0x2d0 ext4_file_write_iter+0x3fa/0x710 aio_write+0x235/0x330 io_submit_one+0x510/0xeb0 __x64_sys_io_submit+0x122/0x340 do_syscall_64+0x71/0x220 entry_SYSCALL_64_after_hwframe+0x49/0xbe other info that might help us debug this: Chain exists of: (wq_completion)"dio/%s"sb->s_id --> (work_completion)(&dio->complete_work) --> &sb->s_type->i_mutex_key#14 Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&sb->s_type->i_mutex_key#14); lock((work_completion)(&dio->complete_work)); lock(&sb->s_type->i_mutex_key#14); lock((wq_completion)"dio/%s"sb->s_id); *** DEADLOCK *** 1 lock held by fio/4129: #0: 00000000a0acecf9 (&sb->s_type->i_mutex_key#14){+.+.}, at: ext4_file_write_iter+0x154/0x710 stack backtrace: CPU: 3 PID: 4129 Comm: fio Not tainted 4.19.0-dbg+ #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 Call Trace: dump_stack+0x86/0xc5 print_circular_bug.isra.32+0x20a/0x218 __lock_acquire+0x1c68/0x1cf0 lock_acquire+0xc5/0x200 flush_workqueue+0xf3/0x970 drain_workqueue+0xec/0x220 destroy_workqueue+0x23/0x350 sb_init_dio_done_wq+0x6a/0x80 do_blockdev_direct_IO+0x1f33/0x4be0 __blockdev_direct_IO+0x79/0x86 ext4_direct_IO+0x5df/0xbb0 generic_file_direct_write+0x119/0x220 __generic_file_write_iter+0x131/0x2d0 ext4_file_write_iter+0x3fa/0x710 aio_write+0x235/0x330 io_submit_one+0x510/0xeb0 __x64_sys_io_submit+0x122/0x340 do_syscall_64+0x71/0x220 entry_SYSCALL_64_after_hwframe+0x49/0xbe Signed-off-by: Bart Van Assche <bvanassche@acm.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Johannes Berg <johannes.berg@intel.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Tejun Heo <tj@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Waiman Long <longman@redhat.com> Cc: Will Deacon <will.deacon@arm.com> Link: https://lkml.kernel.org/r/20190214230058.196511-20-bvanassche@acm.org [ Reworked the changelog a bit. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 108c148 commit 669de8b

File tree

2 files changed

+54
-33
lines changed

2 files changed

+54
-33
lines changed

include/linux/workqueue.h

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -390,43 +390,23 @@ extern struct workqueue_struct *system_freezable_wq;
390390
extern struct workqueue_struct *system_power_efficient_wq;
391391
extern struct workqueue_struct *system_freezable_power_efficient_wq;
392392

393-
extern struct workqueue_struct *
394-
__alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active,
395-
struct lock_class_key *key, const char *lock_name, ...) __printf(1, 6);
396-
397393
/**
398394
* alloc_workqueue - allocate a workqueue
399395
* @fmt: printf format for the name of the workqueue
400396
* @flags: WQ_* flags
401397
* @max_active: max in-flight work items, 0 for default
402-
* @args...: args for @fmt
398+
* remaining args: args for @fmt
403399
*
404400
* Allocate a workqueue with the specified parameters. For detailed
405401
* information on WQ_* flags, please refer to
406402
* Documentation/core-api/workqueue.rst.
407403
*
408-
* The __lock_name macro dance is to guarantee that single lock_class_key
409-
* doesn't end up with different namesm, which isn't allowed by lockdep.
410-
*
411404
* RETURNS:
412405
* Pointer to the allocated workqueue on success, %NULL on failure.
413406
*/
414-
#ifdef CONFIG_LOCKDEP
415-
#define alloc_workqueue(fmt, flags, max_active, args...) \
416-
({ \
417-
static struct lock_class_key __key; \
418-
const char *__lock_name; \
419-
\
420-
__lock_name = "(wq_completion)"#fmt#args; \
421-
\
422-
__alloc_workqueue_key((fmt), (flags), (max_active), \
423-
&__key, __lock_name, ##args); \
424-
})
425-
#else
426-
#define alloc_workqueue(fmt, flags, max_active, args...) \
427-
__alloc_workqueue_key((fmt), (flags), (max_active), \
428-
NULL, NULL, ##args)
429-
#endif
407+
struct workqueue_struct *alloc_workqueue(const char *fmt,
408+
unsigned int flags,
409+
int max_active, ...);
430410

431411
/**
432412
* alloc_ordered_workqueue - allocate an ordered workqueue

kernel/workqueue.c

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ struct workqueue_struct {
259259
struct wq_device *wq_dev; /* I: for sysfs interface */
260260
#endif
261261
#ifdef CONFIG_LOCKDEP
262+
char *lock_name;
263+
struct lock_class_key key;
262264
struct lockdep_map lockdep_map;
263265
#endif
264266
char name[WQ_NAME_LEN]; /* I: workqueue name */
@@ -3337,11 +3339,49 @@ static int init_worker_pool(struct worker_pool *pool)
33373339
return 0;
33383340
}
33393341

3342+
#ifdef CONFIG_LOCKDEP
3343+
static void wq_init_lockdep(struct workqueue_struct *wq)
3344+
{
3345+
char *lock_name;
3346+
3347+
lockdep_register_key(&wq->key);
3348+
lock_name = kasprintf(GFP_KERNEL, "%s%s", "(wq_completion)", wq->name);
3349+
if (!lock_name)
3350+
lock_name = wq->name;
3351+
lockdep_init_map(&wq->lockdep_map, lock_name, &wq->key, 0);
3352+
}
3353+
3354+
static void wq_unregister_lockdep(struct workqueue_struct *wq)
3355+
{
3356+
lockdep_unregister_key(&wq->key);
3357+
}
3358+
3359+
static void wq_free_lockdep(struct workqueue_struct *wq)
3360+
{
3361+
if (wq->lock_name != wq->name)
3362+
kfree(wq->lock_name);
3363+
}
3364+
#else
3365+
static void wq_init_lockdep(struct workqueue_struct *wq)
3366+
{
3367+
}
3368+
3369+
static void wq_unregister_lockdep(struct workqueue_struct *wq)
3370+
{
3371+
}
3372+
3373+
static void wq_free_lockdep(struct workqueue_struct *wq)
3374+
{
3375+
}
3376+
#endif
3377+
33403378
static void rcu_free_wq(struct rcu_head *rcu)
33413379
{
33423380
struct workqueue_struct *wq =
33433381
container_of(rcu, struct workqueue_struct, rcu);
33443382

3383+
wq_free_lockdep(wq);
3384+
33453385
if (!(wq->flags & WQ_UNBOUND))
33463386
free_percpu(wq->cpu_pwqs);
33473387
else
@@ -3532,8 +3572,10 @@ static void pwq_unbound_release_workfn(struct work_struct *work)
35323572
* If we're the last pwq going away, @wq is already dead and no one
35333573
* is gonna access it anymore. Schedule RCU free.
35343574
*/
3535-
if (is_last)
3575+
if (is_last) {
3576+
wq_unregister_lockdep(wq);
35363577
call_rcu(&wq->rcu, rcu_free_wq);
3578+
}
35373579
}
35383580

35393581
/**
@@ -4067,11 +4109,9 @@ static int init_rescuer(struct workqueue_struct *wq)
40674109
return 0;
40684110
}
40694111

4070-
struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
4071-
unsigned int flags,
4072-
int max_active,
4073-
struct lock_class_key *key,
4074-
const char *lock_name, ...)
4112+
struct workqueue_struct *alloc_workqueue(const char *fmt,
4113+
unsigned int flags,
4114+
int max_active, ...)
40754115
{
40764116
size_t tbl_size = 0;
40774117
va_list args;
@@ -4106,7 +4146,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
41064146
goto err_free_wq;
41074147
}
41084148

4109-
va_start(args, lock_name);
4149+
va_start(args, max_active);
41104150
vsnprintf(wq->name, sizeof(wq->name), fmt, args);
41114151
va_end(args);
41124152

@@ -4123,7 +4163,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
41234163
INIT_LIST_HEAD(&wq->flusher_overflow);
41244164
INIT_LIST_HEAD(&wq->maydays);
41254165

4126-
lockdep_init_map(&wq->lockdep_map, lock_name, key, 0);
4166+
wq_init_lockdep(wq);
41274167
INIT_LIST_HEAD(&wq->list);
41284168

41294169
if (alloc_and_link_pwqs(wq) < 0)
@@ -4161,7 +4201,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
41614201
destroy_workqueue(wq);
41624202
return NULL;
41634203
}
4164-
EXPORT_SYMBOL_GPL(__alloc_workqueue_key);
4204+
EXPORT_SYMBOL_GPL(alloc_workqueue);
41654205

41664206
/**
41674207
* destroy_workqueue - safely terminate a workqueue
@@ -4214,6 +4254,7 @@ void destroy_workqueue(struct workqueue_struct *wq)
42144254
kthread_stop(wq->rescuer->task);
42154255

42164256
if (!(wq->flags & WQ_UNBOUND)) {
4257+
wq_unregister_lockdep(wq);
42174258
/*
42184259
* The base ref is never dropped on per-cpu pwqs. Directly
42194260
* schedule RCU free.

0 commit comments

Comments
 (0)