Skip to contentSkip to conversation

Add lockdep_assert_not_held()

PATCH In review Shuah Khan 3 participants 9 messages Feb 27, 2021 – Mar 1, 2021

Subsystems (4)
lore
Share & tools
lore

Shuah Khan

author
#0 lore raw

v3 cover Add lockdep_assert_not_held()

Some kernel functions must not be called holding a specific lock. Doing so could lead to locking problems. Currently these routines call lock_is_held() to check for lock hold followed by WARN_ON.

Adding a common lockdep interface will help reduce the duplication of this logic in the rest of the kernel.

Add lockdep_assert_not_held() to be used in these functions to detect incorrect calls while holding a lock.

lockdep_assert_not_held() provides the opposite functionality of lockdep_assert_held() which is used to assert calls that require holding a specific lock.

The need for lockdep_assert_not_held() came up in a discussion on ath10k patch. ath10k_drain_tx() and i915_vma_pin_ww() are examples of functions that can use lockdep_assert_not_held().

This patch series adds lockdep_assert_not_held() and uses it in the second patch in ath10k_drain_tx() function.

Changes since v2:
-- Fixed coding style issues
-- Patch 2 uses new lock states in __lock_is_held() and
lock_is_held_type

Patch 1 incorporates suggestions from Peter Zijlstra on v1 series to avoid misfires when lockdep_off() is employed.

Patch 2 Johannes Berg's suggestions as it make it easier to read and maintain the lock states. These are defines and a enum to avoid changes to lock_is_held_type() and lockdep_is_held() return types.

Patch 2 is a separate patch because it adds defines to lockdep.h and kernel/locking/lockdep.c now includes lockdep.h - decided make this a separate patch just in case issues related to header dependencies pop up. I can combine Patches 1&2 if that is preferred.

Patch 3 uses the new interface in ath10k_drain_tx() function. Added Kalle Valo's Ack from v1 for this change.

Tested on the mainline from yesterday.

Shuah Khan (3):
lockdep: add lockdep_assert_not_held()
lockdep: add lockdep lock state defines
ath10k: detect conf_mutex held ath10k_drain_tx() calls

 drivers/net/wireless/ath/ath10k/mac.c |  2 ++
 include/linux/lockdep.h               | 18 +++++++++++++++---
 kernel/locking/lockdep.c              | 15 ++++++++++-----
 3 files changed, 27 insertions(+), 8 deletions(-)
Signature · 7 lines
2.27.0


_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k
author
#3 lore raw

PATCH v3 3/3 ath10k: detect conf_mutex held ath10k_drain_tx() calls

ath10k_drain_tx() must not be called with conf_mutex held as workers can use that also. Add call to lockdep_assert_not_held() on conf_mutex to detect if conf_mutex is held by the caller.

The idea for this patch stemmed from coming across the comment block above the ath10k_drain_tx() while reviewing the conf_mutex holds during to debug the conf_mutex lock assert in ath10k_debug_fw_stats_request().

Adding detection to assert on conf_mutex hold will help detect incorrect usages that could lead to locking problems when async worker routines try to call this routine.

---
 drivers/net/wireless/ath/ath10k/mac.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/wireless/ath/ath10k/mac.c +2 -0
