mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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