diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-08-09 13:04:41 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-09-21 10:29:37 +0200 |
commit | e4a8065910cd6b2e7e0448cc6431ca2906322389 (patch) | |
tree | 73492991cfa40f994c20d761d476e6bc16304536 /freebsd/sys/kern/uipc_socket.c | |
parent | Update to FreeBSD head 2017-08-01 (diff) | |
download | rtems-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.c | 52 |
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); } |