summaryrefslogtreecommitdiffstats
path: root/sys/kern/uipc_usrreq.c
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2007-03-01 09:00:42 +0000
committerrwatson <rwatson@FreeBSD.org>2007-03-01 09:00:42 +0000
commitc077671dda9549bc2cddb686b4eecf89eeec5a4b (patch)
treee2b2d58775628b38061bef1ca31d50911fe9c24a /sys/kern/uipc_usrreq.c
parentc00cd44eca5abca948f15cc5e1e5772003e71ea3 (diff)
downloadFreeBSD-src-c077671dda9549bc2cddb686b4eecf89eeec5a4b.zip
FreeBSD-src-c077671dda9549bc2cddb686b4eecf89eeec5a4b.tar.gz
Remove two simultaneous acquisitions of multiple unpcb locks from
uipc_send in cases where only a global read lock is held by breaking them out and avoiding the unpcb lock acquire in the common case. This avoids deadlocks which manifested with X11, and should also marginally further improve performance. Reported by: sepotvin, brooks
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r--sys/kern/uipc_usrreq.c41
1 files changed, 19 insertions, 22 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 3a30138..17a8fb5 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -774,21 +774,18 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
unp2 = unp->unp_conn;
if (nam != NULL) {
+ UNP_GLOBAL_WLOCK_ASSERT();
if (unp2 != NULL) {
error = EISCONN;
- UNP_PCB_LOCK(unp);
break;
}
error = unp_connect(so, nam, td);
- UNP_PCB_LOCK(unp);
if (error)
break;
unp2 = unp->unp_conn;
} else {
- UNP_PCB_LOCK(unp);
if (unp2 == NULL) {
error = ENOTCONN;
- UNP_PCB_LOCK(unp);
break;
}
}
@@ -799,19 +796,19 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
* return the slightly counter-intuitive but otherwise
* correct error that the socket is not connected.
*/
- UNP_PCB_LOCK_ASSERT(unp);
if (unp2 == NULL) {
error = ENOTCONN;
break;
}
- UNP_PCB_LOCK(unp2);
- so2 = unp2->unp_socket;
+ /* Lockless read. */
+ if (unp2->unp_flags & UNP_WANTCRED)
+ control = unp_addsockcred(td, control);
+ UNP_PCB_LOCK(unp);
if (unp->unp_addr != NULL)
from = (struct sockaddr *)unp->unp_addr;
else
from = &sun_noname;
- if (unp2->unp_flags & UNP_WANTCRED)
- control = unp_addsockcred(td, control);
+ so2 = unp2->unp_socket;
SOCKBUF_LOCK(&so2->so_rcv);
if (sbappendaddr_locked(&so2->so_rcv, from, m, control)) {
sorwakeup_locked(so2);
@@ -821,9 +818,13 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
SOCKBUF_UNLOCK(&so2->so_rcv);
error = ENOBUFS;
}
- if (nam != NULL)
+ if (nam != NULL) {
+ UNP_GLOBAL_WLOCK_ASSERT();
+ UNP_PCB_LOCK(unp2);
unp_disconnect(unp, unp2);
- UNP_PCB_UNLOCK(unp2);
+ UNP_PCB_UNLOCK(unp2);
+ }
+ UNP_PCB_UNLOCK(unp);
break;
}
@@ -836,18 +837,15 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
*/
if ((so->so_state & SS_ISCONNECTED) == 0) {
if (nam != NULL) {
+ UNP_GLOBAL_WLOCK_ASSERT();
error = unp_connect(so, nam, td);
- UNP_PCB_LOCK(unp);
if (error)
break; /* XXX */
} else {
error = ENOTCONN;
- UNP_PCB_LOCK(unp);
break;
}
- } else
- UNP_PCB_LOCK(unp);
- UNP_PCB_LOCK_ASSERT(unp);
+ }
/* Lockless read. */
if (so->so_snd.sb_state & SBS_CANTSENDMORE) {
@@ -874,13 +872,12 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
error = ENOTCONN;
break;
}
- UNP_PCB_LOCK(unp2);
so2 = unp2->unp_socket;
+ UNP_PCB_LOCK(unp2);
SOCKBUF_LOCK(&so2->so_rcv);
if (unp2->unp_flags & UNP_WANTCRED) {
/*
- * Credentials are passed only once on
- * SOCK_STREAM.
+ * Credentials are passed only once on SOCK_STREAM.
*/
unp2->unp_flags &= ~UNP_WANTCRED;
control = unp_addsockcred(td, control);
@@ -915,14 +912,14 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
}
/*
- * SEND_EOF is equivalent to a SEND followed by
- * a SHUTDOWN.
+ * SEND_EOF is equivalent to a SEND followed by a SHUTDOWN.
*/
if (flags & PRUS_EOF) {
+ UNP_PCB_LOCK(unp);
socantsendmore(so);
unp_shutdown(unp);
+ UNP_PCB_UNLOCK(unp);
}
- UNP_PCB_UNLOCK(unp);
if ((nam != NULL) || (flags & PRUS_EOF))
UNP_GLOBAL_WUNLOCK();
OpenPOWER on IntegriCloud