diff options
author | scottl <scottl@FreeBSD.org> | 2005-06-09 19:59:09 +0000 |
---|---|---|
committer | scottl <scottl@FreeBSD.org> | 2005-06-09 19:59:09 +0000 |
commit | 7a9b003ce5c89fb242246f4edde01e66fdb5a39d (patch) | |
tree | 4970329c2802c6329dd4f6e781d84b27dbf8f412 /sys/kern/uipc_socket.c | |
parent | 6be4cb00a414f098d88dc8554c3daee743124744 (diff) | |
download | FreeBSD-src-7a9b003ce5c89fb242246f4edde01e66fdb5a39d.zip FreeBSD-src-7a9b003ce5c89fb242246f4edde01e66fdb5a39d.tar.gz |
Drat! Committed from the wrong branch. Restore HEAD to its previous goodness.
Diffstat (limited to 'sys/kern/uipc_socket.c')
-rw-r--r-- | sys/kern/uipc_socket.c | 1470 |
1 files changed, 966 insertions, 504 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index de55b24..4c6f8bf 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -1,4 +1,6 @@ -/* +/*- + * Copyright (c) 2004 The FreeBSD Foundation + * Copyright (c) 2004-2005 Robert N. M. Watson * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * @@ -10,10 +12,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -31,20 +29,27 @@ * SUCH DAMAGE. * * @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94 - * $FreeBSD$ */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + #include "opt_inet.h" +#include "opt_mac.h" +#include "opt_zero.h" #include <sys/param.h> #include <sys/systm.h> #include <sys/fcntl.h> +#include <sys/limits.h> +#include <sys/lock.h> +#include <sys/mac.h> #include <sys/malloc.h> #include <sys/mbuf.h> +#include <sys/mutex.h> #include <sys/domain.h> #include <sys/file.h> /* for struct knote */ #include <sys/kernel.h> -#include <sys/malloc.h> #include <sys/event.h> #include <sys/poll.h> #include <sys/proc.h> @@ -56,28 +61,27 @@ #include <sys/sysctl.h> #include <sys/uio.h> #include <sys/jail.h> -#include <vm/vm_zone.h> -#include <machine/limits.h> +#include <vm/uma.h> -#ifdef INET -static int do_setopt_accept_filter(struct socket *so, struct sockopt *sopt); -#endif /* INET */ -static void filt_sordetach(struct knote *kn); -static int filt_soread(struct knote *kn, long hint); -static void filt_sowdetach(struct knote *kn); +static int soreceive_rcvoob(struct socket *so, struct uio *uio, + int flags); + +static void filt_sordetach(struct knote *kn); +static int filt_soread(struct knote *kn, long hint); +static void filt_sowdetach(struct knote *kn); static int filt_sowrite(struct knote *kn, long hint); static int filt_solisten(struct knote *kn, long hint); -static struct filterops solisten_filtops = +static struct filterops solisten_filtops = { 1, NULL, filt_sordetach, filt_solisten }; static struct filterops soread_filtops = { 1, NULL, filt_sordetach, filt_soread }; -static struct filterops sowrite_filtops = +static struct filterops sowrite_filtops = { 1, NULL, filt_sowdetach, filt_sowrite }; -struct vm_zone *socket_zone; +uma_zone_t socket_zone; so_gen_t so_gencnt; /* generation count for sockets */ MALLOC_DEFINE(M_SONAME, "soname", "socket name"); @@ -86,8 +90,39 @@ MALLOC_DEFINE(M_PCB, "pcb", "protocol control block"); SYSCTL_DECL(_kern_ipc); static int somaxconn = SOMAXCONN; -SYSCTL_INT(_kern_ipc, KIPC_SOMAXCONN, somaxconn, CTLFLAG_RW, - &somaxconn, 0, "Maximum pending socket connection queue size"); +static int somaxconn_sysctl(SYSCTL_HANDLER_ARGS); +/* XXX: we dont have SYSCTL_USHORT */ +SYSCTL_PROC(_kern_ipc, KIPC_SOMAXCONN, somaxconn, CTLTYPE_UINT | CTLFLAG_RW, + 0, sizeof(int), somaxconn_sysctl, "I", "Maximum pending socket connection " + "queue size"); +static int numopensockets; +SYSCTL_INT(_kern_ipc, OID_AUTO, numopensockets, CTLFLAG_RD, + &numopensockets, 0, "Number of open sockets"); +#ifdef ZERO_COPY_SOCKETS +/* These aren't static because they're used in other files. */ +int so_zero_copy_send = 1; +int so_zero_copy_receive = 1; +SYSCTL_NODE(_kern_ipc, OID_AUTO, zero_copy, CTLFLAG_RD, 0, + "Zero copy controls"); +SYSCTL_INT(_kern_ipc_zero_copy, OID_AUTO, receive, CTLFLAG_RW, + &so_zero_copy_receive, 0, "Enable zero copy receive"); +SYSCTL_INT(_kern_ipc_zero_copy, OID_AUTO, send, CTLFLAG_RW, + &so_zero_copy_send, 0, "Enable zero copy send"); +#endif /* ZERO_COPY_SOCKETS */ + +/* + * accept_mtx locks down per-socket fields relating to accept queues. See + * socketvar.h for an annotation of the protected fields of struct socket. + */ +struct mtx accept_mtx; +MTX_SYSINIT(accept_mtx, &accept_mtx, "accept", MTX_DEF); + +/* + * so_global_mtx protects so_gencnt, numopensockets, and the per-socket + * so_gencnt field. + */ +static struct mtx so_global_mtx; +MTX_SYSINIT(so_global_mtx, &so_global_mtx, "so_glabel", MTX_DEF); /* * Socket operation routines. @@ -99,48 +134,63 @@ SYSCTL_INT(_kern_ipc, KIPC_SOMAXCONN, somaxconn, CTLFLAG_RW, /* * Get a socket structure from our zone, and initialize it. - * We don't implement `waitok' yet (see comments in uipc_domain.c). * Note that it would probably be better to allocate socket * and PCB at the same time, but I'm not convinced that all * the protocols can be easily modified to do this. + * + * soalloc() returns a socket with a ref count of 0. */ struct socket * -soalloc(waitok) - int waitok; +soalloc(int mflags) { struct socket *so; - so = zalloci(socket_zone); - if (so) { - /* XXX race condition for reentrant kernel */ - bzero(so, sizeof *so); - so->so_gencnt = ++so_gencnt; + so = uma_zalloc(socket_zone, mflags | M_ZERO); + if (so != NULL) { +#ifdef MAC + if (mac_init_socket(so, mflags) != 0) { + uma_zfree(socket_zone, so); + return (NULL); + } +#endif + SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd"); + SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv"); TAILQ_INIT(&so->so_aiojobq); + mtx_lock(&so_global_mtx); + so->so_gencnt = ++so_gencnt; + ++numopensockets; + mtx_unlock(&so_global_mtx); } - return so; + return (so); } +/* + * socreate returns a socket with a ref count of 1. The socket should be + * closed with soclose(). + */ int -socreate(dom, aso, type, proto, p) +socreate(dom, aso, type, proto, cred, td) int dom; struct socket **aso; - register int type; + int type; int proto; - struct proc *p; + struct ucred *cred; + struct thread *td; { - register struct protosw *prp; - register struct socket *so; - register int error; + struct protosw *prp; + struct socket *so; + int error; if (proto) prp = pffindproto(dom, proto, type); else prp = pffindtype(dom, type); - if (prp == 0 || prp->pr_usrreqs->pru_attach == 0) + if (prp == NULL || prp->pr_usrreqs->pru_attach == NULL || + prp->pr_usrreqs->pru_attach == pru_attach_notsupp) return (EPROTONOSUPPORT); - if (p->p_prison && jail_socket_unixiproute_only && + if (jailed(cred) && jail_socket_unixiproute_only && prp->pr_domain->dom_family != PF_LOCAL && prp->pr_domain->dom_family != PF_INET && prp->pr_domain->dom_family != PF_ROUTE) { @@ -149,20 +199,27 @@ socreate(dom, aso, type, proto, p) if (prp->pr_type != type) return (EPROTOTYPE); - so = soalloc(p != 0); - if (so == 0) + so = soalloc(M_WAITOK); + if (so == NULL) return (ENOBUFS); TAILQ_INIT(&so->so_incomp); TAILQ_INIT(&so->so_comp); so->so_type = type; - so->so_cred = p->p_ucred; - crhold(so->so_cred); + so->so_cred = crhold(cred); so->so_proto = prp; - error = (*prp->pr_usrreqs->pru_attach)(so, proto, p); +#ifdef MAC + mac_create_socket(cred, so); +#endif + knlist_init(&so->so_rcv.sb_sel.si_note, SOCKBUF_MTX(&so->so_rcv)); + knlist_init(&so->so_snd.sb_sel.si_note, SOCKBUF_MTX(&so->so_snd)); + so->so_count = 1; + error = (*prp->pr_usrreqs->pru_attach)(so, proto, td); if (error) { + ACCEPT_LOCK(); + SOCK_LOCK(so); so->so_state |= SS_NOFDREF; - sofree(so); + sorele(so); return (error); } *aso = so; @@ -170,25 +227,23 @@ socreate(dom, aso, type, proto, p) } int -sobind(so, nam, p) +sobind(so, nam, td) struct socket *so; struct sockaddr *nam; - struct proc *p; + struct thread *td; { - int s = splnet(); - int error; - error = (*so->so_proto->pr_usrreqs->pru_bind)(so, nam, p); - splx(s); - return (error); + return ((*so->so_proto->pr_usrreqs->pru_bind)(so, nam, td)); } void -sodealloc(so) - struct socket *so; +sodealloc(struct socket *so) { + KASSERT(so->so_count == 0, ("sodealloc(): so_count %d", so->so_count)); + mtx_lock(&so_global_mtx); so->so_gencnt = ++so_gencnt; + mtx_unlock(&so_global_mtx); if (so->so_rcv.sb_hiwat) (void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_rcv.sb_hiwat, 0, RLIM_INFINITY); @@ -196,75 +251,159 @@ sodealloc(so) (void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat, 0, RLIM_INFINITY); #ifdef INET - if (so->so_accf != NULL) { - if (so->so_accf->so_accept_filter != NULL && - so->so_accf->so_accept_filter->accf_destroy != NULL) { - so->so_accf->so_accept_filter->accf_destroy(so); - } - if (so->so_accf->so_accept_filter_str != NULL) - FREE(so->so_accf->so_accept_filter_str, M_ACCF); - FREE(so->so_accf, M_ACCF); - } -#endif /* INET */ + /* remove acccept filter if one is present. */ + if (so->so_accf != NULL) + do_setopt_accept_filter(so, NULL); +#endif +#ifdef MAC + mac_destroy_socket(so); +#endif crfree(so->so_cred); - zfreei(socket_zone, so); + SOCKBUF_LOCK_DESTROY(&so->so_snd); + SOCKBUF_LOCK_DESTROY(&so->so_rcv); + uma_zfree(socket_zone, so); + mtx_lock(&so_global_mtx); + --numopensockets; + mtx_unlock(&so_global_mtx); } +/* + * solisten() transitions a socket from a non-listening state to a listening + * state, but can also be used to update the listen queue depth on an + * existing listen socket. The protocol will call back into the sockets + * layer using solisten_proto_check() and solisten_proto() to check and set + * socket-layer listen state. Call backs are used so that the protocol can + * acquire both protocol and socket layer locks in whatever order is reuiqred + * by the protocol. + * + * Protocol implementors are advised to hold the socket lock across the + * socket-layer test and set to avoid races at the socket layer. + */ int -solisten(so, backlog, p) - register struct socket *so; +solisten(so, backlog, td) + struct socket *so; int backlog; - struct proc *p; + struct thread *td; { - int s, error; + int error; - s = splnet(); - if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING)) { - splx(s); - return (EINVAL); - } - error = (*so->so_proto->pr_usrreqs->pru_listen)(so, p); - if (error) { - splx(s); + error = (*so->so_proto->pr_usrreqs->pru_listen)(so, td); + if (error) return (error); - } - if (TAILQ_EMPTY(&so->so_comp)) - so->so_options |= SO_ACCEPTCONN; + + /* + * XXXRW: The following state adjustment should occur in + * solisten_proto(), but we don't currently pass the backlog request + * to the protocol via pru_listen(). + */ if (backlog < 0 || backlog > somaxconn) backlog = somaxconn; so->so_qlimit = backlog; - splx(s); return (0); } +int +solisten_proto_check(so) + struct socket *so; +{ + + SOCK_LOCK_ASSERT(so); + + if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING | + SS_ISDISCONNECTING)) + return (EINVAL); + return (0); +} + +void +solisten_proto(so) + struct socket *so; +{ + + SOCK_LOCK_ASSERT(so); + + so->so_options |= SO_ACCEPTCONN; +} + +/* + * Attempt to free a socket. This should really be sotryfree(). + * + * We free the socket if the protocol is no longer interested in the socket, + * there's no file descriptor reference, and the refcount is 0. While the + * calling macro sotryfree() tests the refcount, sofree() has to test it + * again as it's possible to race with an accept()ing thread if the socket is + * in an listen queue of a listen socket, as being in the listen queue + * doesn't elevate the reference count. sofree() acquires the accept mutex + * early for this test in order to avoid that race. + */ void sofree(so) - register struct socket *so; + struct socket *so; { - struct socket *head = so->so_head; + struct socket *head; - if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) + ACCEPT_LOCK_ASSERT(); + SOCK_LOCK_ASSERT(so); + + if (so->so_pcb != NULL || (so->so_state & SS_NOFDREF) == 0 || + so->so_count != 0) { + SOCK_UNLOCK(so); + ACCEPT_UNLOCK(); return; + } + + head = so->so_head; if (head != NULL) { - if (so->so_state & SS_INCOMP) { - TAILQ_REMOVE(&head->so_incomp, so, so_list); - head->so_incqlen--; - } else if (so->so_state & SS_COMP) { - /* - * We must not decommission a socket that's - * on the accept(2) queue. If we do, then - * accept(2) may hang after select(2) indicated - * that the listening socket was ready. - */ + KASSERT((so->so_qstate & SQ_COMP) != 0 || + (so->so_qstate & SQ_INCOMP) != 0, + ("sofree: so_head != NULL, but neither SQ_COMP nor " + "SQ_INCOMP")); + KASSERT((so->so_qstate & SQ_COMP) == 0 || + (so->so_qstate & SQ_INCOMP) == 0, + ("sofree: so->so_qstate is SQ_COMP and also SQ_INCOMP")); + /* + * accept(2) is responsible draining the completed + * connection queue and freeing those sockets, so + * we just return here if this socket is currently + * on the completed connection queue. Otherwise, + * accept(2) may hang after select(2) has indicating + * that a listening socket was ready. If it's an + * incomplete connection, we remove it from the queue + * and free it; otherwise, it won't be released until + * the listening socket is closed. + */ + if ((so->so_qstate & SQ_COMP) != 0) { + SOCK_UNLOCK(so); + ACCEPT_UNLOCK(); return; - } else { - panic("sofree: not queued"); } - so->so_state &= ~SS_INCOMP; + TAILQ_REMOVE(&head->so_incomp, so, so_list); + head->so_incqlen--; + so->so_qstate &= ~SQ_INCOMP; so->so_head = NULL; } - sbrelease(&so->so_snd, so); + KASSERT((so->so_qstate & SQ_COMP) == 0 && + (so->so_qstate & SQ_INCOMP) == 0, + ("sofree: so_head == NULL, but still SQ_COMP(%d) or SQ_INCOMP(%d)", + so->so_qstate & SQ_COMP, so->so_qstate & SQ_INCOMP)); + SOCK_UNLOCK(so); + ACCEPT_UNLOCK(); + SOCKBUF_LOCK(&so->so_snd); + so->so_snd.sb_flags |= SB_NOINTR; + (void)sblock(&so->so_snd, M_WAITOK); + /* + * socantsendmore_locked() drops the socket buffer mutex so that it + * can safely perform wakeups. Re-acquire the mutex before + * continuing. + */ + socantsendmore_locked(so); + SOCKBUF_LOCK(&so->so_snd); + sbunlock(&so->so_snd); + sbrelease_locked(&so->so_snd, so); + SOCKBUF_UNLOCK(&so->so_snd); sorflush(so); + knlist_destroy(&so->so_rcv.sb_sel.si_note); + knlist_destroy(&so->so_snd.sb_sel.si_note); sodealloc(so); } @@ -272,34 +411,44 @@ sofree(so) * Close a socket on last file table reference removal. * Initiate disconnect if connected. * Free socket when disconnect complete. + * + * This function will sorele() the socket. Note that soclose() may be + * called prior to the ref count reaching zero. The actual socket + * structure will not be freed until the ref count reaches zero. */ int soclose(so) - register struct socket *so; + struct socket *so; { - int s = splnet(); /* conservative */ int error = 0; - funsetown(so->so_sigio); - if (so->so_options & SO_ACCEPTCONN) { - struct socket *sp, *sonext; + KASSERT(!(so->so_state & SS_NOFDREF), ("soclose: SS_NOFDREF on enter")); - sp = TAILQ_FIRST(&so->so_incomp); - for (; sp != NULL; sp = sonext) { - sonext = TAILQ_NEXT(sp, so_list); + funsetown(&so->so_sigio); + if (so->so_options & SO_ACCEPTCONN) { + struct socket *sp; + ACCEPT_LOCK(); + while ((sp = TAILQ_FIRST(&so->so_incomp)) != NULL) { + TAILQ_REMOVE(&so->so_incomp, sp, so_list); + so->so_incqlen--; + sp->so_qstate &= ~SQ_INCOMP; + sp->so_head = NULL; + ACCEPT_UNLOCK(); (void) soabort(sp); + ACCEPT_LOCK(); } - for (sp = TAILQ_FIRST(&so->so_comp); sp != NULL; sp = sonext) { - sonext = TAILQ_NEXT(sp, so_list); - /* Dequeue from so_comp since sofree() won't do it */ + while ((sp = TAILQ_FIRST(&so->so_comp)) != NULL) { TAILQ_REMOVE(&so->so_comp, sp, so_list); so->so_qlen--; - sp->so_state &= ~SS_COMP; + sp->so_qstate &= ~SQ_COMP; sp->so_head = NULL; + ACCEPT_UNLOCK(); (void) soabort(sp); + ACCEPT_LOCK(); } + ACCEPT_UNLOCK(); } - if (so->so_pcb == 0) + if (so->so_pcb == NULL) goto discard; if (so->so_state & SS_ISCONNECTED) { if ((so->so_state & SS_ISDISCONNECTING) == 0) { @@ -312,7 +461,7 @@ soclose(so) (so->so_state & SS_NBIO)) goto drop; while (so->so_state & SS_ISCONNECTED) { - error = tsleep((caddr_t)&so->so_timeo, + error = tsleep(&so->so_timeo, PSOCK | PCATCH, "soclos", so->so_linger * hz); if (error) break; @@ -320,22 +469,25 @@ soclose(so) } } drop: - if (so->so_pcb) { + if (so->so_pcb != NULL) { int error2 = (*so->so_proto->pr_usrreqs->pru_detach)(so); if (error == 0) error = error2; } discard: - if (so->so_state & SS_NOFDREF) - panic("soclose: NOFDREF"); + ACCEPT_LOCK(); + SOCK_LOCK(so); + KASSERT((so->so_state & SS_NOFDREF) == 0, ("soclose: NOFDREF")); so->so_state |= SS_NOFDREF; - sofree(so); - splx(s); + sorele(so); return (error); } /* - * Must be called at splnet... + * soabort() must not be called with any socket locks held, as it calls + * into the protocol, which will call back into the socket code causing + * it to acquire additional socket locks that may cause recursion or lock + * order reversals. */ int soabort(so) @@ -345,7 +497,9 @@ soabort(so) error = (*so->so_proto->pr_usrreqs->pru_abort)(so); if (error) { - sofree(so); + ACCEPT_LOCK(); + SOCK_LOCK(so); + sotryfree(so); /* note: does not decrement the ref count */ return error; } return (0); @@ -353,32 +507,29 @@ soabort(so) int soaccept(so, nam) - register struct socket *so; + struct socket *so; struct sockaddr **nam; { - int s = splnet(); int error; - if ((so->so_state & SS_NOFDREF) == 0) - panic("soaccept: !NOFDREF"); + SOCK_LOCK(so); + KASSERT((so->so_state & SS_NOFDREF) != 0, ("soaccept: !NOFDREF")); so->so_state &= ~SS_NOFDREF; + SOCK_UNLOCK(so); error = (*so->so_proto->pr_usrreqs->pru_accept)(so, nam); - splx(s); return (error); } int -soconnect(so, nam, p) - register struct socket *so; +soconnect(so, nam, td) + struct socket *so; struct sockaddr *nam; - struct proc *p; + struct thread *td; { - int s; int error; if (so->so_options & SO_ACCEPTCONN) return (EOPNOTSUPP); - s = splnet(); /* * If protocol is connection-based, can only connect once. * Otherwise, if connected, try to disconnect first. @@ -387,45 +538,40 @@ soconnect(so, nam, p) */ if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) && ((so->so_proto->pr_flags & PR_CONNREQUIRED) || - (error = sodisconnect(so)))) + (error = sodisconnect(so)))) { error = EISCONN; - else - error = (*so->so_proto->pr_usrreqs->pru_connect)(so, nam, p); - splx(s); + } else { + /* + * Prevent accumulated error from previous connection + * from biting us. + */ + so->so_error = 0; + error = (*so->so_proto->pr_usrreqs->pru_connect)(so, nam, td); + } + return (error); } int soconnect2(so1, so2) - register struct socket *so1; + struct socket *so1; struct socket *so2; { - int s = splnet(); - int error; - error = (*so1->so_proto->pr_usrreqs->pru_connect2)(so1, so2); - splx(s); - return (error); + return ((*so1->so_proto->pr_usrreqs->pru_connect2)(so1, so2)); } int sodisconnect(so) - register struct socket *so; + struct socket *so; { - int s = splnet(); int error; - if ((so->so_state & SS_ISCONNECTED) == 0) { - error = ENOTCONN; - goto bad; - } - if (so->so_state & SS_ISDISCONNECTING) { - error = EALREADY; - goto bad; - } + if ((so->so_state & SS_ISCONNECTED) == 0) + return (ENOTCONN); + if (so->so_state & SS_ISDISCONNECTING) + return (EALREADY); error = (*so->so_proto->pr_usrreqs->pru_disconnect)(so); -bad: - splx(s); return (error); } @@ -447,23 +593,42 @@ bad: * must check for short counts if EINTR/ERESTART are returned. * Data and control buffers are freed on return. */ + +#ifdef ZERO_COPY_SOCKETS +struct so_zerocopy_stats{ + int size_ok; + int align_ok; + int found_ifp; +}; +struct so_zerocopy_stats so_zerocp_stats = {0,0,0}; +#include <netinet/in.h> +#include <net/route.h> +#include <netinet/in_pcb.h> +#include <vm/vm.h> +#include <vm/vm_page.h> +#include <vm/vm_object.h> +#endif /*ZERO_COPY_SOCKETS*/ + int -sosend(so, addr, uio, top, control, flags, p) - register struct socket *so; +sosend(so, addr, uio, top, control, flags, td) + struct socket *so; struct sockaddr *addr; struct uio *uio; struct mbuf *top; struct mbuf *control; int flags; - struct proc *p; + struct thread *td; { struct mbuf **mp; - register struct mbuf *m; - register long space, len, resid; - int clen = 0, error, s, dontroute, mlen; + struct mbuf *m; + long space, len = 0, resid; + int clen = 0, error, dontroute; int atomic = sosendallatonce(so) || top; +#ifdef ZERO_COPY_SOCKETS + int cow_send; +#endif /* ZERO_COPY_SOCKETS */ - if (uio) + if (uio != NULL) resid = uio->uio_resid; else resid = top->m_pkthdr.len; @@ -485,24 +650,25 @@ sosend(so, addr, uio, top, control, flags, p) dontroute = (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && (so->so_proto->pr_flags & PR_ATOMIC); - if (p) - p->p_stats->p_ru.ru_msgsnd++; - if (control) + if (td != NULL) + td->td_proc->p_stats->p_ru.ru_msgsnd++; + if (control != NULL) clen = control->m_len; -#define snderr(errno) { error = errno; splx(s); goto release; } +#define snderr(errno) { error = (errno); goto release; } + SOCKBUF_LOCK(&so->so_snd); restart: + SOCKBUF_LOCK_ASSERT(&so->so_snd); error = sblock(&so->so_snd, SBLOCKWAIT(flags)); if (error) - goto out; + goto out_locked; do { - s = splnet(); - if (so->so_state & SS_CANTSENDMORE) + SOCKBUF_LOCK_ASSERT(&so->so_snd); + if (so->so_snd.sb_state & SBS_CANTSENDMORE) snderr(EPIPE); if (so->so_error) { error = so->so_error; so->so_error = 0; - splx(s); goto release; } if ((so->so_state & SS_ISCONNECTED) == 0) { @@ -517,7 +683,7 @@ restart: if ((so->so_state & SS_ISCONFIRMING) == 0 && !(resid == 0 && clen != 0)) snderr(ENOTCONN); - } else if (addr == 0) + } else if (addr == NULL) snderr(so->so_proto->pr_flags & PR_CONNREQUIRED ? ENOTCONN : EDESTADDRREQ); } @@ -529,16 +695,15 @@ restart: snderr(EMSGSIZE); if (space < resid + clen && (atomic || space < so->so_snd.sb_lowat || space < clen)) { - if (so->so_state & SS_NBIO) + if ((so->so_state & SS_NBIO) || (flags & MSG_NBIO)) snderr(EWOULDBLOCK); sbunlock(&so->so_snd); error = sbwait(&so->so_snd); - splx(s); if (error) - goto out; + goto out_locked; goto restart; } - splx(s); + SOCKBUF_UNLOCK(&so->so_snd); mp = ⊤ space -= clen; do { @@ -550,47 +715,94 @@ restart: if (flags & MSG_EOR) top->m_flags |= M_EOR; } else do { - if (top == 0) { - MGETHDR(m, M_WAIT, MT_DATA); - if (m == NULL) { - error = ENOBUFS; - goto release; +#ifdef ZERO_COPY_SOCKETS + cow_send = 0; +#endif /* ZERO_COPY_SOCKETS */ + if (resid >= MINCLSIZE) { +#ifdef ZERO_COPY_SOCKETS + if (top == NULL) { + MGETHDR(m, M_TRYWAIT, MT_DATA); + if (m == NULL) { + error = ENOBUFS; + SOCKBUF_LOCK(&so->so_snd); + goto release; + } + m->m_pkthdr.len = 0; + m->m_pkthdr.rcvif = (struct ifnet *)0; + } else { + MGET(m, M_TRYWAIT, MT_DATA); + if (m == NULL) { + error = ENOBUFS; + SOCKBUF_LOCK(&so->so_snd); + goto release; + } + } + if (so_zero_copy_send && + resid>=PAGE_SIZE && + space>=PAGE_SIZE && + uio->uio_iov->iov_len>=PAGE_SIZE) { + so_zerocp_stats.size_ok++; + so_zerocp_stats.align_ok++; + cow_send = socow_setup(m, uio); + len = cow_send; } - mlen = MHLEN; - m->m_pkthdr.len = 0; - m->m_pkthdr.rcvif = (struct ifnet *)0; + if (!cow_send) { + MCLGET(m, M_TRYWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } else { + len = min(min(MCLBYTES, resid), space); + } + } +#else /* ZERO_COPY_SOCKETS */ + if (top == NULL) { + m = m_getcl(M_TRYWAIT, MT_DATA, M_PKTHDR); + m->m_pkthdr.len = 0; + m->m_pkthdr.rcvif = (struct ifnet *)0; + } else + m = m_getcl(M_TRYWAIT, MT_DATA, 0); + len = min(min(MCLBYTES, resid), space); +#endif /* ZERO_COPY_SOCKETS */ } else { - MGET(m, M_WAIT, MT_DATA); - if (m == NULL) { - error = ENOBUFS; - goto release; + if (top == NULL) { + m = m_gethdr(M_TRYWAIT, MT_DATA); + m->m_pkthdr.len = 0; + m->m_pkthdr.rcvif = (struct ifnet *)0; + + len = min(min(MHLEN, resid), space); + /* + * For datagram protocols, leave room + * for protocol headers in first mbuf. + */ + if (atomic && m && len < MHLEN) + MH_ALIGN(m, len); + } else { + m = m_get(M_TRYWAIT, MT_DATA); + len = min(min(MLEN, resid), space); } - mlen = MLEN; } - if (resid >= MINCLSIZE) { - MCLGET(m, M_WAIT); - if ((m->m_flags & M_EXT) == 0) - goto nopages; - mlen = MCLBYTES; - len = min(min(mlen, resid), space); - } else { -nopages: - len = min(min(mlen, resid), space); - /* - * For datagram protocols, leave room - * for protocol headers in first mbuf. - */ - if (atomic && top == 0 && len < mlen) - MH_ALIGN(m, len); + if (m == NULL) { + error = ENOBUFS; + SOCKBUF_LOCK(&so->so_snd); + goto release; } + space -= len; - error = uiomove(mtod(m, caddr_t), (int)len, uio); +#ifdef ZERO_COPY_SOCKETS + if (cow_send) + error = 0; + else +#endif /* ZERO_COPY_SOCKETS */ + error = uiomove(mtod(m, void *), (int)len, uio); resid = uio->uio_resid; m->m_len = len; *mp = m; top->m_pkthdr.len += len; - if (error) + if (error) { + SOCKBUF_LOCK(&so->so_snd); goto release; + } mp = &m->m_next; if (resid <= 0) { if (flags & MSG_EOR) @@ -598,15 +810,17 @@ nopages: break; } } while (space > 0 && atomic); - if (dontroute) + if (dontroute) { + SOCK_LOCK(so); so->so_options |= SO_DONTROUTE; - s = splnet(); /* XXX */ + SOCK_UNLOCK(so); + } /* - * XXX all the SS_CANTSENDMORE checks previously + * XXX all the SBS_CANTSENDMORE checks previously * done could be out of date. We could have recieved * a reset packet in an interrupt or maybe we slept * while doing page faults in uiomove() etc. We could - * probably recheck again inside the splnet() protection + * probably recheck again inside the locking protection * here, but there are probably other places that this * also happens. We must rethink this. */ @@ -623,30 +837,127 @@ nopages: PRUS_EOF : /* If there is more to send set PRUS_MORETOCOME */ (resid > 0 && space > 0) ? PRUS_MORETOCOME : 0, - top, addr, control, p); - splx(s); - if (dontroute) + top, addr, control, td); + if (dontroute) { + SOCK_LOCK(so); so->so_options &= ~SO_DONTROUTE; + SOCK_UNLOCK(so); + } clen = 0; - control = 0; - top = 0; + control = NULL; + top = NULL; mp = ⊤ - if (error) + if (error) { + SOCKBUF_LOCK(&so->so_snd); 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) + if (top != NULL) m_freem(top); - if (control) + if (control != NULL) m_freem(control); return (error); } /* + * The part of soreceive() that implements reading non-inline out-of-band + * data from a socket. For more complete comments, see soreceive(), from + * which this code originated. + * + * Note that soreceive_rcvoob(), unlike the remainder of soreceive(), is + * unable to return an mbuf chain to the caller. + */ +static int +soreceive_rcvoob(so, uio, flags) + struct socket *so; + struct uio *uio; + int flags; +{ + struct protosw *pr = so->so_proto; + struct mbuf *m; + int error; + + KASSERT(flags & MSG_OOB, ("soreceive_rcvoob: (flags & MSG_OOB) == 0")); + + m = m_get(M_TRYWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + error = (*pr->pr_usrreqs->pru_rcvoob)(so, m, flags & MSG_PEEK); + if (error) + goto bad; + do { +#ifdef ZERO_COPY_SOCKETS + if (so_zero_copy_receive) { + int disposable; + + if ((m->m_flags & M_EXT) + && (m->m_ext.ext_type == EXT_DISPOSABLE)) + disposable = 1; + else + disposable = 0; + + error = uiomoveco(mtod(m, void *), + min(uio->uio_resid, m->m_len), + uio, disposable); + } else +#endif /* ZERO_COPY_SOCKETS */ + error = uiomove(mtod(m, void *), + (int) min(uio->uio_resid, m->m_len), uio); + m = m_free(m); + } while (uio->uio_resid && error == 0 && m); +bad: + if (m != NULL) + m_freem(m); + return (error); +} + +/* + * Following replacement or removal of the first mbuf on the first mbuf chain + * of a socket buffer, push necessary state changes back into the socket + * buffer so that other consumers see the values consistently. 'nextrecord' + * is the callers locally stored value of the original value of + * sb->sb_mb->m_nextpkt which must be restored when the lead mbuf changes. + * NOTE: 'nextrecord' may be NULL. + */ +static __inline void +sockbuf_pushsync(struct sockbuf *sb, struct mbuf *nextrecord) +{ + + SOCKBUF_LOCK_ASSERT(sb); + /* + * First, update for the new value of nextrecord. If necessary, make + * it the first record. + */ + if (sb->sb_mb != NULL) + sb->sb_mb->m_nextpkt = nextrecord; + else + sb->sb_mb = nextrecord; + + /* + * Now update any dependent socket buffer fields to reflect the new + * state. This is an expanded inline of SB_EMPTY_FIXUP(), with the + * addition of a second clause that takes care of the case where + * sb_mb has been updated, but remains the last record. + */ + if (sb->sb_mb == NULL) { + sb->sb_mbtail = NULL; + sb->sb_lastrecord = NULL; + } else if (sb->sb_mb->m_nextpkt == NULL) + sb->sb_lastrecord = sb->sb_mb; +} + + +/* * Implement receive operations on a socket. * We depend on the way that records are added to the sockbuf * by sbappend*. In particular, each record (mbufs linked through m_next) @@ -664,56 +975,43 @@ out: */ int soreceive(so, psa, uio, mp0, controlp, flagsp) - register struct socket *so; + struct socket *so; struct sockaddr **psa; struct uio *uio; struct mbuf **mp0; struct mbuf **controlp; int *flagsp; { - register struct mbuf *m, **mp; - register int flags, len, error, s, offset; + struct mbuf *m, **mp; + int flags, len, error, offset; struct protosw *pr = so->so_proto; struct mbuf *nextrecord; int moff, type = 0; int orig_resid = uio->uio_resid; mp = mp0; - if (psa) - *psa = 0; - if (controlp) - *controlp = 0; - if (flagsp) + if (psa != NULL) + *psa = NULL; + if (controlp != NULL) + *controlp = NULL; + if (flagsp != NULL) flags = *flagsp &~ MSG_EOR; else flags = 0; - if (flags & MSG_OOB) { - m = m_get(M_WAIT, MT_DATA); - if (m == NULL) - return (ENOBUFS); - error = (*pr->pr_usrreqs->pru_rcvoob)(so, m, flags & MSG_PEEK); - if (error) - goto bad; - do { - error = uiomove(mtod(m, caddr_t), - (int) min(uio->uio_resid, m->m_len), uio); - m = m_free(m); - } while (uio->uio_resid && error == 0 && m); -bad: - if (m) - m_freem(m); - return (error); - } - if (mp) - *mp = (struct mbuf *)0; - if (so->so_state & SS_ISCONFIRMING && uio->uio_resid) + if (flags & MSG_OOB) + return (soreceive_rcvoob(so, uio, flags)); + if (mp != NULL) + *mp = NULL; + if ((pr->pr_flags & PR_WANTRCVD) && (so->so_state & SS_ISCONFIRMING) + && 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) - return (error); - s = splnet(); + goto out; m = so->so_rcv.sb_mb; /* @@ -727,27 +1025,30 @@ restart: * we have to do the receive in sections, and thus risk returning * a short count if a timeout or signal occurs after we start. */ - if (m == 0 || (((flags & MSG_DONTWAIT) == 0 && + if (m == NULL || (((flags & MSG_DONTWAIT) == 0 && so->so_rcv.sb_cc < uio->uio_resid) && (so->so_rcv.sb_cc < so->so_rcv.sb_lowat || ((flags & MSG_WAITALL) && uio->uio_resid <= so->so_rcv.sb_hiwat)) && - m->m_nextpkt == 0 && (pr->pr_flags & PR_ATOMIC) == 0)) { - KASSERT(m != 0 || !so->so_rcv.sb_cc, ("receive 1")); + m->m_nextpkt == NULL && (pr->pr_flags & PR_ATOMIC) == 0)) { + KASSERT(m != NULL || !so->so_rcv.sb_cc, + ("receive: m == %p so->so_rcv.sb_cc == %u", + m, so->so_rcv.sb_cc)); if (so->so_error) { - if (m) + if (m != NULL) goto dontblock; error = so->so_error; if ((flags & MSG_PEEK) == 0) so->so_error = 0; goto release; } - if (so->so_state & SS_CANTRCVMORE) { + SOCKBUF_LOCK_ASSERT(&so->so_rcv); + if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { if (m) goto dontblock; else goto release; } - for (; m; m = m->m_next) + for (; m != NULL; m = m->m_next) if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) { m = so->so_rcv.sb_mb; goto dontblock; @@ -759,7 +1060,8 @@ restart: } if (uio->uio_resid == 0) goto release; - if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) { + if ((so->so_state & SS_NBIO) || + (flags & (MSG_DONTWAIT|MSG_NBIO))) { error = EWOULDBLOCK; goto release; } @@ -767,69 +1069,109 @@ restart: SBLASTMBUFCHK(&so->so_rcv); sbunlock(&so->so_rcv); error = sbwait(&so->so_rcv); - splx(s); if (error) - return (error); + goto out; goto restart; } dontblock: - if (uio->uio_procp) - uio->uio_procp->p_stats->p_ru.ru_msgrcv++; + /* + * From this point onward, we maintain 'nextrecord' as a cache of the + * pointer to the next record in the socket buffer. We must keep the + * various socket buffer pointers and local stack versions of the + * pointers in sync, pushing out modifications before dropping the + * socket buffer mutex, and re-reading them when picking it up. + * + * Otherwise, we will race with the network stack appending new data + * or records onto the socket buffer by using inconsistent/stale + * versions of the field, possibly resulting in socket buffer + * corruption. + * + * By holding the high-level sblock(), we prevent simultaneous + * readers from pulling off the front of the socket buffer. + */ + SOCKBUF_LOCK_ASSERT(&so->so_rcv); + if (uio->uio_td) + uio->uio_td->td_proc->p_stats->p_ru.ru_msgrcv++; + KASSERT(m == so->so_rcv.sb_mb, ("soreceive: m != so->so_rcv.sb_mb")); SBLASTRECORDCHK(&so->so_rcv); SBLASTMBUFCHK(&so->so_rcv); nextrecord = m->m_nextpkt; if (pr->pr_flags & PR_ADDR) { - KASSERT(m->m_type == MT_SONAME, ("receive 1a")); + KASSERT(m->m_type == MT_SONAME, + ("m->m_type == %d", m->m_type)); orig_resid = 0; - if (psa) - *psa = dup_sockaddr(mtod(m, struct sockaddr *), - mp0 == 0); + if (psa != NULL) + *psa = sodupsockaddr(mtod(m, struct sockaddr *), + M_NOWAIT); if (flags & MSG_PEEK) { m = m->m_next; } else { sbfree(&so->so_rcv, m); so->so_rcv.sb_mb = m_free(m); m = so->so_rcv.sb_mb; + sockbuf_pushsync(&so->so_rcv, nextrecord); } } - while (m && m->m_type == MT_CONTROL && error == 0) { - if (flags & MSG_PEEK) { - if (controlp) - *controlp = m_copy(m, 0, m->m_len); - m = m->m_next; - } else { - sbfree(&so->so_rcv, m); - if (controlp) { - if (pr->pr_domain->dom_externalize && - mtod(m, struct cmsghdr *)->cmsg_type == - SCM_RIGHTS) - error = (*pr->pr_domain->dom_externalize)(m); - *controlp = m; - so->so_rcv.sb_mb = m->m_next; - m->m_next = 0; - m = so->so_rcv.sb_mb; + + /* + * Process one or more MT_CONTROL mbufs present before any data mbufs + * in the first mbuf chain on the socket buffer. If MSG_PEEK, we + * just copy the data; if !MSG_PEEK, we call into the protocol to + * perform externalization (or freeing if controlp == NULL). + */ + if (m != NULL && m->m_type == MT_CONTROL) { + struct mbuf *cm = NULL, *cmn; + struct mbuf **cme = &cm; + + do { + if (flags & MSG_PEEK) { + if (controlp != NULL) { + *controlp = m_copy(m, 0, m->m_len); + controlp = &(*controlp)->m_next; + } + m = m->m_next; } else { - so->so_rcv.sb_mb = m_free(m); + sbfree(&so->so_rcv, m); + so->so_rcv.sb_mb = m->m_next; + m->m_next = NULL; + *cme = m; + cme = &(*cme)->m_next; m = so->so_rcv.sb_mb; } + } while (m != NULL && m->m_type == MT_CONTROL); + if ((flags & MSG_PEEK) == 0) + sockbuf_pushsync(&so->so_rcv, nextrecord); + while (cm != NULL) { + cmn = cm->m_next; + cm->m_next = NULL; + if (pr->pr_domain->dom_externalize != NULL) { + SOCKBUF_UNLOCK(&so->so_rcv); + error = (*pr->pr_domain->dom_externalize) + (cm, controlp); + SOCKBUF_LOCK(&so->so_rcv); + } else if (controlp != NULL) + *controlp = cm; + else + m_freem(cm); + if (controlp != NULL) { + orig_resid = 0; + while (*controlp != NULL) + controlp = &(*controlp)->m_next; + } + cm = cmn; } - if (controlp) { - orig_resid = 0; - controlp = &(*controlp)->m_next; - } + nextrecord = so->so_rcv.sb_mb->m_nextpkt; + orig_resid = 0; } - if (m) { + if (m != NULL) { if ((flags & MSG_PEEK) == 0) { - m->m_nextpkt = nextrecord; - /* - * If nextrecord == NULL (this is a single chain), - * then sb_lastrecord may not be valid here if m - * was changed earlier. - */ + KASSERT(m->m_nextpkt == nextrecord, + ("soreceive: post-control, nextrecord !sync")); if (nextrecord == NULL) { KASSERT(so->so_rcv.sb_mb == m, - ("receive tailq 1")); - so->so_rcv.sb_lastrecord = m; + ("soreceive: post-control, sb_mb!=m")); + KASSERT(so->so_rcv.sb_lastrecord == m, + ("soreceive: post-control, lastrecord!=m")); } } type = m->m_type; @@ -837,17 +1179,34 @@ dontblock: flags |= MSG_OOB; } else { if ((flags & MSG_PEEK) == 0) { - KASSERT(so->so_rcv.sb_mb == m,("receive tailq 2")); - so->so_rcv.sb_mb = nextrecord; - SB_EMPTY_FIXUP(&so->so_rcv); + KASSERT(so->so_rcv.sb_mb == nextrecord, + ("soreceive: sb_mb != nextrecord")); + if (so->so_rcv.sb_mb == NULL) { + KASSERT(so->so_rcv.sb_lastrecord == NULL, + ("soreceive: sb_lastercord != NULL")); + } } } + SOCKBUF_LOCK_ASSERT(&so->so_rcv); SBLASTRECORDCHK(&so->so_rcv); SBLASTMBUFCHK(&so->so_rcv); + /* + * Now continue to read any data mbufs off of the head of the socket + * buffer until the read request is satisfied. Note that 'type' is + * used to store the type of any mbuf reads that have happened so far + * such that soreceive() can stop reading if the type changes, which + * causes soreceive() to return only one of regular data and inline + * out-of-band data in a single socket receive operation. + */ moff = 0; offset = 0; - while (m && uio->uio_resid > 0 && error == 0) { + while (m != NULL && uio->uio_resid > 0 && error == 0) { + /* + * If the type of mbuf has changed since the last mbuf + * examined ('type'), end the receive operation. + */ + SOCKBUF_LOCK_ASSERT(&so->so_rcv); if (m->m_type == MT_OOBDATA) { if (type != MT_OOBDATA) break; @@ -855,8 +1214,8 @@ dontblock: break; else KASSERT(m->m_type == MT_DATA || m->m_type == MT_HEADER, - ("receive 3")); - so->so_state &= ~SS_RCVATMARK; + ("m->m_type == %d", m->m_type)); + so->so_rcv.sb_state &= ~SBS_RCVATMARK; len = uio->uio_resid; if (so->so_oobmark && len > so->so_oobmark - offset) len = so->so_oobmark - offset; @@ -870,16 +1229,33 @@ dontblock: * we must note any additions to the sockbuf when we * block interrupts again. */ - if (mp == 0) { + if (mp == NULL) { + SOCKBUF_LOCK_ASSERT(&so->so_rcv); SBLASTRECORDCHK(&so->so_rcv); SBLASTMBUFCHK(&so->so_rcv); - splx(s); - error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio); - s = splnet(); + SOCKBUF_UNLOCK(&so->so_rcv); +#ifdef ZERO_COPY_SOCKETS + if (so_zero_copy_receive) { + int disposable; + + if ((m->m_flags & M_EXT) + && (m->m_ext.ext_type == EXT_DISPOSABLE)) + disposable = 1; + else + disposable = 0; + + error = uiomoveco(mtod(m, char *) + moff, + (int)len, uio, + disposable); + } else +#endif /* ZERO_COPY_SOCKETS */ + error = uiomove(mtod(m, char *) + moff, (int)len, uio); + SOCKBUF_LOCK(&so->so_rcv); if (error) goto release; } else uio->uio_resid -= len; + SOCKBUF_LOCK_ASSERT(&so->so_rcv); if (len == m->m_len - moff) { if (m->m_flags & M_EOR) flags |= MSG_EOR; @@ -889,15 +1265,16 @@ dontblock: } else { nextrecord = m->m_nextpkt; sbfree(&so->so_rcv, m); - if (mp) { + if (mp != NULL) { *mp = m; mp = &m->m_next; so->so_rcv.sb_mb = m = m->m_next; - *mp = (struct mbuf *)0; + *mp = NULL; } else { - so->so_rcv.sb_mb = m = m_free(m); + so->so_rcv.sb_mb = m_free(m); + m = so->so_rcv.sb_mb; } - if (m) { + if (m != NULL) { m->m_nextpkt = nextrecord; if (nextrecord == NULL) so->so_rcv.sb_lastrecord = m; @@ -912,18 +1289,40 @@ dontblock: if (flags & MSG_PEEK) moff += len; else { - if (mp) - *mp = m_copym(m, 0, len, M_WAIT); + if (mp != NULL) { + int copy_flag; + + if (flags & MSG_DONTWAIT) + copy_flag = M_DONTWAIT; + else + copy_flag = M_TRYWAIT; + if (copy_flag == M_TRYWAIT) + SOCKBUF_UNLOCK(&so->so_rcv); + *mp = m_copym(m, 0, len, copy_flag); + if (copy_flag == M_TRYWAIT) + SOCKBUF_LOCK(&so->so_rcv); + if (*mp == NULL) { + /* + * m_copym() couldn't allocate an mbuf. + * Adjust uio_resid back (it was adjusted + * down by len bytes, which we didn't end + * up "copying" over). + */ + uio->uio_resid += len; + break; + } + } m->m_data += len; m->m_len -= len; so->so_rcv.sb_cc -= len; } } + SOCKBUF_LOCK_ASSERT(&so->so_rcv); if (so->so_oobmark) { if ((flags & MSG_PEEK) == 0) { so->so_oobmark -= len; if (so->so_oobmark == 0) { - so->so_state |= SS_RCVATMARK; + so->so_rcv.sb_state |= SBS_RCVATMARK; break; } } else { @@ -941,39 +1340,39 @@ dontblock: * with a short count but without error. * Keep sockbuf locked against other readers. */ - while (flags & MSG_WAITALL && m == 0 && uio->uio_resid > 0 && - !sosendallatonce(so) && !nextrecord) { - if (so->so_error || so->so_state & SS_CANTRCVMORE) + while (flags & MSG_WAITALL && m == NULL && uio->uio_resid > 0 && + !sosendallatonce(so) && nextrecord == NULL) { + SOCKBUF_LOCK_ASSERT(&so->so_rcv); + if (so->so_error || so->so_rcv.sb_state & SBS_CANTRCVMORE) break; /* - * The window might have closed to zero, make - * sure we send an ack now that we've drained - * the buffer or we might end up blocking until - * the idle takes over (5 seconds). + * Notify the protocol that some data has been + * drained before blocking. */ - if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) + if (pr->pr_flags & PR_WANTRCVD && so->so_pcb != NULL) { + SOCKBUF_UNLOCK(&so->so_rcv); (*pr->pr_usrreqs->pru_rcvd)(so, flags); + SOCKBUF_LOCK(&so->so_rcv); + } SBLASTRECORDCHK(&so->so_rcv); SBLASTMBUFCHK(&so->so_rcv); error = sbwait(&so->so_rcv); - if (error) { - sbunlock(&so->so_rcv); - splx(s); - return (0); - } + if (error) + goto release; m = so->so_rcv.sb_mb; - if (m) + if (m != NULL) nextrecord = m->m_nextpkt; } } - if (m && pr->pr_flags & PR_ATOMIC) { + SOCKBUF_LOCK_ASSERT(&so->so_rcv); + if (m != NULL && pr->pr_flags & PR_ATOMIC) { flags |= MSG_TRUNC; if ((flags & MSG_PEEK) == 0) - (void) sbdroprecord(&so->so_rcv); + (void) sbdroprecord_locked(&so->so_rcv); } if ((flags & MSG_PEEK) == 0) { - if (m == 0) { + if (m == NULL) { /* * First part is an inline SB_EMPTY_FIXUP(). Second * part makes sure sb_lastrecord is up-to-date if @@ -988,30 +1387,42 @@ dontblock: } SBLASTRECORDCHK(&so->so_rcv); SBLASTMBUFCHK(&so->so_rcv); - if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) + /* + * If soreceive() is being done from the socket callback, then + * don't need to generate ACK to peer to update window, since + * ACK will be generated on return to TCP. + */ + if (!(flags & MSG_SOCALLBCK) && + (pr->pr_flags & PR_WANTRCVD) && so->so_pcb) { + SOCKBUF_UNLOCK(&so->so_rcv); (*pr->pr_usrreqs->pru_rcvd)(so, flags); + SOCKBUF_LOCK(&so->so_rcv); + } } + SOCKBUF_LOCK_ASSERT(&so->so_rcv); if (orig_resid == uio->uio_resid && orig_resid && - (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { + (flags & MSG_EOR) == 0 && (so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0) { sbunlock(&so->so_rcv); - splx(s); goto restart; } - if (flagsp) + if (flagsp != NULL) *flagsp |= flags; release: + SOCKBUF_LOCK_ASSERT(&so->so_rcv); sbunlock(&so->so_rcv); - splx(s); +out: + SOCKBUF_LOCK_ASSERT(&so->so_rcv); + SOCKBUF_UNLOCK(&so->so_rcv); return (error); } int soshutdown(so, how) - register struct socket *so; - register int how; + struct socket *so; + int how; { - register struct protosw *pr = so->so_proto; + struct protosw *pr = so->so_proto; if (!(how == SHUT_RD || how == SHUT_WR || how == SHUT_RDWR)) return (EINVAL); @@ -1025,109 +1436,49 @@ soshutdown(so, how) void sorflush(so) - register struct socket *so; + struct socket *so; { - register struct sockbuf *sb = &so->so_rcv; - register struct protosw *pr = so->so_proto; - register int s; + struct sockbuf *sb = &so->so_rcv; + struct protosw *pr = so->so_proto; struct sockbuf asb; + /* + * XXXRW: This is quite ugly. Previously, this code made a copy of + * the socket buffer, then zero'd the original to clear the buffer + * fields. However, with mutexes in the socket buffer, this causes + * problems. We only clear the zeroable bits of the original; + * however, we have to initialize and destroy the mutex in the copy + * so that dom_dispose() and sbrelease() can lock t as needed. + */ + SOCKBUF_LOCK(sb); sb->sb_flags |= SB_NOINTR; (void) sblock(sb, M_WAITOK); - s = splimp(); - socantrcvmore(so); + /* + * 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); - asb = *sb; - bzero((caddr_t)sb, sizeof (*sb)); - if (asb.sb_flags & SB_KNOTE) { - sb->sb_sel.si_note = asb.sb_sel.si_note; - sb->sb_flags = SB_KNOTE; - } - splx(s); - if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose) + /* + * Invalidate/clear most of the sockbuf structure, but leave + * selinfo and mutex data unchanged. + */ + 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); + + SOCKBUF_LOCK_INIT(&asb, "so_rcv"); + if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL) (*pr->pr_domain->dom_dispose)(asb.sb_mb); sbrelease(&asb, so); + SOCKBUF_LOCK_DESTROY(&asb); } -#ifdef INET -static int -do_setopt_accept_filter(so, sopt) - struct socket *so; - struct sockopt *sopt; -{ - struct accept_filter_arg *afap = NULL; - struct accept_filter *afp; - struct so_accf *af = so->so_accf; - int error = 0; - - /* do not set/remove accept filters on non listen sockets */ - if ((so->so_options & SO_ACCEPTCONN) == 0) { - error = EINVAL; - goto out; - } - - /* removing the filter */ - if (sopt == NULL) { - if (af != NULL) { - if (af->so_accept_filter != NULL && - af->so_accept_filter->accf_destroy != NULL) { - af->so_accept_filter->accf_destroy(so); - } - if (af->so_accept_filter_str != NULL) { - FREE(af->so_accept_filter_str, M_ACCF); - } - FREE(af, M_ACCF); - so->so_accf = NULL; - } - so->so_options &= ~SO_ACCEPTFILTER; - return (0); - } - /* adding a filter */ - /* must remove previous filter first */ - if (af != NULL) { - error = EINVAL; - goto out; - } - /* don't put large objects on the kernel stack */ - MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, M_WAITOK); - error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); - afap->af_name[sizeof(afap->af_name)-1] = '\0'; - afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; - if (error) - goto out; - afp = accept_filt_get(afap->af_name); - if (afp == NULL) { - error = ENOENT; - goto out; - } - MALLOC(af, struct so_accf *, sizeof(*af), M_ACCF, M_WAITOK); - bzero(af, sizeof(*af)); - if (afp->accf_create != NULL) { - if (afap->af_name[0] != '\0') { - int len = strlen(afap->af_name) + 1; - - MALLOC(af->so_accept_filter_str, char *, len, M_ACCF, M_WAITOK); - strcpy(af->so_accept_filter_str, afap->af_name); - } - af->so_accept_filter_arg = afp->accf_create(so, afap->af_arg); - if (af->so_accept_filter_arg == NULL) { - FREE(af->so_accept_filter_str, M_ACCF); - FREE(af, M_ACCF); - so->so_accf = NULL; - error = EINVAL; - goto out; - } - } - af->so_accept_filter = afp; - so->so_accf = af; - so->so_options |= SO_ACCEPTFILTER; -out: - if (afap != NULL) - FREE(afap, M_TEMP); - return (error); -} -#endif /* INET */ - /* * Perhaps this routine, and sooptcopyout(), below, ought to come in * an additional variant to handle the case where the option value needs @@ -1155,13 +1506,32 @@ sooptcopyin(sopt, buf, len, minlen) if (valsize > len) sopt->sopt_valsize = valsize = len; - if (sopt->sopt_p != 0) + if (sopt->sopt_td != NULL) return (copyin(sopt->sopt_val, buf, valsize)); bcopy(sopt->sopt_val, buf, valsize); return 0; } +/* + * Kernel version of setsockopt(2)/ + * XXX: optlen is size_t, not socklen_t + */ +int +so_setsockopt(struct socket *so, int level, int optname, void *optval, + size_t optlen) +{ + struct sockopt sopt; + + sopt.sopt_level = level; + sopt.sopt_name = optname; + sopt.sopt_dir = SOPT_SET; + sopt.sopt_val = optval; + sopt.sopt_valsize = optlen; + sopt.sopt_td = NULL; + return (sosetopt(so, &sopt)); +} + int sosetopt(so, sopt) struct socket *so; @@ -1171,6 +1541,9 @@ sosetopt(so, sopt) struct linger l; struct timeval tv; u_long val; +#ifdef MAC + struct mac extmac; +#endif error = 0; if (sopt->sopt_level != SOL_SOCKET) { @@ -1186,17 +1559,19 @@ sosetopt(so, sopt) if (error) goto bad; break; -#endif /* INET */ +#endif case SO_LINGER: error = sooptcopyin(sopt, &l, sizeof l, sizeof l); if (error) goto bad; + SOCK_LOCK(so); so->so_linger = l.l_linger; if (l.l_onoff) so->so_options |= SO_LINGER; else so->so_options &= ~SO_LINGER; + SOCK_UNLOCK(so); break; case SO_DEBUG: @@ -1208,14 +1583,18 @@ sosetopt(so, sopt) case SO_REUSEPORT: case SO_OOBINLINE: case SO_TIMESTAMP: + case SO_BINTIME: + case SO_NOSIGPIPE: error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) goto bad; + SOCK_LOCK(so); if (optval) so->so_options |= sopt->sopt_name; else so->so_options &= ~sopt->sopt_name; + SOCK_UNLOCK(so); break; case SO_SNDBUF: @@ -1241,7 +1620,7 @@ sosetopt(so, sopt) case SO_RCVBUF: if (sbreserve(sopt->sopt_name == SO_SNDBUF ? &so->so_snd : &so->so_rcv, (u_long)optval, - so, curproc) == 0) { + so, curthread) == 0) { error = ENOBUFS; goto bad; } @@ -1252,14 +1631,18 @@ sosetopt(so, sopt) * the high-water. */ case SO_SNDLOWAT: + SOCKBUF_LOCK(&so->so_snd); so->so_snd.sb_lowat = (optval > so->so_snd.sb_hiwat) ? so->so_snd.sb_hiwat : optval; + SOCKBUF_UNLOCK(&so->so_snd); break; case SO_RCVLOWAT: + SOCKBUF_LOCK(&so->so_rcv); so->so_rcv.sb_lowat = (optval > so->so_rcv.sb_hiwat) ? so->so_rcv.sb_hiwat : optval; + SOCKBUF_UNLOCK(&so->so_rcv); break; } break; @@ -1272,15 +1655,15 @@ sosetopt(so, sopt) goto bad; /* assert(hz > 0); */ - if (tv.tv_sec < 0 || tv.tv_sec > SHRT_MAX / hz || + if (tv.tv_sec < 0 || tv.tv_sec > INT_MAX / hz || tv.tv_usec < 0 || tv.tv_usec >= 1000000) { error = EDOM; goto bad; } /* assert(tick > 0); */ - /* assert(ULONG_MAX - SHRT_MAX >= 1000000); */ + /* assert(ULONG_MAX - INT_MAX >= 1000000); */ val = (u_long)(tv.tv_sec * hz) + tv.tv_usec / tick; - if (val > SHRT_MAX) { + if (val > INT_MAX) { error = EDOM; goto bad; } @@ -1296,11 +1679,24 @@ sosetopt(so, sopt) break; } break; + case SO_LABEL: +#ifdef MAC + error = sooptcopyin(sopt, &extmac, sizeof extmac, + sizeof extmac); + if (error) + goto bad; + error = mac_setsockopt_label(sopt->sopt_td->td_ucred, + so, &extmac); +#else + error = EOPNOTSUPP; +#endif + break; default: error = ENOPROTOOPT; break; } - if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) { + if (error == 0 && so->so_proto != NULL && + so->so_proto->pr_ctloutput != NULL) { (void) ((*so->so_proto->pr_ctloutput) (so, sopt)); } @@ -1329,8 +1725,8 @@ sooptcopyout(struct sockopt *sopt, const void *buf, size_t len) */ valsize = min(len, sopt->sopt_valsize); sopt->sopt_valsize = valsize; - if (sopt->sopt_val != 0) { - if (sopt->sopt_p != 0) + if (sopt->sopt_val != NULL) { + if (sopt->sopt_td != NULL) error = copyout(buf, sopt->sopt_val, valsize); else bcopy(buf, sopt->sopt_val, valsize); @@ -1346,7 +1742,9 @@ sogetopt(so, sopt) int error, optval; struct linger l; struct timeval tv; - struct accept_filter_arg *afap; +#ifdef MAC + struct mac extmac; +#endif error = 0; if (sopt->sopt_level != SOL_SOCKET) { @@ -1359,24 +1757,14 @@ sogetopt(so, sopt) switch (sopt->sopt_name) { #ifdef INET case SO_ACCEPTFILTER: - if ((so->so_options & SO_ACCEPTCONN) == 0) - return (EINVAL); - MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), - M_TEMP, M_WAITOK); - bzero(afap, sizeof(*afap)); - if ((so->so_options & SO_ACCEPTFILTER) != 0) { - strcpy(afap->af_name, so->so_accf->so_accept_filter->accf_name); - if (so->so_accf->so_accept_filter_str != NULL) - strcpy(afap->af_arg, so->so_accf->so_accept_filter_str); - } - error = sooptcopyout(sopt, afap, sizeof(*afap)); - FREE(afap, M_TEMP); + error = do_getopt_accept_filter(so, sopt); break; -#endif /* INET */ - +#endif case SO_LINGER: + SOCK_LOCK(so); l.l_onoff = so->so_options & SO_LINGER; l.l_linger = so->so_linger; + SOCK_UNLOCK(so); error = sooptcopyout(sopt, &l, sizeof l); break; @@ -1389,6 +1777,8 @@ sogetopt(so, sopt) case SO_BROADCAST: case SO_OOBINLINE: case SO_TIMESTAMP: + case SO_BINTIME: + case SO_NOSIGPIPE: optval = so->so_options & sopt->sopt_name; integer: error = sooptcopyout(sopt, &optval, sizeof optval); @@ -1427,8 +1817,37 @@ integer: tv.tv_sec = optval / hz; tv.tv_usec = (optval % hz) * tick; error = sooptcopyout(sopt, &tv, sizeof tv); - break; - + break; + case SO_LABEL: +#ifdef MAC + error = sooptcopyin(sopt, &extmac, sizeof(extmac), + sizeof(extmac)); + if (error) + return (error); + error = mac_getsockopt_label(sopt->sopt_td->td_ucred, + so, &extmac); + if (error) + return (error); + error = sooptcopyout(sopt, &extmac, sizeof extmac); +#else + error = EOPNOTSUPP; +#endif + break; + case SO_PEERLABEL: +#ifdef MAC + error = sooptcopyin(sopt, &extmac, sizeof(extmac), + sizeof(extmac)); + if (error) + return (error); + error = mac_getsockopt_peerlabel( + sopt->sopt_td->td_ucred, so, &extmac); + if (error) + return (error); + error = sooptcopyout(sopt, &extmac, sizeof extmac); +#else + error = EOPNOTSUPP; +#endif + break; default: error = ENOPROTOOPT; break; @@ -1444,11 +1863,11 @@ soopt_getm(struct sockopt *sopt, struct mbuf **mp) struct mbuf *m, *m_prev; int sopt_size = sopt->sopt_valsize; - MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_DATA); - if (m == 0) + MGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT, MT_DATA); + if (m == NULL) return ENOBUFS; if (sopt_size > MLEN) { - MCLGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT); + MCLGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); return ENOBUFS; @@ -1462,14 +1881,16 @@ soopt_getm(struct sockopt *sopt, struct mbuf **mp) m_prev = m; while (sopt_size) { - MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_DATA); - if (m == 0) { + MGET(m, sopt->sopt_td ? M_TRYWAIT : M_DONTWAIT, MT_DATA); + if (m == NULL) { m_freem(*mp); return ENOBUFS; } if (sopt_size > MLEN) { - MCLGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT); + MCLGET(m, sopt->sopt_td != NULL ? M_TRYWAIT : + M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { + m_freem(m); m_freem(*mp); return ENOBUFS; } @@ -1493,7 +1914,7 @@ soopt_mcopyin(struct sockopt *sopt, struct mbuf *m) if (sopt->sopt_val == NULL) return 0; while (m != NULL && sopt->sopt_valsize >= m->m_len) { - if (sopt->sopt_p != NULL) { + if (sopt->sopt_td != NULL) { int error; error = copyin(sopt->sopt_val, mtod(m, char *), @@ -1505,7 +1926,7 @@ soopt_mcopyin(struct sockopt *sopt, struct mbuf *m) } else bcopy(sopt->sopt_val, mtod(m, char *), m->m_len); sopt->sopt_valsize -= m->m_len; - (caddr_t)sopt->sopt_val += m->m_len; + sopt->sopt_val = (char *)sopt->sopt_val + m->m_len; m = m->m_next; } if (m != NULL) /* should be allocated enoughly at ip6_sooptmcopyin() */ @@ -1523,7 +1944,7 @@ soopt_mcopyout(struct sockopt *sopt, struct mbuf *m) if (sopt->sopt_val == NULL) return 0; while (m != NULL && sopt->sopt_valsize >= m->m_len) { - if (sopt->sopt_p != NULL) { + if (sopt->sopt_td != NULL) { int error; error = copyout(mtod(m, char *), sopt->sopt_val, @@ -1535,7 +1956,7 @@ soopt_mcopyout(struct sockopt *sopt, struct mbuf *m) } else bcopy(mtod(m, char *), sopt->sopt_val, m->m_len); sopt->sopt_valsize -= m->m_len; - (caddr_t)sopt->sopt_val += m->m_len; + sopt->sopt_val = (char *)sopt->sopt_val + m->m_len; valsize += m->m_len; m = m->m_next; } @@ -1550,62 +1971,69 @@ soopt_mcopyout(struct sockopt *sopt, struct mbuf *m) void sohasoutofband(so) - register struct socket *so; + struct socket *so; { if (so->so_sigio != NULL) - pgsigio(so->so_sigio, SIGURG, 0); - selwakeup(&so->so_rcv.sb_sel); + pgsigio(&so->so_sigio, SIGURG, 0); + selwakeuppri(&so->so_rcv.sb_sel, PSOCK); } int -sopoll(struct socket *so, int events, struct ucred *cred, struct proc *p) +sopoll(struct socket *so, int events, struct ucred *active_cred, + struct thread *td) { int revents = 0; - int s = splnet(); + SOCKBUF_LOCK(&so->so_snd); + SOCKBUF_LOCK(&so->so_rcv); if (events & (POLLIN | POLLRDNORM)) if (soreadable(so)) revents |= events & (POLLIN | POLLRDNORM); + if (events & POLLINIGNEOF) + if (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat || + !TAILQ_EMPTY(&so->so_comp) || so->so_error) + revents |= POLLINIGNEOF; + if (events & (POLLOUT | POLLWRNORM)) if (sowriteable(so)) revents |= events & (POLLOUT | POLLWRNORM); if (events & (POLLPRI | POLLRDBAND)) - if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) + if (so->so_oobmark || (so->so_rcv.sb_state & SBS_RCVATMARK)) revents |= events & (POLLPRI | POLLRDBAND); if (revents == 0) { - if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { - selrecord(p, &so->so_rcv.sb_sel); + if (events & + (POLLIN | POLLINIGNEOF | POLLPRI | POLLRDNORM | + POLLRDBAND)) { + selrecord(td, &so->so_rcv.sb_sel); so->so_rcv.sb_flags |= SB_SEL; } if (events & (POLLOUT | POLLWRNORM)) { - selrecord(p, &so->so_snd.sb_sel); + selrecord(td, &so->so_snd.sb_sel); so->so_snd.sb_flags |= SB_SEL; } } - splx(s); + SOCKBUF_UNLOCK(&so->so_rcv); + SOCKBUF_UNLOCK(&so->so_snd); return (revents); } int -sokqfilter(struct file *fp, struct knote *kn) +soo_kqfilter(struct file *fp, struct knote *kn) { - struct socket *so = (struct socket *)kn->kn_fp->f_data; + struct socket *so = kn->kn_fp->f_data; struct sockbuf *sb; - int s; switch (kn->kn_filter) { case EVFILT_READ: if (so->so_options & SO_ACCEPTCONN) kn->kn_fop = &solisten_filtops; - else if (so->so_state & SS_ISCONNECTED) - kn->kn_fop = &soread_filtops; else - return (EINVAL); + kn->kn_fop = &soread_filtops; sb = &so->so_rcv; break; case EVFILT_WRITE: @@ -1613,87 +2041,121 @@ sokqfilter(struct file *fp, struct knote *kn) sb = &so->so_snd; break; default: - return (1); + return (EINVAL); } - s = splnet(); - SLIST_INSERT_HEAD(&sb->sb_sel.si_note, kn, kn_selnext); + SOCKBUF_LOCK(sb); + knlist_add(&sb->sb_sel.si_note, kn, 1); sb->sb_flags |= SB_KNOTE; - splx(s); + SOCKBUF_UNLOCK(sb); return (0); } static void filt_sordetach(struct knote *kn) { - struct socket *so = (struct socket *)kn->kn_fp->f_data; - int s = splnet(); + struct socket *so = kn->kn_fp->f_data; - SLIST_REMOVE(&so->so_rcv.sb_sel.si_note, kn, knote, kn_selnext); - if (SLIST_EMPTY(&so->so_rcv.sb_sel.si_note)) + SOCKBUF_LOCK(&so->so_rcv); + knlist_remove(&so->so_rcv.sb_sel.si_note, kn, 1); + if (knlist_empty(&so->so_rcv.sb_sel.si_note)) so->so_rcv.sb_flags &= ~SB_KNOTE; - splx(s); + SOCKBUF_UNLOCK(&so->so_rcv); } /*ARGSUSED*/ static int filt_soread(struct knote *kn, long hint) { - struct socket *so = (struct socket *)kn->kn_fp->f_data; + struct socket *so; - kn->kn_data = so->so_rcv.sb_cc; - if (so->so_state & SS_CANTRCVMORE) { - kn->kn_flags |= EV_EOF; + so = kn->kn_fp->f_data; + SOCKBUF_LOCK_ASSERT(&so->so_rcv); + + kn->kn_data = so->so_rcv.sb_cc - so->so_rcv.sb_ctl; + if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { + kn->kn_flags |= EV_EOF; kn->kn_fflags = so->so_error; return (1); - } - if (so->so_error) /* temporary udp error */ + } else if (so->so_error) /* temporary udp error */ return (1); - if (kn->kn_sfflags & NOTE_LOWAT) + else if (kn->kn_sfflags & NOTE_LOWAT) return (kn->kn_data >= kn->kn_sdata); - return (kn->kn_data >= so->so_rcv.sb_lowat); + else + return (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat); } static void filt_sowdetach(struct knote *kn) { - struct socket *so = (struct socket *)kn->kn_fp->f_data; - int s = splnet(); + struct socket *so = kn->kn_fp->f_data; - SLIST_REMOVE(&so->so_snd.sb_sel.si_note, kn, knote, kn_selnext); - if (SLIST_EMPTY(&so->so_snd.sb_sel.si_note)) + SOCKBUF_LOCK(&so->so_snd); + knlist_remove(&so->so_snd.sb_sel.si_note, kn, 1); + if (knlist_empty(&so->so_snd.sb_sel.si_note)) so->so_snd.sb_flags &= ~SB_KNOTE; - splx(s); + SOCKBUF_UNLOCK(&so->so_snd); } /*ARGSUSED*/ static int filt_sowrite(struct knote *kn, long hint) { - struct socket *so = (struct socket *)kn->kn_fp->f_data; + struct socket *so; + so = kn->kn_fp->f_data; + SOCKBUF_LOCK_ASSERT(&so->so_snd); kn->kn_data = sbspace(&so->so_snd); - if (so->so_state & SS_CANTSENDMORE) { - kn->kn_flags |= EV_EOF; + if (so->so_snd.sb_state & SBS_CANTSENDMORE) { + kn->kn_flags |= EV_EOF; kn->kn_fflags = so->so_error; return (1); - } - if (so->so_error) /* temporary udp error */ + } else if (so->so_error) /* temporary udp error */ return (1); - if (((so->so_state & SS_ISCONNECTED) == 0) && + else if (((so->so_state & SS_ISCONNECTED) == 0) && (so->so_proto->pr_flags & PR_CONNREQUIRED)) return (0); - if (kn->kn_sfflags & NOTE_LOWAT) + else if (kn->kn_sfflags & NOTE_LOWAT) return (kn->kn_data >= kn->kn_sdata); - return (kn->kn_data >= so->so_snd.sb_lowat); + else + return (kn->kn_data >= so->so_snd.sb_lowat); } /*ARGSUSED*/ static int filt_solisten(struct knote *kn, long hint) { - struct socket *so = (struct socket *)kn->kn_fp->f_data; + struct socket *so = kn->kn_fp->f_data; kn->kn_data = so->so_qlen; return (! TAILQ_EMPTY(&so->so_comp)); } + +int +socheckuid(struct socket *so, uid_t uid) +{ + + if (so == NULL) + return (EPERM); + if (so->so_cred->cr_uid != uid) + return (EPERM); + return (0); +} + +static int +somaxconn_sysctl(SYSCTL_HANDLER_ARGS) +{ + int error; + int val; + + val = somaxconn; + error = sysctl_handle_int(oidp, &val, sizeof(int), req); + if (error || !req->newptr ) + return (error); + + if (val < 1 || val > USHRT_MAX) + return (EINVAL); + + somaxconn = val; + return (0); +} |