summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/uipc_socket.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-08-09 13:04:41 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-09-21 10:29:37 +0200
commite4a8065910cd6b2e7e0448cc6431ca2906322389 (patch)
tree73492991cfa40f994c20d761d476e6bc16304536 /freebsd/sys/kern/uipc_socket.c
parentUpdate to FreeBSD head 2017-08-01 (diff)
downloadrtems-libbsd-e4a8065910cd6b2e7e0448cc6431ca2906322389.tar.bz2
Update to FreeBSD head 2017-10-01
Git mirror commit b2f0376b45428f13151d229c5ae9d4d8f74acbd1. Update #3472.
Diffstat (limited to 'freebsd/sys/kern/uipc_socket.c')
-rw-r--r--freebsd/sys/kern/uipc_socket.c52
1 files changed, 34 insertions, 18 deletions
diff --git a/freebsd/sys/kern/uipc_socket.c b/freebsd/sys/kern/uipc_socket.c
index 1773606d..5e4fda56 100644
--- a/freebsd/sys/kern/uipc_socket.c
+++ b/freebsd/sys/kern/uipc_socket.c
@@ -3744,24 +3744,41 @@ soisconnecting(struct socket *so)
void
soisconnected(struct socket *so)
{
- struct socket *head;
- int ret;
- /*
- * XXXGL: this is the only place where we acquire socket locks
- * in reverse order: first child, then listening socket. To
- * avoid possible LOR, use try semantics.
- */
-restart:
SOCK_LOCK(so);
- if ((head = so->so_listen) != NULL &&
- __predict_false(SOLISTEN_TRYLOCK(head) == 0)) {
- SOCK_UNLOCK(so);
- goto restart;
- }
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
so->so_state |= SS_ISCONNECTED;
- if (head != NULL && (so->so_qstate == SQ_INCOMP)) {
+
+ if (so->so_qstate == SQ_INCOMP) {
+ struct socket *head = so->so_listen;
+ int ret;
+
+ KASSERT(head, ("%s: so %p on incomp of NULL", __func__, so));
+ /*
+ * Promoting a socket from incomplete queue to complete, we
+ * need to go through reverse order of locking. We first do
+ * trylock, and if that doesn't succeed, we go the hard way
+ * leaving a reference and rechecking consistency after proper
+ * locking.
+ */
+ if (__predict_false(SOLISTEN_TRYLOCK(head) == 0)) {
+ soref(head);
+ SOCK_UNLOCK(so);
+ SOLISTEN_LOCK(head);
+ SOCK_LOCK(so);
+ if (__predict_false(head != so->so_listen)) {
+ /*
+ * The socket went off the listen queue,
+ * should be lost race to close(2) of sol.
+ * The socket is about to soabort().
+ */
+ SOCK_UNLOCK(so);
+ sorele(head);
+ return;
+ }
+ /* Not the last one, as so holds a ref. */
+ refcount_release(&head->so_count);
+ }
again:
if ((so->so_options & SO_ACCEPTFILTER) == 0) {
TAILQ_REMOVE(&head->sol_incomp, so, so_list);
@@ -3790,8 +3807,6 @@ again:
}
return;
}
- if (head != NULL)
- SOLISTEN_UNLOCK(head);
SOCK_UNLOCK(so);
wakeup(&so->so_timeo);
sorwakeup(so);
@@ -3825,13 +3840,14 @@ soisdisconnected(struct socket *so)
so->so_state |= SS_ISDISCONNECTED;
if (!SOLISTENING(so)) {
+ SOCK_UNLOCK(so);
SOCKBUF_LOCK(&so->so_rcv);
socantrcvmore_locked(so);
SOCKBUF_LOCK(&so->so_snd);
sbdrop_locked(&so->so_snd, sbused(&so->so_snd));
socantsendmore_locked(so);
- }
- SOCK_UNLOCK(so);
+ } else
+ SOCK_UNLOCK(so);
wakeup(&so->so_timeo);
}