index bb6c5ee43ac0..5ce4f8d038b9 100644
@@ -4727,6 +4727,8 @@ static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
 /* Must not be called with conf_mutex held as workers can use that also. */
 void ath10k_drain_tx(struct ath10k *ar)
 {
+	lockdep_assert_not_held(&ar->conf_mutex);
+
 	/* make sure rcu-protected mac80211 tx path itself is drained */
 	synchronize_net();
 
Signature · 7 lines
2.27.0


_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k
author
#1 lore raw

PATCH v3 1/3 lockdep: add lockdep_assert_not_held()

In reply to Shuah Khan

Some kernel functions must be called without holding a specific lock. Add lockdep_assert_not_held() to be used in these functions to detect incorrect calls while holding a lock.

lockdep_assert_not_held() provides the opposite functionality of lockdep_assert_held() which is used to assert calls that require holding a specific lock.

Incorporates suggestions from Peter Zijlstra to avoid misfires when lockdep_off() is employed.

The need for lockdep_assert_not_held() came up in a discussion on ath10k patch. ath10k_drain_tx() and i915_vma_pin_ww() are examples of functions that can use lockdep_assert_not_held().

---
 include/linux/lockdep.h  | 11 ++++++++---
 kernel/locking/lockdep.c |  6 +++++-
 2 files changed, 13 insertions(+), 4 deletions(-)
include/linux/lockdep.h +8 -3
index 7b7ebf2e28ec..dbd9ea846b36 100644
@@ -301,8 +301,12 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
 
 #define lockdep_depth(tsk)	(debug_locks ? (tsk)->lockdep_depth : 0)
 
-#define lockdep_assert_held(l)	do {				\
-		WARN_ON(debug_locks && !lockdep_is_held(l));	\
+#define lockdep_assert_held(l)	do {					\
+		WARN_ON(debug_locks && lockdep_is_held(l) == 0);	\
+	} while (0)
+
+#define lockdep_assert_not_held(l)	do {				\
+		WARN_ON(debug_locks && lockdep_is_held(l) == 1);	\
 	} while (0)
 
 #define lockdep_assert_held_write(l)	do {			\
@@ -393,7 +397,8 @@ extern int lockdep_is_held(const void *);
 #define lockdep_is_held_type(l, r)		(1)
 
 #define lockdep_assert_held(l)			do { (void)(l); } while (0)
-#define lockdep_assert_held_write(l)	do { (void)(l); } while (0)
+#define lockdep_assert_not_held(l)		do { (void)(l); } while (0)
+#define lockdep_assert_held_write(l)		do { (void)(l); } while (0)
 #define lockdep_assert_held_read(l)		do { (void)(l); } while (0)
 #define lockdep_assert_held_once(l)		do { (void)(l); } while (0)
 
kernel/locking/lockdep.c +5 -1
index c6d0c1dc6253..818d0ceed3eb 100644
@@ -5539,8 +5539,12 @@ noinstr int lock_is_held_type(const struct lockdep_map *lock, int read)
 	unsigned long flags;
 	int ret = 0;
 
+	/*
+	 * avoid false negative lockdep_assert_held() and
+	 * lockdep_assert_not_held()
+	 */
 	if (unlikely(!lockdep_enabled()))
-		return 1; /* avoid false negative lockdep_assert_held() */
+		return -1;
 
 	raw_local_irq_save(flags);
 	check_flags(flags);
Signature · 7 lines
2.27.0


_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k
author
#2 lore raw

PATCH v3 2/3 lockdep: add lockdep lock state defines

In reply to Shuah Khan

Adds defines for lock state returns from lock_is_held_type() based on Johannes Berg's suggestions as it make it easier to read and maintain the lock states. These are defines and a enum to avoid changes to lock_is_held_type() and lockdep_is_held() return types.

Updates to lock_is_held_type() and __lock_is_held() to use the new defines.

---
 include/linux/lockdep.h  | 11 +++++++++--
 kernel/locking/lockdep.c | 11 ++++++-----
 2 files changed, 15 insertions(+), 7 deletions(-)
include/linux/lockdep.h +9 -2
index dbd9ea846b36..17805aac0e85 100644
@@ -268,6 +268,11 @@ extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
 
 extern void lock_release(struct lockdep_map *lock, unsigned long ip);
 
+/* lock_is_held_type() returns */
+#define LOCK_STATE_UNKNOWN	-1
+#define LOCK_STATE_NOT_HELD	0
+#define LOCK_STATE_HELD		1
+
 /*
  * Same "read" as for lock_acquire(), except -1 means any.
  */
@@ -302,11 +307,13 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
 #define lockdep_depth(tsk)	(debug_locks ? (tsk)->lockdep_depth : 0)
 
 #define lockdep_assert_held(l)	do {					\
-		WARN_ON(debug_locks && lockdep_is_held(l) == 0);	\
+		WARN_ON(debug_locks &&					\
+			lockdep_is_held(l) == LOCK_STATE_NOT_HELD);	\
 	} while (0)
 
 #define lockdep_assert_not_held(l)	do {				\
