summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/uipc_debug.c8
-rw-r--r--sys/kern/uipc_sockbuf.c35
-rw-r--r--sys/kern/uipc_socket.c129
-rw-r--r--sys/kern/uipc_syscalls.c4
-rw-r--r--sys/netinet/sctputil.c25
-rw-r--r--sys/sys/socketvar.h26
6 files changed, 98 insertions, 129 deletions
diff --git a/sys/kern/uipc_debug.c b/sys/kern/uipc_debug.c
index 1570d72..48103c5 100644
--- a/sys/kern/uipc_debug.c
+++ b/sys/kern/uipc_debug.c
@@ -340,14 +340,6 @@ db_print_sbflags(short sb_flags)
int comma;
comma = 0;
- if (sb_flags & SB_LOCK) {
- db_printf("%sSB_LOCK", comma ? ", " : "");
- comma = 1;
- }
- if (sb_flags & SB_WANT) {
- db_printf("%sSB_WANT", comma ? ", " : "");
- comma = 1;
- }
if (sb_flags & SB_WAIT) {
db_printf("%sSB_WAIT", comma ? ", " : "");
comma = 1;
diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c
index 381632c..a35a2c6 100644
--- a/sys/kern/uipc_sockbuf.c
+++ b/sys/kern/uipc_sockbuf.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
+#include <sys/sx.h>
#include <sys/sysctl.h>
/*
@@ -132,27 +133,25 @@ sbwait(struct sockbuf *sb)
sb->sb_timeo));
}
-/*
- * Lock a sockbuf already known to be locked; return any error returned from
- * sleep (EINTR).
- */
int
-sb_lock(struct sockbuf *sb)
+sblock(struct sockbuf *sb, int flags)
{
- int error;
- SOCKBUF_LOCK_ASSERT(sb);
-
- while (sb->sb_flags & SB_LOCK) {
- sb->sb_flags |= SB_WANT;
- error = msleep(&sb->sb_flags, &sb->sb_mtx,
- (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK|PCATCH,
- "sblock", 0);
- if (error)
- return (error);
+ if (flags == M_WAITOK) {
+ sx_xlock(&sb->sb_sx);
+ return (0);
+ } else {
+ if (sx_try_xlock(&sb->sb_sx) == 0)
+ return (EWOULDBLOCK);
+ return (0);
}
- sb->sb_flags |= SB_LOCK;
- return (0);
+}
+
+void
+sbunlock(struct sockbuf *sb)
+{
+
+ sx_xunlock(&sb->sb_sx);
}
/*
@@ -797,8 +796,6 @@ static void
sbflush_internal(struct sockbuf *sb)
{
- if (sb->sb_flags & SB_LOCK)
- panic("sbflush_internal: locked");
while (sb->sb_mbcnt) {
/*
* Don't call sbdrop(sb, 0) if the leading mbuf is non-empty:
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index de83932..f67347b 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -2,7 +2,7 @@
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California.
* Copyright (c) 2004 The FreeBSD Foundation
- * Copyright (c) 2004-2006 Robert N. M. Watson
+ * Copyright (c) 2004-2007 Robert N. M. Watson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -124,6 +124,7 @@ __FBSDID("$FreeBSD$");
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/stat.h>
+#include <sys/sx.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <sys/jail.h>
@@ -273,6 +274,8 @@ soalloc(void)
#endif
SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd");
SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv");
+ sx_init(&so->so_snd.sb_sx, "so_snd_sx");
+ sx_init(&so->so_rcv.sb_sx, "so_rcv_sx");
TAILQ_INIT(&so->so_aiojobq);
mtx_lock(&so_global_mtx);
so->so_gencnt = ++so_gencnt;
@@ -312,6 +315,8 @@ sodealloc(struct socket *so)
mac_destroy_socket(so);
#endif
crfree(so->so_cred);
+ sx_destroy(&so->so_snd.sb_sx);
+ sx_destroy(&so->so_rcv.sb_sx);
SOCKBUF_LOCK_DESTROY(&so->so_snd);
SOCKBUF_LOCK_DESTROY(&so->so_rcv);
uma_zfree(socket_zone, so);
@@ -624,11 +629,8 @@ sofree(so)
*
* Notice that the socket buffer and kqueue state are torn down
* before calling pru_detach. This means that protocols shold not
- * assume they can perform socket wakeups, etc, in their detach
- * code.
+ * assume they can perform socket wakeups, etc, in their detach code.
*/
- KASSERT((so->so_snd.sb_flags & SB_LOCK) == 0, ("sofree: snd sblock"));
- KASSERT((so->so_rcv.sb_flags & SB_LOCK) == 0, ("sofree: rcv sblock"));
sbdestroy(&so->so_snd, so);
sbdestroy(&so->so_rcv, so);
knlist_destroy(&so->so_rcv.sb_sel.si_note);
@@ -1123,7 +1125,6 @@ out:
* counts if EINTR/ERESTART are returned. Data and control buffers are freed
* on return.
*/
-#define snderr(errno) { error = (errno); goto release; }
int
sosend_generic(so, addr, uio, top, control, flags, td)
struct socket *so;
@@ -1165,19 +1166,22 @@ sosend_generic(so, addr, uio, top, control, flags, td)
if (control != NULL)
clen = control->m_len;
- SOCKBUF_LOCK(&so->so_snd);
-restart:
- SOCKBUF_LOCK_ASSERT(&so->so_snd);
error = sblock(&so->so_snd, SBLOCKWAIT(flags));
if (error)
- goto out_locked;
+ goto out;
+
+restart:
do {
- SOCKBUF_LOCK_ASSERT(&so->so_snd);
- if (so->so_snd.sb_state & SBS_CANTSENDMORE)
- snderr(EPIPE);
+ SOCKBUF_LOCK(&so->so_snd);
+ if (so->so_snd.sb_state & SBS_CANTSENDMORE) {
+ SOCKBUF_UNLOCK(&so->so_snd);
+ error = EPIPE;
+ goto release;
+ }
if (so->so_error) {
error = so->so_error;
so->so_error = 0;
+ SOCKBUF_UNLOCK(&so->so_snd);
goto release;
}
if ((so->so_state & SS_ISCONNECTED) == 0) {
@@ -1190,26 +1194,40 @@ restart:
if ((so->so_proto->pr_flags & PR_CONNREQUIRED) &&
(so->so_proto->pr_flags & PR_IMPLOPCL) == 0) {
if ((so->so_state & SS_ISCONFIRMING) == 0 &&
- !(resid == 0 && clen != 0))
- snderr(ENOTCONN);
- } else if (addr == NULL)
- snderr(so->so_proto->pr_flags & PR_CONNREQUIRED ?
- ENOTCONN : EDESTADDRREQ);
+ !(resid == 0 && clen != 0)) {
+ SOCKBUF_UNLOCK(&so->so_snd);
+ error = ENOTCONN;
+ goto release;
+ }
+ } else if (addr == NULL) {
+ SOCKBUF_UNLOCK(&so->so_snd);
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED)
+ error = ENOTCONN;
+ else
+ error = EDESTADDRREQ;
+ goto release;
+ }
}
space = sbspace(&so->so_snd);
if (flags & MSG_OOB)
space += 1024;
if ((atomic && resid > so->so_snd.sb_hiwat) ||
- clen > so->so_snd.sb_hiwat)
- snderr(EMSGSIZE);
+ clen > so->so_snd.sb_hiwat) {
+ SOCKBUF_UNLOCK(&so->so_snd);
+ error = EMSGSIZE;
+ goto release;
+ }
if (space < resid + clen &&
(atomic || space < so->so_snd.sb_lowat || space < clen)) {
- if ((so->so_state & SS_NBIO) || (flags & MSG_NBIO))
- snderr(EWOULDBLOCK);
- sbunlock(&so->so_snd);
+ if ((so->so_state & SS_NBIO) || (flags & MSG_NBIO)) {
+ SOCKBUF_UNLOCK(&so->so_snd);
+ error = EWOULDBLOCK;
+ goto release;
+ }
error = sbwait(&so->so_snd);
if (error)
- goto out_locked;
+ goto release;
+ SOCKBUF_UNLOCK(&so->so_snd);
goto restart;
}
SOCKBUF_UNLOCK(&so->so_snd);
@@ -1223,10 +1241,8 @@ restart:
#ifdef ZERO_COPY_SOCKETS
error = sosend_copyin(uio, &top, atomic,
&space, flags);
- if (error != 0) {
- SOCKBUF_LOCK(&so->so_snd);
+ if (error != 0)
goto release;
- }
#else
/*
* Copy the data from userland into a mbuf
@@ -1238,7 +1254,6 @@ restart:
(atomic ? M_PKTHDR : 0) |
((flags & MSG_EOR) ? M_EOR : 0));
if (top == NULL) {
- SOCKBUF_LOCK(&so->so_snd);
error = EFAULT; /* only possible error */
goto release;
}
@@ -1283,20 +1298,13 @@ restart:
clen = 0;
control = NULL;
top = NULL;
- if (error) {
- SOCKBUF_LOCK(&so->so_snd);
+ if (error)
goto release;
- }
} while (resid && space > 0);
- SOCKBUF_LOCK(&so->so_snd);
} while (resid);
release:
- SOCKBUF_LOCK_ASSERT(&so->so_snd);
sbunlock(&so->so_snd);
-out_locked:
- SOCKBUF_LOCK_ASSERT(&so->so_snd);
- SOCKBUF_UNLOCK(&so->so_snd);
out:
if (top != NULL)
m_freem(top);
@@ -1304,7 +1312,6 @@ out:
m_freem(control);
return (error);
}
-#undef snderr
int
sosend(so, addr, uio, top, control, flags, td)
@@ -1462,13 +1469,12 @@ soreceive_generic(so, psa, uio, mp0, controlp, flagsp)
&& uio->uio_resid)
(*pr->pr_usrreqs->pru_rcvd)(so, 0);
- SOCKBUF_LOCK(&so->so_rcv);
-restart:
- SOCKBUF_LOCK_ASSERT(&so->so_rcv);
error = sblock(&so->so_rcv, SBLOCKWAIT(flags));
if (error)
- goto out;
+ return (error);
+restart:
+ SOCKBUF_LOCK(&so->so_rcv);
m = so->so_rcv.sb_mb;
/*
* If we have less data than requested, block awaiting more (subject
@@ -1495,14 +1501,16 @@ restart:
error = so->so_error;
if ((flags & MSG_PEEK) == 0)
so->so_error = 0;
+ SOCKBUF_UNLOCK(&so->so_rcv);
goto release;
}
SOCKBUF_LOCK_ASSERT(&so->so_rcv);
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) {
- if (m)
- goto dontblock;
- else
+ if (m == NULL) {
+ SOCKBUF_UNLOCK(&so->so_rcv);
goto release;
+ } else
+ goto dontblock;
}
for (; m != NULL; m = m->m_next)
if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) {
@@ -1511,22 +1519,26 @@ restart:
}
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
(so->so_proto->pr_flags & PR_CONNREQUIRED)) {
+ SOCKBUF_UNLOCK(&so->so_rcv);
error = ENOTCONN;
goto release;
}
- if (uio->uio_resid == 0)
+ if (uio->uio_resid == 0) {
+ SOCKBUF_UNLOCK(&so->so_rcv);
goto release;
+ }
if ((so->so_state & SS_NBIO) ||
(flags & (MSG_DONTWAIT|MSG_NBIO))) {
+ SOCKBUF_UNLOCK(&so->so_rcv);
error = EWOULDBLOCK;
goto release;
}
SBLASTRECORDCHK(&so->so_rcv);
SBLASTMBUFCHK(&so->so_rcv);
- sbunlock(&so->so_rcv);
error = sbwait(&so->so_rcv);
+ SOCKBUF_UNLOCK(&so->so_rcv);
if (error)
- goto out;
+ goto release;
goto restart;
}
dontblock:
@@ -1721,6 +1733,7 @@ dontblock:
if (m && pr->pr_flags & PR_ATOMIC &&
((flags & MSG_PEEK) == 0))
(void)sbdroprecord_locked(&so->so_rcv);
+ SOCKBUF_UNLOCK(&so->so_rcv);
goto release;
}
} else
@@ -1822,8 +1835,10 @@ dontblock:
SBLASTRECORDCHK(&so->so_rcv);
SBLASTMBUFCHK(&so->so_rcv);
error = sbwait(&so->so_rcv);
- if (error)
+ if (error) {
+ SOCKBUF_UNLOCK(&so->so_rcv);
goto release;
+ }
m = so->so_rcv.sb_mb;
if (m != NULL)
nextrecord = m->m_nextpkt;
@@ -1867,18 +1882,15 @@ dontblock:
SOCKBUF_LOCK_ASSERT(&so->so_rcv);
if (orig_resid == uio->uio_resid && orig_resid &&
(flags & MSG_EOR) == 0 && (so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0) {
- sbunlock(&so->so_rcv);
+ SOCKBUF_UNLOCK(&so->so_rcv);
goto restart;
}
+ SOCKBUF_UNLOCK(&so->so_rcv);
if (flagsp != NULL)
*flagsp |= flags;
release:
- SOCKBUF_LOCK_ASSERT(&so->so_rcv);
sbunlock(&so->so_rcv);
-out:
- SOCKBUF_LOCK_ASSERT(&so->so_rcv);
- SOCKBUF_UNLOCK(&so->so_rcv);
return (error);
}
@@ -1933,27 +1945,22 @@ sorflush(so)
* however, we have to initialize and destroy the mutex in the copy
* so that dom_dispose() and sbrelease() can lock t as needed.
*/
+ (void) sblock(sb, M_WAITOK);
SOCKBUF_LOCK(sb);
sb->sb_flags |= SB_NOINTR;
- (void) sblock(sb, M_WAITOK);
- /*
- * socantrcvmore_locked() drops the socket buffer mutex so that it
- * can safely perform wakeups. Re-acquire the mutex before
- * continuing.
- */
socantrcvmore_locked(so);
- SOCKBUF_LOCK(sb);
- sbunlock(sb);
/*
* Invalidate/clear most of the sockbuf structure, but leave selinfo
* and mutex data unchanged.
*/
+ SOCKBUF_LOCK(sb);
bzero(&asb, offsetof(struct sockbuf, sb_startzero));
bcopy(&sb->sb_startzero, &asb.sb_startzero,
sizeof(*sb) - offsetof(struct sockbuf, sb_startzero));
bzero(&sb->sb_startzero,
sizeof(*sb) - offsetof(struct sockbuf, sb_startzero));
SOCKBUF_UNLOCK(sb);
+ sbunlock(sb);
SOCKBUF_LOCK_INIT(&asb, "so_rcv");
if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL)
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index 53de5bc..463ae89 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -1920,9 +1920,7 @@ kern_sendfile(struct thread *td, struct sendfile_args *uap,
}
/* Protect against multiple writers to the socket. */
- SOCKBUF_LOCK(&so->so_snd);
(void) sblock(&so->so_snd, M_WAITOK);
- SOCKBUF_UNLOCK(&so->so_snd);
/*
* Loop through the pages of the file, starting with the requested
@@ -2196,9 +2194,7 @@ retry_space:
}
done:
- SOCKBUF_LOCK(&so->so_snd);
sbunlock(&so->so_snd);
- SOCKBUF_UNLOCK(&so->so_snd);
out:
/*
* If there was no error we have to clear td->td_retval[0]
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index 0d0c270..bb1911d 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -3862,9 +3862,7 @@ sctp_pull_off_control_to_new_inp(struct sctp_inpcb *old_inp,
old_so = old_inp->sctp_socket;
new_so = new_inp->sctp_socket;
TAILQ_INIT(&tmp_queue);
- SOCKBUF_LOCK(&(old_so->so_rcv));
error = sblock(&old_so->so_rcv, waitflags);
- SOCKBUF_UNLOCK(&(old_so->so_rcv));
if (error) {
/*
* Gak, can't get sblock, we have a problem. data will be
@@ -3903,10 +3901,8 @@ sctp_pull_off_control_to_new_inp(struct sctp_inpcb *old_inp,
}
SCTP_INP_READ_UNLOCK(old_inp);
/* Remove the sb-lock on the old socket */
- SOCKBUF_LOCK(&(old_so->so_rcv));
sbunlock(&old_so->so_rcv);
- SOCKBUF_UNLOCK(&(old_so->so_rcv));
/* Now we move them over to the new socket buffer */
control = TAILQ_FIRST(&tmp_queue);
SCTP_INP_READ_LOCK(new_inp);
@@ -4521,6 +4517,7 @@ sctp_sorecvmsg(struct socket *so,
int alen = 0;
int slen = 0;
int held_length = 0;
+ int sockbuf_lock = 0;
if (msg_flags) {
in_flags = *msg_flags;
@@ -4558,8 +4555,6 @@ sctp_sorecvmsg(struct socket *so,
sctp_misc_ints(SCTP_SORECV_ENTER,
rwnd_req, in_eeor_mode, so->so_rcv.sb_cc, uio->uio_resid);
#endif
- SOCKBUF_LOCK(&so->so_rcv);
- hold_sblock = 1;
#ifdef SCTP_RECV_RWND_LOGGING
sctp_misc_ints(SCTP_SORECV_ENTERPL,
rwnd_req, block_allowed, so->so_rcv.sb_cc, uio->uio_resid);
@@ -4567,15 +4562,12 @@ sctp_sorecvmsg(struct socket *so,
error = sblock(&so->so_rcv, (block_allowed ? M_WAITOK : 0));
+ sockbuf_lock = 1;
if (error) {
goto release_unlocked;
}
restart:
- if (hold_sblock == 0) {
- SOCKBUF_LOCK(&so->so_rcv);
- hold_sblock = 1;
- }
- sbunlock(&so->so_rcv);
+
restart_nosblocks:
if (hold_sblock == 0) {
@@ -4704,7 +4696,6 @@ restart_nosblocks:
SOCKBUF_UNLOCK(&so->so_rcv);
hold_sblock = 0;
}
- error = sblock(&so->so_rcv, (block_allowed ? M_WAITOK : 0));
/* we possibly have data we can read */
control = TAILQ_FIRST(&inp->read_queue);
if (control == NULL) {
@@ -5529,11 +5520,12 @@ release:
SCTP_INP_READ_UNLOCK(inp);
hold_rlock = 0;
}
- if (hold_sblock == 0) {
- SOCKBUF_LOCK(&so->so_rcv);
- hold_sblock = 1;
+ if (hold_sblock == 1) {
+ SOCKBUF_UNLOCK(&so->so_rcv);
+ hold_sblock = 0;
}
sbunlock(&so->so_rcv);
+ sockbuf_lock = 0;
release_unlocked:
if (hold_sblock) {
@@ -5566,6 +5558,9 @@ out:
SOCKBUF_UNLOCK(&so->so_rcv);
hold_sblock = 0;
}
+ if (sockbuf_lock) {
+ sbunlock(&so->so_rcv);
+ }
if (freecnt_applied) {
/*
* The lock on the socket buffer protects us so the free
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
index f0b3fe2..83ef094 100644
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -37,6 +37,7 @@
#include <sys/selinfo.h> /* for struct selinfo */
#include <sys/_lock.h>
#include <sys/_mutex.h>
+#include <sys/_sx.h>
/*
* Kernel structure per socket.
@@ -97,6 +98,7 @@ struct socket {
struct sockbuf {
struct selinfo sb_sel; /* process selecting read/write */
struct mtx sb_mtx; /* sockbuf lock */
+ struct sx sb_sx; /* prevent I/O interlacing */
short sb_state; /* (c/d) socket state on sockbuf */
#define sb_startzero sb_mb
struct mbuf *sb_mb; /* (c/d) the mbuf chain */
@@ -121,8 +123,6 @@ struct socket {
/*
* Constants for sb_flags field of struct sockbuf.
*/
-#define SB_LOCK 0x01 /* lock on data queue */
-#define SB_WANT 0x02 /* someone is waiting to lock */
#define SB_WAIT 0x04 /* someone is waiting for data/space */
#define SB_SEL 0x08 /* someone is selecting */
#define SB_ASYNC 0x10 /* ASYNC I/O, need signals */
@@ -332,25 +332,6 @@ struct xsocket {
}
/*
- * Set lock on sockbuf sb; sleep if lock is already held.
- * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
- * Returns error without lock if sleep is interrupted.
- */
-#define sblock(sb, wf) ((sb)->sb_flags & SB_LOCK ? \
- (((wf) == M_WAITOK) ? sb_lock(sb) : EWOULDBLOCK) : \
- ((sb)->sb_flags |= SB_LOCK), 0)
-
-/* release lock on sockbuf sb */
-#define sbunlock(sb) do { \
- SOCKBUF_LOCK_ASSERT(sb); \
- (sb)->sb_flags &= ~SB_LOCK; \
- if ((sb)->sb_flags & SB_WANT) { \
- (sb)->sb_flags &= ~SB_WANT; \
- wakeup(&(sb)->sb_flags); \
- } \
-} while (0)
-
-/*
* soref()/sorele() ref-count the socket structure. Note that you must
* still explicitly close the socket, but the last ref count will free
* the structure.
@@ -503,7 +484,8 @@ struct mbuf *
sbsndptr(struct sockbuf *sb, u_int off, u_int len, u_int *moff);
void sbtoxsockbuf(struct sockbuf *sb, struct xsockbuf *xsb);
int sbwait(struct sockbuf *sb);
-int sb_lock(struct sockbuf *sb);
+int sblock(struct sockbuf *sb, int flags);
+void sbunlock(struct sockbuf *sb);
void soabort(struct socket *so);
int soaccept(struct socket *so, struct sockaddr **nam);
int socheckuid(struct socket *so, uid_t uid);
OpenPOWER on IntegriCloud