diff options
author | jhb <jhb@FreeBSD.org> | 2009-06-01 21:17:03 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2009-06-01 21:17:03 +0000 |
commit | a1af9ecca44f362b24fe3a8342ca6ed8676a399c (patch) | |
tree | 13628b6be10af95db7dc7d8ef88b3291d48583ab /sys/kern/uipc_sockbuf.c | |
parent | 9956d85f164d16d3c1db67cc01f521c1c09d5fdb (diff) | |
download | FreeBSD-src-a1af9ecca44f362b24fe3a8342ca6ed8676a399c.zip FreeBSD-src-a1af9ecca44f362b24fe3a8342ca6ed8676a399c.tar.gz |
Rework socket upcalls to close some races with setup/teardown of upcalls.
- Each socket upcall is now invoked with the appropriate socket buffer
locked. It is not permissible to call soisconnected() with this lock
held; however, so socket upcalls now return an integer value. The two
possible values are SU_OK and SU_ISCONNECTED. If an upcall returns
SU_ISCONNECTED, then the soisconnected() will be invoked on the
socket after the socket buffer lock is dropped.
- A new API is provided for setting and clearing socket upcalls. The
API consists of soupcall_set() and soupcall_clear().
- To simplify locking, each socket buffer now has a separate upcall.
- When a socket upcall returns SU_ISCONNECTED, the upcall is cleared from
the receive socket buffer automatically. Note that a SO_SND upcall
should never return SU_ISCONNECTED.
- All this means that accept filters should now return SU_ISCONNECTED
instead of calling soisconnected() directly. They also no longer need
to explicitly clear the upcall on the new socket.
- The HTTP accept filter still uses soupcall_set() to manage its internal
state machine, but other accept filters no longer have any explicit
knowlege of socket upcall internals aside from their return value.
- The various RPC client upcalls currently drop the socket buffer lock
while invoking soreceive() as a temporary band-aid. The plan for
the future is to add a new flag to allow soreceive() to be called with
the socket buffer locked.
- The AIO callback for socket I/O is now also invoked with the socket
buffer locked. Previously sowakeup() would drop the socket buffer
lock only to call aio_swake() which immediately re-acquired the socket
buffer lock for the duration of the function call.
Discussed with: rwatson, rmacklem
Diffstat (limited to 'sys/kern/uipc_sockbuf.c')
-rw-r--r-- | sys/kern/uipc_sockbuf.c | 18 |
1 files changed, 14 insertions, 4 deletions
diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index 8c0a618..2d86d74 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -175,6 +175,7 @@ sbunlock(struct sockbuf *sb) void sowakeup(struct socket *so, struct sockbuf *sb) { + int ret; SOCKBUF_LOCK_ASSERT(sb); @@ -186,13 +187,22 @@ sowakeup(struct socket *so, struct sockbuf *sb) wakeup(&sb->sb_cc); } KNOTE_LOCKED(&sb->sb_sel.si_note, 0); + if (sb->sb_upcall != NULL) { + ret = sb->sb_upcall(so, sb->sb_upcallarg, M_DONTWAIT); + if (ret == SU_ISCONNECTED) { + KASSERT(sb == &so->so_rcv, + ("SO_SND upcall returned SU_ISCONNECTED")); + soupcall_clear(so, SO_RCV); + } + } else + ret = SU_OK; + if (sb->sb_flags & SB_AIO) + aio_swake(so, sb); SOCKBUF_UNLOCK(sb); + if (ret == SU_ISCONNECTED) + soisconnected(so); if ((so->so_state & SS_ASYNC) && so->so_sigio != NULL) pgsigio(&so->so_sigio, SIGIO, 0); - if (sb->sb_flags & SB_UPCALL) - (*so->so_upcall)(so, so->so_upcallarg, M_DONTWAIT); - if (sb->sb_flags & SB_AIO) - aio_swake(so, sb); mtx_assert(SOCKBUF_MTX(sb), MA_NOTOWNED); } |