locking/lockdep: Apply crossrelease to completions
Although wait_for_completion() and its family can cause deadlock, the lock correctness validator could not be applied to them until now, because things like complete() are usually called in a different context from the waiting context, which violates lockdep's assumption. Thanks to CONFIG_LOCKDEP_CROSSRELEASE, we can now apply the lockdep detector to those completion operations. Applied it. Signed-off-by: Byungchul Park <byungchul.park@lge.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: akpm@linux-foundation.org Cc: boqun.feng@gmail.com Cc: kernel-team@lge.com Cc: kirill@shutemov.name Cc: npiggin@gmail.com Cc: walken@google.com Cc: willy@infradead.org Link: http://lkml.kernel.org/r/1502089981-21272-10-git-send-email-byungchul.park@lge.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
383a4bc888
commit
cd8084f91c
3 changed files with 64 additions and 1 deletions
|
@ -9,6 +9,9 @@
|
|||
*/
|
||||
|
||||
#include <linux/wait.h>
|
||||
#ifdef CONFIG_LOCKDEP_COMPLETE
|
||||
#include <linux/lockdep.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct completion - structure used to maintain state for a "completion"
|
||||
|
@ -25,10 +28,50 @@
|
|||
struct completion {
|
||||
unsigned int done;
|
||||
wait_queue_head_t wait;
|
||||
#ifdef CONFIG_LOCKDEP_COMPLETE
|
||||
struct lockdep_map_cross map;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LOCKDEP_COMPLETE
|
||||
static inline void complete_acquire(struct completion *x)
|
||||
{
|
||||
lock_acquire_exclusive((struct lockdep_map *)&x->map, 0, 0, NULL, _RET_IP_);
|
||||
}
|
||||
|
||||
static inline void complete_release(struct completion *x)
|
||||
{
|
||||
lock_release((struct lockdep_map *)&x->map, 0, _RET_IP_);
|
||||
}
|
||||
|
||||
static inline void complete_release_commit(struct completion *x)
|
||||
{
|
||||
lock_commit_crosslock((struct lockdep_map *)&x->map);
|
||||
}
|
||||
|
||||
#define init_completion(x) \
|
||||
do { \
|
||||
static struct lock_class_key __key; \
|
||||
lockdep_init_map_crosslock((struct lockdep_map *)&(x)->map, \
|
||||
"(complete)" #x, \
|
||||
&__key, 0); \
|
||||
__init_completion(x); \
|
||||
} while (0)
|
||||
#else
|
||||
#define init_completion(x) __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) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LOCKDEP_COMPLETE
|
||||
#define COMPLETION_INITIALIZER(work) \
|
||||
{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait), \
|
||||
STATIC_CROSS_LOCKDEP_MAP_INIT("(complete)" #work, &(work)) }
|
||||
#else
|
||||
#define COMPLETION_INITIALIZER(work) \
|
||||
{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
|
||||
#endif
|
||||
|
||||
#define COMPLETION_INITIALIZER_ONSTACK(work) \
|
||||
({ init_completion(&work); work; })
|
||||
|
@ -70,7 +113,7 @@ struct completion {
|
|||
* This inline function will initialize a dynamically created completion
|
||||
* structure.
|
||||
*/
|
||||
static inline void init_completion(struct completion *x)
|
||||
static inline void __init_completion(struct completion *x)
|
||||
{
|
||||
x->done = 0;
|
||||
init_waitqueue_head(&x->wait);
|
||||
|
|
|
@ -32,6 +32,12 @@ void complete(struct completion *x)
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&x->wait.lock, flags);
|
||||
|
||||
/*
|
||||
* Perform commit of crossrelease here.
|
||||
*/
|
||||
complete_release_commit(x);
|
||||
|
||||
if (x->done != UINT_MAX)
|
||||
x->done++;
|
||||
__wake_up_locked(&x->wait, TASK_NORMAL, 1);
|
||||
|
@ -92,9 +98,14 @@ __wait_for_common(struct completion *x,
|
|||
{
|
||||
might_sleep();
|
||||
|
||||
complete_acquire(x);
|
||||
|
||||
spin_lock_irq(&x->wait.lock);
|
||||
timeout = do_wait_for_common(x, action, timeout, state);
|
||||
spin_unlock_irq(&x->wait.lock);
|
||||
|
||||
complete_release(x);
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
|
|
|
@ -1162,6 +1162,15 @@ config LOCKDEP_CROSSRELEASE
|
|||
such as page locks or completions can use the lock correctness
|
||||
detector, lockdep.
|
||||
|
||||
config LOCKDEP_COMPLETE
|
||||
bool "Lock debugging: allow completions to use deadlock detector"
|
||||
depends on PROVE_LOCKING
|
||||
select LOCKDEP_CROSSRELEASE
|
||||
default n
|
||||
help
|
||||
A deadlock caused by wait_for_completion() and complete() can be
|
||||
detected by lockdep using crossrelease feature.
|
||||
|
||||
config DEBUG_LOCKDEP
|
||||
bool "Lock dependency engine debugging"
|
||||
depends on DEBUG_KERNEL && LOCKDEP
|
||||
|
|
Loading…
Reference in a new issue