-		WARN_ON(debug_locks && lockdep_is_held(l) == 1);	\
+		WARN_ON(debug_locks &&					\
+			lockdep_is_held(l) == LOCK_STATE_HELD);		\
 	} while (0)
 
 #define lockdep_assert_held_write(l)	do {			\
kernel/locking/lockdep.c +6 -5
index 818d0ceed3eb..f5a8200f24f1 100644
@@ -54,6 +54,7 @@
 #include <linux/nmi.h>
 #include <linux/rcupdate.h>
 #include <linux/kprobes.h>
+#include <linux/lockdep.h>
 
 #include <asm/sections.h>
 
@@ -5252,13 +5253,13 @@ int __lock_is_held(const struct lockdep_map *lock, int read)
 
 		if (match_held_lock(hlock, lock)) {
 			if (read == -1 || hlock->read == read)
-				return 1;
+				return LOCK_STATE_HELD;
 
-			return 0;
+			return LOCK_STATE_NOT_HELD;
 		}
 	}
 
-	return 0;
+	return LOCK_STATE_NOT_HELD;
 }
 
 static struct pin_cookie __lock_pin_lock(struct lockdep_map *lock)
@@ -5537,14 +5538,14 @@ EXPORT_SYMBOL_GPL(lock_release);
 noinstr int lock_is_held_type(const struct lockdep_map *lock, int read)
 {
 	unsigned long flags;
-	int ret = 0;
+	int ret = LOCK_STATE_NOT_HELD;
 
 	/*
 	 * avoid false negative lockdep_assert_held() and
 	 * lockdep_assert_not_held()
 	 */
 	if (unlikely(!lockdep_enabled()))
-		return -1;
+		return LOCK_STATE_UNKNOWN;
 
 	raw_local_irq_save(flags);
 	check_flags(flags);
Signature · 7 lines
2.27.0


_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k
#5 lore raw
Quoted 7 lines · Peter Zijlstra
Quoted 4 lines · Shuah Khan

Shuah Khan (3):
lockdep: add lockdep_assert_not_held()
lockdep: add lockdep lock state defines
ath10k: detect conf_mutex held ath10k_drain_tx() calls

Thanks!

Via which tree should these go?

Signature · 8 lines
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k
maintainer
#6 lore raw
Quoted 11 lines · Kalle Valo
Quoted 7 lines · Peter Zijlstra
Quoted 4 lines · Shuah Khan

Shuah Khan (3):
lockdep: add lockdep_assert_not_held()
lockdep: add lockdep lock state defines
ath10k: detect conf_mutex held ath10k_drain_tx() calls

Thanks!

Via which tree should these go?

I've just queued the lot for locking/core, which will show up in tip when the robots don't hate on it.

Does that work for you?

_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k

#7 lore raw
Quoted 9 lines · Peter Zijlstra
Quoted 8 lines · Kalle Valo
Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Shuah Khan

Shuah Khan (3):
lockdep: add lockdep_assert_not_held()
lockdep: add lockdep lock state defines
ath10k: detect conf_mutex held ath10k_drain_tx() calls

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

Thanks!

Via which tree should these go?

I've just queued the lot for locking/core, which will show up in tip when the robots don't hate on it.

Does that work for you?

That's perfect, thanks! Just making sure that the patches don't get lost.

Signature · 8 lines
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k
author
#8 lore raw
Quoted 15 lines · Kalle Valo
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Kalle Valo
Quoted 7 lines · Peter Zijlstra
Quoted 4 lines · Shuah Khan

Shuah Khan (3):
lockdep: add lockdep_assert_not_held()
lockdep: add lockdep lock state defines
ath10k: detect conf_mutex held ath10k_drain_tx() calls

Thanks!

Via which tree should these go?

Quoted 8 lines
Quoted 4 lines

I've just queued the lot for locking/core, which will show up in tip when the robots don't hate on it.

Does that work for you?

That's perfect, thanks! Just making sure that the patches don't get lost.

Awesome. Thank you.

thanks,
-- Shuah

_______________________________________________
ath10k mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/ath10k