[RFC PATCH ovpn net] locking/spinlock, ovpn: add spin_lock_bh_nested

Ralf Lici <[email protected]>
Newsgroups gmane.network.openvpn.devel
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.