summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorNiels Provos <provos@cvs.openbsd.org>2001-11-28 13:49:09 +0000
committerNiels Provos <provos@cvs.openbsd.org>2001-11-28 13:49:09 +0000
commit4208f783ff04f597c2e7f87e07cadbf087dcf5fa (patch)
tree5cb7e276b3930f49bd00fe2f541ed100a9b3c88c /sys
parent4c46afe9f467831fba921bfa77a12903c4c39c7d (diff)
avoid "thundering herd" problem in accept by waking just one process.
based on freebsd. okay art@ markus@
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/uipc_socket2.c4
-rw-r--r--sys/kern/uipc_syscalls.c55
2 files changed, 38 insertions, 21 deletions
diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c
index be3fb91993c..59ef43433f0 100644
--- a/sys/kern/uipc_socket2.c
+++ b/sys/kern/uipc_socket2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_socket2.c,v 1.23 2001/11/28 02:28:55 provos Exp $ */
+/* $OpenBSD: uipc_socket2.c,v 1.24 2001/11/28 13:49:08 provos Exp $ */
/* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */
/*
@@ -111,7 +111,7 @@ soisconnected(so)
if (head && soqremque(so, 0)) {
soqinsque(head, so, 1);
sorwakeup(head);
- wakeup((caddr_t)&head->so_timeo);
+ wakeup_one((caddr_t)&head->so_timeo);
} else {
wakeup((caddr_t)&so->so_timeo);
sorwakeup(so);
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index be8f7f45af6..3649e68a052 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_syscalls.c,v 1.44 2001/11/27 15:51:36 provos Exp $ */
+/* $OpenBSD: uipc_syscalls.c,v 1.45 2001/11/28 13:49:08 provos Exp $ */
/* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */
/*
@@ -158,7 +158,7 @@ sys_accept(p, v, retval)
struct mbuf *nam;
socklen_t namelen;
int error, s, tmpfd;
- register struct socket *so;
+ struct socket *head, *so;
if (SCARG(uap, name) && (error = copyin((caddr_t)SCARG(uap, anamelen),
(caddr_t)&namelen, sizeof (namelen))))
@@ -166,47 +166,64 @@ sys_accept(p, v, retval)
if ((error = getsock(p->p_fd, SCARG(uap, s), &fp)) != 0)
return (error);
s = splsoftnet();
- so = (struct socket *)fp->f_data;
- if ((so->so_options & SO_ACCEPTCONN) == 0) {
+ head = (struct socket *)fp->f_data;
+ if ((head->so_options & SO_ACCEPTCONN) == 0) {
splx(s);
return (EINVAL);
}
- if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
+ if ((head->so_state & SS_NBIO) && head->so_qlen == 0) {
splx(s);
return (EWOULDBLOCK);
}
- while (so->so_qlen == 0 && so->so_error == 0) {
- if (so->so_state & SS_CANTRCVMORE) {
- so->so_error = ECONNABORTED;
+ while (head->so_qlen == 0 && head->so_error == 0) {
+ if (head->so_state & SS_CANTRCVMORE) {
+ head->so_error = ECONNABORTED;
break;
}
- error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
+ error = tsleep((caddr_t)&head->so_timeo, PSOCK | PCATCH,
netcon, 0);
if (error) {
splx(s);
return (error);
}
}
- if (so->so_error) {
- error = so->so_error;
- so->so_error = 0;
+ if (head->so_error) {
+ error = head->so_error;
+ head->so_error = 0;
splx(s);
return (error);
}
+
+ /*
+ * At this point we know that there is at least one connection
+ * ready to be accepted. Remove it from the queue prior to
+ * allocating the file descriptor for it since falloc() may
+ * block allowing another process to accept the connection
+ * instead.
+ */
+ so = TAILQ_FIRST(&head->so_q);
+ if (soqremque(so, 1) == 0)
+ panic("accept");
+
if ((error = falloc(p, &fp, &tmpfd)) != 0) {
+ /*
+ * Probably ran out of file descriptors. Put the
+ * unaccepted connection back onto the queue and
+ * do another wakeup so some other process might
+ * have a chance at it.
+ */
+ so->so_head = head;
+ head->so_qlen++;
+ so->so_onq = &head->so_q;
+ TAILQ_INSERT_HEAD(so->so_onq, so, so_qe);
+ wakeup_one(&head->so_timeo);
splx(s);
return (error);
}
*retval = tmpfd;
/* connection has been removed from the listen queue */
- KNOTE(&so->so_rcv.sb_sel.si_note, 0);
-
- { struct socket *aso = TAILQ_FIRST(&so->so_q);
- if (soqremque(aso, 1) == 0)
- panic("accept");
- so = aso;
- }
+ KNOTE(&head->so_rcv.sb_sel.si_note, 0);
fp->f_type = DTYPE_SOCKET;
fp->f_flag = FREAD|FWRITE;