forked from mirrors/linux
		
	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
	
	 Byungchul Park
						Byungchul Park