ovpn_tcp_send_skb takes sk_lock.slock directly and may run from a process-context crypto completion with BHs enabled. Since the same socket spinlock is also acquired from softirq-side TCP paths via bh_lock_sock, bottom halves must be disabled while holding sk_lock.slock to avoid local softirq self-deadlock. The acquisition is also nested, so the lockdep subclass annotation must be preserved.
spin_lock_nested provides the required lockdep subclass annotation, but does not disable bottom halves. spin_lock_bh disables bottom halves, but does not allow specifying a lockdep subclass. Add spin_lock_bh_nested and the corresponding raw helper so callers can combine the existing _bh locking semantics with nested lockdep annotation. Implement the raw helper like the existing _raw_spin_lock_bh, but pass the requested subclass to spin_acquire. Use the new helper in ovpn_tcp_send_skb and pair it with spin_unlock_bh, preventing softirq re-entry while sk_lock.slock is held without losing the nested lockdep annotation. As a side note, include/linux/xarray.h already has an xa_lock_bh_nested wrapper which expands to spin_lock_bh_nested, but no caller currently exercises it. This change provides the missing core helper as a real implementation. Signed-off-by: Ralf Lici <[email protected]> --- drivers/net/ovpn/tcp.c | 4 ++-- include/linux/spinlock.h | 10 ++++++++++ include/linux/spinlock_api_smp.h | 2 ++ include/linux/spinlock_api_up.h | 2 ++ include/linux/spinlock_rt.h | 7 +++++++ kernel/locking/spinlock.c | 8 ++++++++ 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 65054cc84be5..e9680f6df959 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -349,7 +349,7 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk, *(__be16 *)__skb_push(skb, sizeof(u16)) = htons(len); - spin_lock_nested(&sk->sk_lock.slock, OVPN_TCP_DEPTH_NESTING); + spin_lock_bh_nested(&sk->sk_lock.slock, OVPN_TCP_DEPTH_NESTING); if (sock_owned_by_user(sk)) { if (skb_queue_len(&peer->tcp.out_queue) >= READ_ONCE(net_hotdata.max_backlog)) { @@ -362,7 +362,7 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk, ovpn_tcp_send_sock_skb(peer, sk, skb); } unlock: - spin_unlock(&sk->sk_lock.slock); + spin_unlock_bh(&sk->sk_lock.slock); } static void ovpn_tcp_release(struct sock *sk) diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 241277cd34cf..67ddff168d42 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -220,6 +220,8 @@ static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock) #ifdef CONFIG_DEBUG_LOCK_ALLOC # define raw_spin_lock_nested(lock, subclass) \ _raw_spin_lock_nested(lock, subclass) +# define raw_spin_lock_bh_nested(lock, subclass) \ + _raw_spin_lock_bh_nested(lock, subclass) # define raw_spin_lock_nest_lock(lock, nest_lock) \ do { \ @@ -234,6 +236,8 @@ static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock) */ # define raw_spin_lock_nested(lock, subclass) \ _raw_spin_lock(((void)(subclass), (lock))) +# define raw_spin_lock_bh_nested(lock, subclass) \ + _raw_spin_lock_bh(((void)(subclass), (lock))) # define raw_spin_lock_nest_lock(lock, nest_lock) _raw_spin_lock(lock) #endif @@ -360,6 +364,12 @@ do { \ __release(spinlock_check(lock)); __acquire(lock); \ } while (0) +#define spin_lock_bh_nested(lock, subclass) \ +do { \ + raw_spin_lock_bh_nested(spinlock_check(lock), subclass); \ + __release(spinlock_check(lock)); __acquire(lock); \ +} while (0) + #define spin_lock_nest_lock(lock, nest_lock) \ do { \ raw_spin_lock_nest_lock(spinlock_check(lock), nest_lock); \ diff --git a/include/linux/spinlock_api_smp.h b/include/linux/spinlock_api_smp.h index bda5e7a390cd..89954db6fd31 100644 --- a/include/linux/spinlock_api_smp.h +++ b/include/linux/spinlock_api_smp.h @@ -22,6 +22,8 @@ int in_lock_functions(unsigned long addr); void __lockfunc _raw_spin_lock(raw_spinlock_t *lock) __acquires(lock); void __lockfunc _raw_spin_lock_nested(raw_spinlock_t *lock, int subclass) __acquires(lock); +void __lockfunc _raw_spin_lock_bh_nested(raw_spinlock_t *lock, int subclass) + __acquires(lock); void __lockfunc _raw_spin_lock_nest_lock(raw_spinlock_t *lock, struct lockdep_map *map) __acquires(lock); diff --git a/include/linux/spinlock_api_up.h b/include/linux/spinlock_api_up.h index a9d5c7c66e03..9e972942d099 100644 --- a/include/linux/spinlock_api_up.h +++ b/include/linux/spinlock_api_up.h @@ -63,6 +63,8 @@ #define _raw_spin_lock(lock) __LOCK(lock) #define _raw_spin_lock_nested(lock, subclass) __LOCK(lock) +#define _raw_spin_lock_bh_nested(lock, subclass) \ + __LOCK_BH(lock) #define _raw_read_lock(lock) __LOCK(lock, shared) #define _raw_write_lock(lock) __LOCK(lock) #define _raw_write_lock_nested(lock, subclass) __LOCK(lock) diff --git a/include/linux/spinlock_rt.h b/include/linux/spinlock_rt.h index 373618a4243c..d14cfaa6f141 100644 --- a/include/linux/spinlock_rt.h +++ b/include/linux/spinlock_rt.h @@ -90,6 +90,13 @@ static __always_inline void spin_lock_bh(spinlock_t *lock) rt_spin_lock(lock); } +static __always_inline void spin_lock_bh_nested(spinlock_t *lock, int subclass) + __acquires(lock) +{ + local_bh_disable(); + __spin_lock_nested(lock, subclass); +} + static __always_inline void spin_lock_irq(spinlock_t *lock) __acquires(lock) { diff --git a/kernel/locking/spinlock.c b/kernel/locking/spinlock.c index b42d293da38b..9b93dae1f4b0 100644 --- a/kernel/locking/spinlock.c +++ b/kernel/locking/spinlock.c @@ -384,6 +384,14 @@ void __lockfunc _raw_spin_lock_nested(raw_spinlock_t *lock, int subclass) } EXPORT_SYMBOL(_raw_spin_lock_nested); +void __lockfunc _raw_spin_lock_bh_nested(raw_spinlock_t *lock, int subclass) +{ + __local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET); + spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_); + LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); +} +EXPORT_SYMBOL(_raw_spin_lock_bh_nested); + unsigned long __lockfunc _raw_spin_lock_irqsave_nested(raw_spinlock_t *lock, int subclass) { -- 2.54.0 _______________________________________________ Openvpn-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openvpn-devel
