Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd8084f9 authored by Byungchul Park's avatar Byungchul Park Committed by Ingo Molnar
Browse files

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: default avatarByungchul Park <byungchul.park@lge.com>
Signed-off-by: default avatarPeter 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: default avatarIngo Molnar <mingo@kernel.org>
parent 383a4bc8
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -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);
+11 −0
Original line number Diff line number Diff line
@@ -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;
}

+9 −0
Original line number Diff line number Diff line
@@ -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