git: c67f3b8b78e5 - main - socket: Move sockbuf mutexes into the owning socket

Mark Johnston markj at FreeBSD.org
Tue Sep 7 21:12:36 UTC 2021


The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=c67f3b8b78e50c6df7c057d6cf108e4d6b4312d0

commit c67f3b8b78e50c6df7c057d6cf108e4d6b4312d0
Author:     Mark Johnston <markj at FreeBSD.org>
AuthorDate: 2021-09-07 18:49:40 +0000
Commit:     Mark Johnston <markj at FreeBSD.org>
CommitDate: 2021-09-07 19:09:02 +0000

    socket: Move sockbuf mutexes into the owning socket
    
    This is necessary to provide proper interlocking with listen(2), which
    destroys the socket buffers.  Otherwise, code must lock the socket
    itself and check SOLISTENING(so), but most I/O paths do not otherwise
    need to acquire the socket lock, so the extra overhead needed to check a
    rare error case is undesirable.
    
    listen(2) calls are relatively rare.  Thus, the strategy is to have it
    acquire all socket buffer locks when transitioning to a listening
    socket.  To do this safely, these locks must be stable, and not
    destroyed during listen(2) as they are today.  So, move them out of the
    sockbuf and into the owning socket.  For the sockbuf mutexes, keep a
    pointer to the mutex in the sockbuf itself, for now.  This can be
    removed by replacing SOCKBUF_LOCK() etc. with macros which operate on
    the socket itself, as was done for the sockbuf I/O locks.
    
    Reviewed by:    tuexen, gallatin
    MFC after:      1 month
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D31658
---
 sys/sys/sockbuf.h   |  7 +++----
 sys/sys/socketvar.h | 19 +++++++++++++++----
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/sys/sys/sockbuf.h b/sys/sys/sockbuf.h
index 3d74a6d953f7..3b345870bd5f 100644
--- a/sys/sys/sockbuf.h
+++ b/sys/sys/sockbuf.h
@@ -79,9 +79,8 @@ struct selinfo;
  * Locking key to struct sockbuf:
  * (a) locked by SOCKBUF_LOCK().
  */
-struct	sockbuf {
-	struct	mtx sb_mtx;		/* sockbuf lock */
-	struct	sx sb_sx;		/* prevent I/O interlacing */
+struct sockbuf {
+	struct	mtx *sb_mtx;		/* sockbuf lock */
 	struct	selinfo *sb_sel;	/* process selecting read/write */
 	short	sb_state;	/* (a) socket state on sockbuf */
 #define	sb_startzero	sb_flags
@@ -122,7 +121,7 @@ struct	sockbuf {
  * Per-socket buffer mutex used to protect most fields in the socket
  * buffer.
  */
-#define	SOCKBUF_MTX(_sb)		(&(_sb)->sb_mtx)
+#define	SOCKBUF_MTX(_sb)		((_sb)->sb_mtx)
 #define	SOCKBUF_LOCK_INIT(_sb, _name) \
 	mtx_init(SOCKBUF_MTX(_sb), _name, NULL, MTX_DEF)
 #define	SOCKBUF_LOCK_DESTROY(_sb)	mtx_destroy(SOCKBUF_MTX(_sb))
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
index a12e60e1b5c6..69e182dfa9a5 100644
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -121,6 +121,17 @@ struct socket {
 
 	int so_ts_clock;	/* type of the clock used for timestamps */
 	uint32_t so_max_pacing_rate;	/* (f) TX rate limit in bytes/s */
+
+	/*
+	 * Mutexes to prevent interleaving of socket I/O.  These have to be
+	 * outside of the socket buffers in order to interlock with listen(2).
+	 */
+	struct sx so_snd_sx __aligned(CACHE_LINE_SIZE);
+	struct mtx so_snd_mtx;
+
+	struct sx so_rcv_sx __aligned(CACHE_LINE_SIZE);
+	struct mtx so_rcv_mtx;
+
 	union {
 		/* Regular (data flow) socket. */
 		struct {
@@ -256,13 +267,13 @@ struct socket {
 #define	SBL_VALID	(SBL_WAIT | SBL_NOINTR)
 
 #define	SOCK_IO_SEND_LOCK(so, flags)					\
-	soiolock((so), &(so)->so_snd.sb_sx, (flags))
+	soiolock((so), &(so)->so_snd_sx, (flags))
 #define	SOCK_IO_SEND_UNLOCK(so)						\
-	soiounlock(&(so)->so_snd.sb_sx)
+	soiounlock(&(so)->so_snd_sx)
 #define	SOCK_IO_RECV_LOCK(so, flags)					\
-	soiolock((so), &(so)->so_rcv.sb_sx, (flags))
+	soiolock((so), &(so)->so_rcv_sx, (flags))
 #define	SOCK_IO_RECV_UNLOCK(so)						\
-	soiounlock(&(so)->so_rcv.sb_sx)
+	soiounlock(&(so)->so_rcv_sx)
 
 /*
  * Do we need to notify the other side when I/O is possible?


More information about the dev-commits-src-all mailing list