sk_stop_timer_sync() calls del_timer_sync(), which spin-waits for the
timer callback to complete on non-RT kernels. But on PREEMPT_RT, it can
sleep. Sleeping inside an RCU read-side critical section might trigger a
lockdep splat.

Instead, keep a reference to the timer, under rcu_read_lock, and call
sk_stop_timer*() without the RCU lock.

While at it, apply the reversed Xmas order when declaring variables.

Fixes: 426358d9be7c ("mptcp: fix a race in mptcp_pm_del_add_timer()")
Cc: [email protected]
Reviewed-by: Mat Martineau <[email protected]>
Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>
---
To: Sebastian Andrzej Siewior <[email protected]>
To: Clark Williams <[email protected]>
To: Steven Rostedt <[email protected]>
To: Hannes Reinecke <[email protected]>
Cc: [email protected]
---
 net/mptcp/pm.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 3e770c7407e1..1e0866159972 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -401,9 +401,9 @@ struct mptcp_pm_add_entry *
 mptcp_pm_del_add_timer(struct mptcp_sock *msk,
                       const struct mptcp_addr_info *addr, bool check_id)
 {
-       struct mptcp_pm_add_entry *entry;
        struct sock *sk = (struct sock *)msk;
-       bool stop_timer = false;
+       struct mptcp_pm_add_entry *entry;
+       struct timer_list *timer = NULL;
 
        rcu_read_lock();
 
@@ -411,7 +411,7 @@ mptcp_pm_del_add_timer(struct mptcp_sock *msk,
        entry = mptcp_lookup_anno_list_by_saddr(msk, addr);
        if (entry && (!check_id || entry->addr.id == addr->id)) {
                entry->retrans_times = ADD_ADDR_RETRANS_MAX;
-               stop_timer = true;
+               timer = &entry->add_timer;
        }
        if (!check_id && entry)
                list_del(&entry->list);
@@ -420,14 +420,14 @@ mptcp_pm_del_add_timer(struct mptcp_sock *msk,
        /* Note: entry might have been removed by another thread.
         * We hold rcu_read_lock() to ensure it is not freed under us.
         */
-       if (stop_timer) {
-               if (check_id)
-                       sk_stop_timer(sk, &entry->add_timer);
-               else
-                       sk_stop_timer_sync(sk, &entry->add_timer);
-       }
+       if (timer && check_id)
+               sk_stop_timer(sk, timer);
 
        rcu_read_unlock();
+
+       if (timer && !check_id)
+               sk_stop_timer_sync(sk, timer);
+
        return entry;
 }
 

-- 
2.53.0


Reply via email to