]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
rcu: Equip sleepable RCU with lockdep dependency graph checks
authorBoqun Feng <boqun.feng@gmail.com>
Fri, 13 Jan 2023 06:59:54 +0000 (22:59 -0800)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Fri, 13 Jan 2023 09:26:50 +0000 (09:26 +0000)
Although all flavors of RCU are annotated correctly with lockdep as
recursive read locks, their 'check' parameter of lock_acquire() is
unset. It means that RCU read locks are not added into the lockdep
dependency graph therefore deadlock detection based on dependency graph
won't catch deadlock caused by RCU. This is fine for "non-sleepable" RCU
flavors since wait-context detection and other context based detection
can catch these deadlocks. However for sleepable RCU, this is limited.

Actually we can detect the deadlocks caused by SRCU by 1) making
srcu_read_lock() a 'check'ed recursive read lock and 2) making
synchronize_srcu() a empty write lock critical section. Even better,
with the newly introduced lock_sync(), we can avoid false positives
about irq-unsafe/safe. So do it.

Note that NMI safe SRCU read side critical sections are currently not
annonated, since step-by-step approach can help us deal with
false-positives. These may be annotated in the future.

Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
include/linux/srcu.h
kernel/rcu/srcutiny.c
kernel/rcu/srcutree.c

index 9b9d0bbf1d3cf68ade40ad4887ccc86f8a38054a..a1595f8c515563858fcadb00d7cd3f5bddf2bd2c 100644 (file)
@@ -102,6 +102,21 @@ static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
        return lock_is_held(&ssp->dep_map);
 }
 
+static inline void srcu_lock_acquire(struct lockdep_map *map)
+{
+       lock_map_acquire_read(map);
+}
+
+static inline void srcu_lock_release(struct lockdep_map *map)
+{
+       lock_map_release(map);
+}
+
+static inline void srcu_lock_sync(struct lockdep_map *map)
+{
+       lock_map_sync(map);
+}
+
 #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
 static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
@@ -109,6 +124,10 @@ static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
        return 1;
 }
 
+#define srcu_lock_acquire(m) do { } while (0)
+#define srcu_lock_release(m) do { } while (0)
+#define srcu_lock_sync(m) do { } while (0)
+
 #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
 #define SRCU_NMI_UNKNOWN       0x0
@@ -182,7 +201,7 @@ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp)
 
        srcu_check_nmi_safety(ssp, false);
        retval = __srcu_read_lock(ssp);
-       rcu_lock_acquire(&(ssp)->dep_map);
+       srcu_lock_acquire(&(ssp)->dep_map);
        return retval;
 }
 
@@ -226,7 +245,7 @@ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx)
 {
        WARN_ON_ONCE(idx & ~0x1);
        srcu_check_nmi_safety(ssp, false);
-       rcu_lock_release(&(ssp)->dep_map);
+       srcu_lock_release(&(ssp)->dep_map);
        __srcu_read_unlock(ssp, idx);
 }
 
index b12fb0cec44d61708302ebf388e2fdbcd2d58399..336af24e0fe358a71209120786ffa1d0255d63f6 100644 (file)
@@ -197,6 +197,8 @@ void synchronize_srcu(struct srcu_struct *ssp)
 {
        struct rcu_synchronize rs;
 
+       srcu_lock_sync(&ssp->dep_map);
+
        RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
                        lock_is_held(&rcu_bh_lock_map) ||
                        lock_is_held(&rcu_lock_map) ||
index ca4b5dcec675bacf421d24044f37c4a4042ef725..408088c73e0e9e908a624f4a6b6afff5f5a442b4 100644 (file)
@@ -1267,6 +1267,8 @@ static void __synchronize_srcu(struct srcu_struct *ssp, bool do_norm)
 {
        struct rcu_synchronize rcu;
 
+       srcu_lock_sync(&ssp->dep_map);
+
        RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
                         lock_is_held(&rcu_bh_lock_map) ||
                         lock_is_held(&rcu_lock_map) ||