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
This commit is contained in:
parent
f94acf52a4
commit
c67f3b8b78
@ -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))
|
||||
|
@ -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?
|
||||
|
Loading…
Reference in New Issue
Block a user