* [PATCH] debug workqueue flushing deadlocks with lockdep
@ 2007-07-04 19:34 Johannes Berg
2007-07-05 8:48 ` Ingo Molnar
2007-07-05 9:03 ` Peter Zijlstra
0 siblings, 2 replies; 4+ messages in thread
From: Johannes Berg @ 2007-07-04 19:34 UTC (permalink / raw)
To: Linux Kernel list
Cc: Ingo Molnar, Oleg Nesterov, Arjan van de Ven, Peter Zijlstra,
Thomas Sattler
This patch adds a fake lock to each workqueue in order to debug things
where you have something like
my_function() -> lock(); ...; flush_workqueue(); ...
vs
run_workqueue() -> my_work() -> ...; lock(); ...
which can obviously deadlock if my_work is scheduled when my_function()
is invoked (but hasn't locked yet.)
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
This should be uncontroversial but doesn't add any debugging
for the cancel_*_work() API.
include/linux/workqueue.h | 20 +++++++++++++++++---
kernel/workqueue.c | 19 ++++++++++++++++---
2 files changed, 33 insertions(+), 6 deletions(-)
--- linux-2.6-git.orig/kernel/workqueue.c 2007-07-04 18:55:19.009991984 +0200
+++ linux-2.6-git/kernel/workqueue.c 2007-07-04 21:29:59.412544144 +0200
@@ -61,6 +61,9 @@ struct workqueue_struct {
const char *name;
int singlethread;
int freezeable; /* Freeze threads during suspend */
+#ifdef CONFIG_LOCKDEP
+ struct lockdep_map lockdep_map;
+#endif
};
/* All the per-cpu workqueues on the system, for hotplug cpu to add/remove
@@ -257,7 +260,9 @@ static void run_workqueue(struct cpu_wor
BUG_ON(get_wq_data(work) != cwq);
work_clear_pending(work);
+ lock_acquire(&cwq->wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_);
f(work);
+ lock_release(&cwq->wq->lockdep_map, 1, _THIS_IP_);
if (unlikely(in_atomic() || lockdep_depth(current) > 0)) {
printk(KERN_ERR "BUG: workqueue leaked lock or atomic: "
@@ -376,6 +381,8 @@ void fastcall flush_workqueue(struct wor
int cpu;
might_sleep();
+ lock_acquire(&wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_);
+ lock_release(&wq->lockdep_map, 1, _THIS_IP_);
for_each_cpu_mask(cpu, *cpu_map)
flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu));
}
@@ -682,8 +689,10 @@ static void start_workqueue_thread(struc
}
}
-struct workqueue_struct *__create_workqueue(const char *name,
- int singlethread, int freezeable)
+struct workqueue_struct *__create_workqueue_key(const char *name,
+ int singlethread,
+ int freezeable,
+ struct lock_class_key *key)
{
struct workqueue_struct *wq;
struct cpu_workqueue_struct *cwq;
@@ -700,6 +709,7 @@ struct workqueue_struct *__create_workqu
}
wq->name = name;
+ lockdep_init_map(&wq->lockdep_map, name, key, 0);
wq->singlethread = singlethread;
wq->freezeable = freezeable;
INIT_LIST_HEAD(&wq->list);
@@ -728,7 +738,7 @@ struct workqueue_struct *__create_workqu
}
return wq;
}
-EXPORT_SYMBOL_GPL(__create_workqueue);
+EXPORT_SYMBOL_GPL(__create_workqueue_key);
static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
{
@@ -739,6 +749,9 @@ static void cleanup_workqueue_thread(str
if (cwq->thread == NULL)
return;
+ lock_acquire(&cwq->wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_);
+ lock_release(&cwq->wq->lockdep_map, 1, _THIS_IP_);
+
/*
* If the caller is CPU_DEAD the single flush_cpu_workqueue()
* is not enough, a concurrent flush_workqueue() can insert a
--- linux-2.6-git.orig/include/linux/workqueue.h 2007-07-04 18:55:19.045991984 +0200
+++ linux-2.6-git/include/linux/workqueue.h 2007-07-04 21:29:59.357544144 +0200
@@ -118,9 +118,23 @@ struct execute_work {
clear_bit(WORK_STRUCT_PENDING, work_data_bits(work))
-extern struct workqueue_struct *__create_workqueue(const char *name,
- int singlethread,
- int freezeable);
+extern struct workqueue_struct *
+__create_workqueue_key(const char *name, int singlethread,
+ int freezeable, struct lock_class_key *key);
+
+#ifdef CONFIG_LOCKDEP
+#define __create_workqueue(name, singlethread, freezeable) \
+({ \
+ static struct lock_class_key __key; \
+ \
+ __create_workqueue_key((name), (singlethread), \
+ (freezeable), &__key); \
+})
+#else
+#define __create_workqueue(name, singlethread, freezeable) \
+ __create_workqueue_key((name), (singlethread), (freezeable), NULL)
+#endif
+
#define create_workqueue(name) __create_workqueue((name), 0, 0)
#define create_freezeable_workqueue(name) __create_workqueue((name), 1, 1)
#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0)
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] debug workqueue flushing deadlocks with lockdep
2007-07-04 19:34 [PATCH] debug workqueue flushing deadlocks with lockdep Johannes Berg
@ 2007-07-05 8:48 ` Ingo Molnar
2007-07-05 8:52 ` Johannes Berg
2007-07-05 9:03 ` Peter Zijlstra
1 sibling, 1 reply; 4+ messages in thread
From: Ingo Molnar @ 2007-07-05 8:48 UTC (permalink / raw)
To: Johannes Berg
Cc: Linux Kernel list, Oleg Nesterov, Arjan van de Ven,
Peter Zijlstra, Thomas Sattler
* Johannes Berg <johannes@sipsolutions.net> wrote:
> This patch adds a fake lock to each workqueue in order to debug things
> where you have something like
>
> my_function() -> lock(); ...; flush_workqueue(); ...
> vs
> run_workqueue() -> my_work() -> ...; lock(); ...
>
> which can obviously deadlock if my_work is scheduled when my_function()
> is invoked (but hasn't locked yet.)
>
> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
i'm still impressed by the elegancy of your idea :)
Acked-by: Ingo Molnar <mingo@elte.hu>
btw., small patch format comment: it would be useful (in the future) to
send such combined patches as:
[patch 0/2] general description
[patch 1/2] first patch
[patch 2/2] second patch
Ingo
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] debug workqueue flushing deadlocks with lockdep
2007-07-05 8:48 ` Ingo Molnar
@ 2007-07-05 8:52 ` Johannes Berg
0 siblings, 0 replies; 4+ messages in thread
From: Johannes Berg @ 2007-07-05 8:52 UTC (permalink / raw)
To: Ingo Molnar
Cc: Linux Kernel list, Oleg Nesterov, Arjan van de Ven,
Peter Zijlstra, Thomas Sattler
[-- Attachment #1: Type: text/plain, Size: 494 bytes --]
On Thu, 2007-07-05 at 10:48 +0200, Ingo Molnar wrote:
> btw., small patch format comment: it would be useful (in the future) to
> send such combined patches as:
>
> [patch 0/2] general description
> [patch 1/2] first patch
> [patch 2/2] second patch
Sure, no problem. I usually do that automatically due to quilt but this
was just 2 out of 52 patches I have pending right now so... :) And akpm
says that 0/2 should only contain stuff not meant for the changelog.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] debug workqueue flushing deadlocks with lockdep
2007-07-04 19:34 [PATCH] debug workqueue flushing deadlocks with lockdep Johannes Berg
2007-07-05 8:48 ` Ingo Molnar
@ 2007-07-05 9:03 ` Peter Zijlstra
1 sibling, 0 replies; 4+ messages in thread
From: Peter Zijlstra @ 2007-07-05 9:03 UTC (permalink / raw)
To: Johannes Berg
Cc: Linux Kernel list, Ingo Molnar, Oleg Nesterov, Arjan van de Ven,
Thomas Sattler
On Wed, 2007-07-04 at 21:34 +0200, Johannes Berg wrote:
> This patch adds a fake lock to each workqueue in order to debug things
> where you have something like
>
> my_function() -> lock(); ...; flush_workqueue(); ...
> vs
> run_workqueue() -> my_work() -> ...; lock(); ...
>
> which can obviously deadlock if my_work is scheduled when my_function()
> is invoked (but hasn't locked yet.)
>
> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2007-07-05 9:03 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-04 19:34 [PATCH] debug workqueue flushing deadlocks with lockdep Johannes Berg
2007-07-05 8:48 ` Ingo Molnar
2007-07-05 8:52 ` Johannes Berg
2007-07-05 9:03 ` Peter Zijlstra
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox