URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/rtos/rtems/c/src/libnetworking/kern
- from Rev 30 to Rev 173
- ↔ Reverse comparison
Rev 30 → Rev 173
/uipc_domain.c
0,0 → 1,228
/* |
* Copyright (c) 1982, 1986, 1993 |
* The Regents of the University of California. All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
* |
* @(#)uipc_domain.c 8.2 (Berkeley) 10/18/93 |
* $Id: uipc_domain.c,v 1.2 2001-09-27 12:01:51 chris Exp $ |
*/ |
|
#include <sys/param.h> |
#include <sys/socket.h> |
#include <sys/protosw.h> |
#include <sys/domain.h> |
#include <sys/mbuf.h> |
#include <sys/kernel.h> |
#include <sys/systm.h> |
|
/* |
* System initialization |
* |
* Note: domain initialization wants to take place on a per domain basis |
* as a result of traversing a linker set. Most likely, each domain |
* want to call a registration function rather than being handled here |
* in domaininit(). Probably this will look like: |
* |
* SYSINIT(unique, SI_SUB_PROTO_DOMAI, SI_ORDER_ANY, domain_add, xxx) |
* |
* Where 'xxx' is replaced by the address of a parameter struct to be |
* passed to the doamin_add() function. |
*/ |
|
#if !defined(__rtems__) |
static int x_save_spl; /* used by kludge*/ |
static void kludge_splimp __P((void *)); |
static void kludge_splx __P((void *)); |
void domaininit __P((void *)); |
SYSINIT(splimp, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, kludge_splimp, &x_save_spl) |
SYSINIT(domain, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, domaininit, NULL) |
SYSINIT(splx, SI_SUB_PROTO_END, SI_ORDER_FIRST, kludge_splx, &x_save_spl) |
#endif |
|
static void pffasttimo __P((void *)); |
static void pfslowtimo __P((void *)); |
|
struct domain *domains; |
|
#define ADDDOMAIN(x) { \ |
__CONCAT(x,domain.dom_next) = domains; \ |
domains = &__CONCAT(x,domain); \ |
} |
|
extern struct linker_set domain_set; |
|
/* ARGSUSED*/ |
void |
domaininit(dummy) |
void *dummy; |
{ |
register struct domain *dp; |
register struct protosw *pr; |
|
/* - not in our sources |
#ifdef ISDN |
ADDDOMAIN(isdn); |
#endif |
*/ |
|
for (dp = domains; dp; dp = dp->dom_next) { |
if (dp->dom_init) |
(*dp->dom_init)(); |
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++){ |
#ifdef PRU_OLDSTYLE |
/* See comments in uipc_socket2.c. */ |
if (pr->pr_usrreqs == 0 && pr->pr_ousrreq) |
pr->pr_usrreqs = &pru_oldstyle; |
#endif |
if (pr->pr_init) |
(*pr->pr_init)(); |
} |
} |
|
if (max_linkhdr < 16) /* XXX */ |
max_linkhdr = 16; |
max_hdr = max_linkhdr + max_protohdr; |
max_datalen = MHLEN - max_hdr; |
timeout(pffasttimo, (void *)0, 1); |
timeout(pfslowtimo, (void *)0, 1); |
} |
|
|
#if !defined(__rtems__) |
/* |
* The following two operations are kludge code. Most likely, they should |
* be done as a "domainpreinit()" for the first function and then rolled |
* in as the last act of "domaininit()" for the second. |
* |
* In point of fact, it is questionable why other initialization prior |
* to this does not also take place at splimp by default. |
*/ |
static void |
kludge_splimp(udata) |
void *udata; |
{ |
int *savesplp = udata; |
|
*savesplp = splimp(); |
} |
|
static void |
kludge_splx(udata) |
void *udata; |
{ |
int *savesplp = udata; |
|
splx( *savesplp); |
} |
#endif /* !defined(__rtems__) */ |
|
struct protosw * |
pffindtype(int family, int type) |
{ |
register struct domain *dp; |
register struct protosw *pr; |
|
for (dp = domains; dp; dp = dp->dom_next) |
if (dp->dom_family == family) |
goto found; |
return (0); |
found: |
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
if (pr->pr_type && pr->pr_type == type) |
return (pr); |
return (0); |
} |
|
struct protosw * |
pffindproto(int family, int protocol, int type) |
{ |
register struct domain *dp; |
register struct protosw *pr; |
struct protosw *maybe = 0; |
|
if (family == 0) |
return (0); |
for (dp = domains; dp; dp = dp->dom_next) |
if (dp->dom_family == family) |
goto found; |
return (0); |
found: |
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { |
if ((pr->pr_protocol == protocol) && (pr->pr_type == type)) |
return (pr); |
|
if (type == SOCK_RAW && pr->pr_type == SOCK_RAW && |
pr->pr_protocol == 0 && maybe == (struct protosw *)0) |
maybe = pr; |
} |
return (maybe); |
} |
|
void |
pfctlinput(cmd, sa) |
int cmd; |
struct sockaddr *sa; |
{ |
register struct domain *dp; |
register struct protosw *pr; |
|
for (dp = domains; dp; dp = dp->dom_next) |
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
if (pr->pr_ctlinput) |
(*pr->pr_ctlinput)(cmd, sa, (void *)0); |
} |
|
static void |
pfslowtimo(arg) |
void *arg; |
{ |
register struct domain *dp; |
register struct protosw *pr; |
|
for (dp = domains; dp; dp = dp->dom_next) |
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
if (pr->pr_slowtimo) |
(*pr->pr_slowtimo)(); |
timeout(pfslowtimo, (void *)0, hz/2); |
} |
|
static void |
pffasttimo(arg) |
void *arg; |
{ |
register struct domain *dp; |
register struct protosw *pr; |
|
for (dp = domains; dp; dp = dp->dom_next) |
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
if (pr->pr_fasttimo) |
(*pr->pr_fasttimo)(); |
timeout(pffasttimo, (void *)0, hz/5); |
} |
/Makefile.am
0,0 → 1,35
## |
## $Id: Makefile.am,v 1.2 2001-09-27 12:01:50 chris Exp $ |
## |
|
AUTOMAKE_OPTIONS = foreign 1.4 |
|
LIBNAME = lib.a |
LIB = $(ARCH)/$(LIBNAME) |
|
C_FILES = kern_subr.c uipc_domain.c uipc_mbuf.c uipc_socket.c uipc_socket2.c |
C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o) |
|
OBJS = $(C_O_FILES) |
|
include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg |
include $(top_srcdir)/../../../automake/lib.am |
|
# |
# Add local stuff here using += |
# |
|
AM_CPPFLAGS += -D_COMPILING_BSD_KERNEL_ -DKERNEL -DINET -DNFS -DDIAGNOSTIC \ |
-DBOOTP_COMPAT |
|
$(LIB): $(OBJS) |
$(make-library) |
|
all-local: $(ARCH) $(OBJS) $(LIB) |
|
.PRECIOUS: $(LIB) |
|
EXTRA_DIST = kern_subr.c uipc_domain.c uipc_mbuf.c uipc_socket.c \ |
uipc_socket2.c |
|
include $(top_srcdir)/../../../automake/local.am |
/uipc_socket.c
0,0 → 1,1143
/* |
* Copyright (c) 1982, 1986, 1988, 1990, 1993 |
* The Regents of the University of California. All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
* |
* @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94 |
* $Id: uipc_socket.c,v 1.2 2001-09-27 12:01:51 chris Exp $ |
*/ |
|
#include <sys/param.h> |
#include <sys/queue.h> |
#include <sys/systm.h> |
#include <sys/proc.h> |
#include <sys/file.h> |
#include <sys/malloc.h> |
#include <sys/mbuf.h> |
#include <sys/domain.h> |
#include <sys/kernel.h> |
#include <sys/protosw.h> |
#include <sys/socket.h> |
#include <sys/socketvar.h> |
#include <sys/resourcevar.h> |
#include <sys/signalvar.h> |
#include <sys/sysctl.h> |
#include <limits.h> |
|
static int somaxconn = SOMAXCONN; |
SYSCTL_INT(_kern, KERN_SOMAXCONN, somaxconn, CTLFLAG_RW, &somaxconn, 0, ""); |
|
/* |
* Socket operation routines. |
* These routines are called by the routines in |
* sys_socket.c or from a system process, and |
* implement the semantics of socket operations by |
* switching out to the protocol specific routines. |
*/ |
/*ARGSUSED*/ |
int |
socreate(dom, aso, type, proto, p) |
int dom; |
struct socket **aso; |
register int type; |
int proto; |
struct proc *p; |
{ |
register struct protosw *prp; |
register struct socket *so; |
register int error; |
|
if (proto) |
prp = pffindproto(dom, proto, type); |
else |
prp = pffindtype(dom, type); |
if (prp == 0 || prp->pr_usrreqs == 0) |
return (EPROTONOSUPPORT); |
if (prp->pr_type != type) |
return (EPROTOTYPE); |
MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_WAIT); |
bzero((caddr_t)so, sizeof(*so)); |
TAILQ_INIT(&so->so_incomp); |
TAILQ_INIT(&so->so_comp); |
so->so_type = type; |
so->so_state = SS_PRIV; |
so->so_uid = 0; |
so->so_proto = prp; |
error = (*prp->pr_usrreqs->pru_attach)(so, proto); |
if (error) { |
so->so_state |= SS_NOFDREF; |
sofree(so); |
return (error); |
} |
*aso = so; |
return (0); |
} |
|
int |
sobind(so, nam) |
struct socket *so; |
struct mbuf *nam; |
{ |
int s = splnet(); |
int error; |
|
error = (*so->so_proto->pr_usrreqs->pru_bind)(so, nam); |
splx(s); |
return (error); |
} |
|
int |
solisten(so, backlog) |
register struct socket *so; |
int backlog; |
{ |
int s = splnet(), error; |
|
error = (*so->so_proto->pr_usrreqs->pru_listen)(so); |
if (error) { |
splx(s); |
return (error); |
} |
if (so->so_comp.tqh_first == NULL) |
so->so_options |= SO_ACCEPTCONN; |
if (backlog < 0 || backlog > somaxconn) |
backlog = somaxconn; |
so->so_qlimit = backlog; |
splx(s); |
return (0); |
} |
|
void |
sofree(so) |
register struct socket *so; |
{ |
struct socket *head = so->so_head; |
|
if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) |
return; |
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) { |
TAILQ_REMOVE(&head->so_comp, so, so_list); |
} else { |
panic("sofree: not queued"); |
} |
head->so_qlen--; |
so->so_state &= ~(SS_INCOMP|SS_COMP); |
so->so_head = NULL; |
} |
sbrelease(&so->so_snd); |
sorflush(so); |
FREE(so, M_SOCKET); |
} |
|
/* |
* Close a socket on last file table reference removal. |
* Initiate disconnect if connected. |
* Free socket when disconnect complete. |
*/ |
int |
soclose(so) |
register struct socket *so; |
{ |
int s = splnet(); /* conservative */ |
int error = 0; |
|
if (so->so_options & SO_ACCEPTCONN) { |
struct socket *sp, *sonext; |
|
for (sp = so->so_incomp.tqh_first; sp != NULL; sp = sonext) { |
sonext = sp->so_list.tqe_next; |
(void) soabort(sp); |
} |
for (sp = so->so_comp.tqh_first; sp != NULL; sp = sonext) { |
sonext = sp->so_list.tqe_next; |
(void) soabort(sp); |
} |
} |
if (so->so_pcb == 0) |
goto discard; |
if (so->so_state & SS_ISCONNECTED) { |
if ((so->so_state & SS_ISDISCONNECTING) == 0) { |
error = sodisconnect(so); |
if (error) |
goto drop; |
} |
if (so->so_options & SO_LINGER) { |
if ((so->so_state & SS_ISDISCONNECTING) && |
(so->so_state & SS_NBIO)) |
goto drop; |
while (so->so_state & SS_ISCONNECTED) { |
soconnsleep (so); |
} |
} |
} |
drop: |
if (so->so_pcb) { |
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"); |
so->so_state |= SS_NOFDREF; |
sofree(so); |
splx(s); |
return (error); |
} |
|
/* |
* Must be called at splnet... |
*/ |
int |
soabort(so) |
struct socket *so; |
{ |
|
return (*so->so_proto->pr_usrreqs->pru_abort)(so); |
} |
|
int |
soaccept(so, nam) |
register struct socket *so; |
struct mbuf *nam; |
{ |
int s = splnet(); |
int error; |
|
if ((so->so_state & SS_NOFDREF) == 0) |
panic("soaccept: !NOFDREF"); |
so->so_state &= ~SS_NOFDREF; |
error = (*so->so_proto->pr_usrreqs->pru_accept)(so, nam); |
splx(s); |
return (error); |
} |
|
int |
soconnect(so, nam) |
register struct socket *so; |
struct mbuf *nam; |
{ |
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. |
* This allows user to disconnect by connecting to, e.g., |
* a null address. |
*/ |
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) && |
((so->so_proto->pr_flags & PR_CONNREQUIRED) || |
(error = sodisconnect(so)))) |
error = EISCONN; |
else |
error = (*so->so_proto->pr_usrreqs->pru_connect)(so, nam); |
splx(s); |
return (error); |
} |
|
int |
soconnect2(so1, so2) |
register 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); |
} |
|
int |
sodisconnect(so) |
register 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; |
} |
error = (*so->so_proto->pr_usrreqs->pru_disconnect)(so); |
bad: |
splx(s); |
return (error); |
} |
|
#define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK) |
/* |
* Send on a socket. |
* If send must go all at once and message is larger than |
* send buffering, then hard error. |
* Lock against other senders. |
* If must go all at once and not enough room now, then |
* inform user that this would block and do nothing. |
* Otherwise, if nonblocking, send as much as possible. |
* The data to be sent is described by "uio" if nonzero, |
* otherwise by the mbuf chain "top" (which must be null |
* if uio is not). Data provided in mbuf chain must be small |
* enough to send all at once. |
* |
* Returns nonzero on error, timeout or signal; callers |
* must check for short counts if EINTR/ERESTART are returned. |
* Data and control buffers are freed on return. |
*/ |
int |
sosend(so, addr, uio, top, control, flags) |
register struct socket *so; |
struct mbuf *addr; |
struct uio *uio; |
struct mbuf *top; |
struct mbuf *control; |
int flags; |
{ |
struct mbuf **mp; |
register struct mbuf *m; |
register long space, len, resid; |
int clen = 0, error, s, dontroute, mlen; |
int atomic = sosendallatonce(so) || top; |
|
if (uio) |
resid = uio->uio_resid; |
else |
resid = top->m_pkthdr.len; |
/* |
* In theory resid should be unsigned. |
* However, space must be signed, as it might be less than 0 |
* if we over-committed, and we must use a signed comparison |
* of space and resid. On the other hand, a negative resid |
* causes us to loop sending 0-length segments to the protocol. |
* |
* Also check to make sure that MSG_EOR isn't used on SOCK_STREAM |
* type sockets since that's an error. |
*/ |
if ((resid < 0) || (so->so_type == SOCK_STREAM && (flags & MSG_EOR))) { |
error = EINVAL; |
goto out; |
} |
|
dontroute = |
(flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && |
(so->so_proto->pr_flags & PR_ATOMIC); |
if (control) |
clen = control->m_len; |
#define snderr(errno) { error = errno; splx(s); goto release; } |
|
restart: |
error = sblock(&so->so_snd, SBLOCKWAIT(flags)); |
if (error) |
goto out; |
do { |
s = splnet(); |
if (so->so_state & SS_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) { |
/* |
* `sendto' and `sendmsg' is allowed on a connection- |
* based socket if it supports implied connect. |
* Return ENOTCONN if not connected and no address is |
* supplied. |
*/ |
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 == 0) |
snderr(so->so_proto->pr_flags & PR_CONNREQUIRED ? |
ENOTCONN : EDESTADDRREQ); |
} |
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); |
if (space < resid + clen && uio && |
(atomic || space < so->so_snd.sb_lowat || space < clen)) { |
if (so->so_state & SS_NBIO) |
snderr(EWOULDBLOCK); |
sbunlock(&so->so_snd); |
error = sbwait(&so->so_snd); |
splx(s); |
if (error) |
goto out; |
goto restart; |
} |
splx(s); |
mp = ⊤ |
space -= clen; |
do { |
if (uio == NULL) { |
/* |
* Data is prepackaged in "top". |
*/ |
resid = 0; |
if (flags & MSG_EOR) |
top->m_flags |= M_EOR; |
} else do { |
if (top == 0) { |
MGETHDR(m, M_WAIT, MT_DATA); |
mlen = MHLEN; |
m->m_pkthdr.len = 0; |
m->m_pkthdr.rcvif = (struct ifnet *)0; |
} else { |
MGET(m, M_WAIT, MT_DATA); |
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); |
} |
space -= len; |
error = uiomove(mtod(m, caddr_t), (int)len, uio); |
resid = uio->uio_resid; |
m->m_len = len; |
*mp = m; |
top->m_pkthdr.len += len; |
if (error) |
goto release; |
mp = &m->m_next; |
if (resid <= 0) { |
if (flags & MSG_EOR) |
top->m_flags |= M_EOR; |
break; |
} |
} while (space > 0 && atomic); |
if (dontroute) |
so->so_options |= SO_DONTROUTE; |
s = splnet(); /* XXX */ |
error = (*so->so_proto->pr_usrreqs->pru_send)(so, |
(flags & MSG_OOB) ? PRUS_OOB : |
/* |
* If the user set MSG_EOF, the protocol |
* understands this flag and nothing left to |
* send then use PRU_SEND_EOF instead of PRU_SEND. |
*/ |
((flags & MSG_EOF) && |
(so->so_proto->pr_flags & PR_IMPLOPCL) && |
(resid <= 0)) ? |
PRUS_EOF : 0, |
top, addr, control); |
splx(s); |
if (dontroute) |
so->so_options &= ~SO_DONTROUTE; |
clen = 0; |
control = 0; |
top = 0; |
mp = ⊤ |
if (error) |
goto release; |
} while (resid && space > 0); |
} while (resid); |
|
release: |
sbunlock(&so->so_snd); |
out: |
if (top) |
m_freem(top); |
if (control) |
m_freem(control); |
return (error); |
} |
|
/* |
* 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) |
* must begin with an address if the protocol so specifies, |
* followed by an optional mbuf or mbufs containing ancillary data, |
* and then zero or more mbufs of data. |
* In order to avoid blocking network interrupts for the entire time here, |
* we splx() while doing the actual copy to user space. |
* Although the sockbuf is locked, new data may still be appended, |
* and thus we must maintain consistency of the sockbuf during that time. |
* |
* The caller may receive the data as a single mbuf chain by supplying |
* an mbuf **mp0 for use in returning the chain. The uio is then used |
* only for the count in uio_resid. |
*/ |
int |
soreceive(so, paddr, uio, mp0, controlp, flagsp) |
register struct socket *so; |
struct mbuf **paddr; |
struct uio *uio; |
struct mbuf **mp0; |
struct mbuf **controlp; |
int *flagsp; |
{ |
register struct mbuf *m, **mp; |
register int flags, len, error, s, offset; |
struct protosw *pr = so->so_proto; |
struct mbuf *nextrecord; |
int moff, type = 0; |
int orig_resid = uio->uio_resid; |
|
mp = mp0; |
if (paddr) |
*paddr = 0; |
if (controlp) |
*controlp = 0; |
if (flagsp) |
flags = *flagsp &~ MSG_EOR; |
else |
flags = 0; |
if (flags & MSG_OOB) { |
m = m_get(M_WAIT, MT_DATA); |
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) |
(*pr->pr_usrreqs->pru_rcvd)(so, 0); |
|
restart: |
error = sblock(&so->so_rcv, SBLOCKWAIT(flags)); |
if (error) |
return (error); |
s = splnet(); |
|
m = so->so_rcv.sb_mb; |
/* |
* If we have less data than requested, block awaiting more |
* (subject to any timeout) if: |
* 1. the current count is less than the low water mark, or |
* 2. MSG_WAITALL is set, and it is possible to do the entire |
* receive operation at once if we block (resid <= hiwat). |
* 3. MSG_DONTWAIT is not set |
* If MSG_WAITALL is set but resid is larger than the receive buffer, |
* 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 && |
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)) { |
#ifdef DIAGNOSTIC |
if (m == 0 && so->so_rcv.sb_cc) |
panic("receive 1"); |
#endif |
if (so->so_error) { |
if (m) |
goto dontblock; |
error = so->so_error; |
if ((flags & MSG_PEEK) == 0) |
so->so_error = 0; |
goto release; |
} |
if (so->so_state & SS_CANTRCVMORE) { |
if (m) |
goto dontblock; |
else |
goto release; |
} |
for (; m; m = m->m_next) |
if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) { |
m = so->so_rcv.sb_mb; |
goto dontblock; |
} |
if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 && |
(so->so_proto->pr_flags & PR_CONNREQUIRED)) { |
error = ENOTCONN; |
goto release; |
} |
if (uio->uio_resid == 0) |
goto release; |
if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) { |
error = EWOULDBLOCK; |
goto release; |
} |
sbunlock(&so->so_rcv); |
error = sbwait(&so->so_rcv); |
splx(s); |
if (error) |
return (error); |
goto restart; |
} |
dontblock: |
nextrecord = m->m_nextpkt; |
if (pr->pr_flags & PR_ADDR) { |
#ifdef DIAGNOSTIC |
if (m->m_type != MT_SONAME) |
panic("receive 1a"); |
#endif |
orig_resid = 0; |
if (flags & MSG_PEEK) { |
if (paddr) |
*paddr = m_copy(m, 0, m->m_len); |
m = m->m_next; |
} else { |
sbfree(&so->so_rcv, m); |
if (paddr) { |
*paddr = m; |
so->so_rcv.sb_mb = m->m_next; |
m->m_next = 0; |
m = so->so_rcv.sb_mb; |
} else { |
MFREE(m, so->so_rcv.sb_mb); |
m = so->so_rcv.sb_mb; |
} |
} |
} |
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; |
} else { |
MFREE(m, so->so_rcv.sb_mb); |
m = so->so_rcv.sb_mb; |
} |
} |
if (controlp) { |
orig_resid = 0; |
controlp = &(*controlp)->m_next; |
} |
} |
if (m) { |
if ((flags & MSG_PEEK) == 0) |
m->m_nextpkt = nextrecord; |
type = m->m_type; |
if (type == MT_OOBDATA) |
flags |= MSG_OOB; |
} |
moff = 0; |
offset = 0; |
while (m && uio->uio_resid > 0 && error == 0) { |
if (m->m_type == MT_OOBDATA) { |
if (type != MT_OOBDATA) |
break; |
} else if (type == MT_OOBDATA) |
break; |
#ifdef DIAGNOSTIC |
else if (m->m_type != MT_DATA && m->m_type != MT_HEADER) |
panic("receive 3"); |
#endif |
so->so_state &= ~SS_RCVATMARK; |
len = uio->uio_resid; |
if (so->so_oobmark && len > so->so_oobmark - offset) |
len = so->so_oobmark - offset; |
if (len > m->m_len - moff) |
len = m->m_len - moff; |
/* |
* If mp is set, just pass back the mbufs. |
* Otherwise copy them out via the uio, then free. |
* Sockbuf must be consistent here (points to current mbuf, |
* it points to next record) when we drop priority; |
* we must note any additions to the sockbuf when we |
* block interrupts again. |
*/ |
if (mp == 0) { |
splx(s); |
error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio); |
s = splnet(); |
if (error) |
goto release; |
} else |
uio->uio_resid -= len; |
if (len == m->m_len - moff) { |
if (m->m_flags & M_EOR) |
flags |= MSG_EOR; |
if (flags & MSG_PEEK) { |
m = m->m_next; |
moff = 0; |
} else { |
nextrecord = m->m_nextpkt; |
sbfree(&so->so_rcv, m); |
if (mp) { |
*mp = m; |
mp = &m->m_next; |
so->so_rcv.sb_mb = m = m->m_next; |
*mp = (struct mbuf *)0; |
} else { |
MFREE(m, so->so_rcv.sb_mb); |
m = so->so_rcv.sb_mb; |
} |
if (m) |
m->m_nextpkt = nextrecord; |
} |
} else { |
if (flags & MSG_PEEK) |
moff += len; |
else { |
if (mp) |
*mp = m_copym(m, 0, len, M_WAIT); |
m->m_data += len; |
m->m_len -= len; |
so->so_rcv.sb_cc -= len; |
} |
} |
if (so->so_oobmark) { |
if ((flags & MSG_PEEK) == 0) { |
so->so_oobmark -= len; |
if (so->so_oobmark == 0) { |
so->so_state |= SS_RCVATMARK; |
break; |
} |
} else { |
offset += len; |
if (offset == so->so_oobmark) |
break; |
} |
} |
if (flags & MSG_EOR) |
break; |
/* |
* If the MSG_WAITALL flag is set (for non-atomic socket), |
* we must not quit until "uio->uio_resid == 0" or an error |
* termination. If a signal/timeout occurs, return |
* 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) |
break; |
error = sbwait(&so->so_rcv); |
if (error) { |
sbunlock(&so->so_rcv); |
splx(s); |
return (0); |
} |
m = so->so_rcv.sb_mb; |
if (m) |
nextrecord = m->m_nextpkt; |
} |
} |
|
if (m && pr->pr_flags & PR_ATOMIC) { |
flags |= MSG_TRUNC; |
if ((flags & MSG_PEEK) == 0) |
(void) sbdroprecord(&so->so_rcv); |
} |
if ((flags & MSG_PEEK) == 0) { |
if (m == 0) |
so->so_rcv.sb_mb = nextrecord; |
if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) |
(*pr->pr_usrreqs->pru_rcvd)(so, flags); |
} |
if (orig_resid == uio->uio_resid && orig_resid && |
(flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { |
sbunlock(&so->so_rcv); |
splx(s); |
goto restart; |
} |
|
if (flagsp) |
*flagsp |= flags; |
release: |
sbunlock(&so->so_rcv); |
splx(s); |
return (error); |
} |
|
int |
soshutdown(so, how) |
register struct socket *so; |
register int how; |
{ |
register struct protosw *pr = so->so_proto; |
|
how++; |
if (how & FREAD) |
sorflush(so); |
if (how & FWRITE) |
return ((*pr->pr_usrreqs->pru_shutdown)(so)); |
return (0); |
} |
|
void |
sorflush(so) |
register struct socket *so; |
{ |
register struct sockbuf *sb = &so->so_rcv; |
register struct protosw *pr = so->so_proto; |
register int s; |
struct sockbuf asb; |
|
sb->sb_flags |= SB_NOINTR; |
(void) sblock(sb, M_WAITOK); |
s = splimp(); |
socantrcvmore(so); |
sbunlock(sb); |
asb = *sb; |
bzero((caddr_t)sb, sizeof (*sb)); |
splx(s); |
if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose) |
(*pr->pr_domain->dom_dispose)(asb.sb_mb); |
sbrelease(&asb); |
} |
|
int |
sosetopt(so, level, optname, m0) |
register struct socket *so; |
int level, optname; |
struct mbuf *m0; |
{ |
int error = 0; |
register struct mbuf *m = m0; |
|
if (level != SOL_SOCKET) { |
if (so->so_proto && so->so_proto->pr_ctloutput) |
return ((*so->so_proto->pr_ctloutput) |
(PRCO_SETOPT, so, level, optname, &m0)); |
error = ENOPROTOOPT; |
} else { |
switch (optname) { |
|
case SO_LINGER: |
if (m == NULL || m->m_len != sizeof (struct linger)) { |
error = EINVAL; |
goto bad; |
} |
so->so_linger = mtod(m, struct linger *)->l_linger; |
/* fall thru... */ |
|
case SO_DEBUG: |
case SO_KEEPALIVE: |
case SO_DONTROUTE: |
case SO_USELOOPBACK: |
case SO_BROADCAST: |
case SO_REUSEADDR: |
case SO_REUSEPORT: |
case SO_OOBINLINE: |
case SO_TIMESTAMP: |
if (m == NULL || m->m_len < sizeof (int)) { |
error = EINVAL; |
goto bad; |
} |
if (*mtod(m, int *)) |
so->so_options |= optname; |
else |
so->so_options &= ~optname; |
break; |
|
case SO_SNDBUF: |
case SO_RCVBUF: |
case SO_SNDLOWAT: |
case SO_RCVLOWAT: |
{ |
int optval; |
|
if (m == NULL || m->m_len < sizeof (int)) { |
error = EINVAL; |
goto bad; |
} |
|
/* |
* Values < 1 make no sense for any of these |
* options, so disallow them. |
*/ |
optval = *mtod(m, int *); |
if (optval < 1) { |
error = EINVAL; |
goto bad; |
} |
|
switch (optname) { |
|
case SO_SNDBUF: |
case SO_RCVBUF: |
if (sbreserve(optname == SO_SNDBUF ? |
&so->so_snd : &so->so_rcv, |
(u_long) optval) == 0) { |
error = ENOBUFS; |
goto bad; |
} |
break; |
|
/* |
* Make sure the low-water is never greater than |
* the high-water. |
*/ |
case SO_SNDLOWAT: |
so->so_snd.sb_lowat = |
(optval > so->so_snd.sb_hiwat) ? |
so->so_snd.sb_hiwat : optval; |
break; |
case SO_RCVLOWAT: |
so->so_rcv.sb_lowat = |
(optval > so->so_rcv.sb_hiwat) ? |
so->so_rcv.sb_hiwat : optval; |
break; |
} |
break; |
} |
|
case SO_SNDTIMEO: |
case SO_RCVTIMEO: |
{ |
struct timeval *tv; |
unsigned long val; |
|
if (m == NULL || m->m_len < sizeof (*tv)) { |
error = EINVAL; |
goto bad; |
} |
tv = mtod(m, struct timeval *); |
if (tv->tv_sec >= (ULONG_MAX - hz) / hz) { |
error = EDOM; |
goto bad; |
} |
val = tv->tv_sec * hz + tv->tv_usec / tick; |
|
switch (optname) { |
|
case SO_SNDTIMEO: |
so->so_snd.sb_timeo = val; |
break; |
case SO_RCVTIMEO: |
so->so_rcv.sb_timeo = val; |
break; |
} |
break; |
} |
|
case SO_PRIVSTATE: |
/* we don't care what the parameter is... */ |
so->so_state &= ~SS_PRIV; |
break; |
|
case SO_SNDWAKEUP: |
case SO_RCVWAKEUP: |
{ |
/* RTEMS addition. */ |
struct sockwakeup *sw; |
struct sockbuf *sb; |
|
if (m == NULL |
|| m->m_len != sizeof (struct sockwakeup)) { |
error = EINVAL; |
goto bad; |
} |
sw = mtod(m, struct sockwakeup *); |
sb = (optname == SO_SNDWAKEUP |
? &so->so_snd |
: &so->so_rcv); |
sb->sb_wakeup = sw->sw_pfn; |
sb->sb_wakeuparg = sw->sw_arg; |
if (sw->sw_pfn) |
sb->sb_flags |= SB_ASYNC; |
else |
sb->sb_flags &=~ SB_ASYNC; |
break; |
} |
|
default: |
error = ENOPROTOOPT; |
break; |
} |
if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) { |
(void) ((*so->so_proto->pr_ctloutput) |
(PRCO_SETOPT, so, level, optname, &m0)); |
m = NULL; /* freed by protocol */ |
} |
} |
bad: |
if (m) |
(void) m_free(m); |
return (error); |
} |
|
int |
sogetopt(so, level, optname, mp) |
register struct socket *so; |
int level, optname; |
struct mbuf **mp; |
{ |
register struct mbuf *m; |
|
if (level != SOL_SOCKET) { |
if (so->so_proto && so->so_proto->pr_ctloutput) { |
return ((*so->so_proto->pr_ctloutput) |
(PRCO_GETOPT, so, level, optname, mp)); |
} else |
return (ENOPROTOOPT); |
} else { |
m = m_get(M_WAIT, MT_SOOPTS); |
m->m_len = sizeof (int); |
|
switch (optname) { |
|
case SO_LINGER: |
m->m_len = sizeof (struct linger); |
mtod(m, struct linger *)->l_onoff = |
so->so_options & SO_LINGER; |
mtod(m, struct linger *)->l_linger = so->so_linger; |
break; |
|
case SO_USELOOPBACK: |
case SO_DONTROUTE: |
case SO_DEBUG: |
case SO_KEEPALIVE: |
case SO_REUSEADDR: |
case SO_REUSEPORT: |
case SO_BROADCAST: |
case SO_OOBINLINE: |
case SO_TIMESTAMP: |
*mtod(m, int *) = so->so_options & optname; |
break; |
|
case SO_PRIVSTATE: |
*mtod(m, int *) = so->so_state & SS_PRIV; |
break; |
|
case SO_TYPE: |
*mtod(m, int *) = so->so_type; |
break; |
|
case SO_ERROR: |
*mtod(m, int *) = so->so_error; |
so->so_error = 0; |
break; |
|
case SO_SNDBUF: |
*mtod(m, int *) = so->so_snd.sb_hiwat; |
break; |
|
case SO_RCVBUF: |
*mtod(m, int *) = so->so_rcv.sb_hiwat; |
break; |
|
case SO_SNDLOWAT: |
*mtod(m, int *) = so->so_snd.sb_lowat; |
break; |
|
case SO_RCVLOWAT: |
*mtod(m, int *) = so->so_rcv.sb_lowat; |
break; |
|
case SO_SNDTIMEO: |
case SO_RCVTIMEO: |
{ |
unsigned long val = (optname == SO_SNDTIMEO ? |
so->so_snd.sb_timeo : so->so_rcv.sb_timeo); |
|
m->m_len = sizeof(struct timeval); |
mtod(m, struct timeval *)->tv_sec = val / hz; |
mtod(m, struct timeval *)->tv_usec = |
(val % hz) * tick; |
break; |
} |
|
case SO_SNDWAKEUP: |
case SO_RCVWAKEUP: |
{ |
struct sockbuf *sb; |
struct sockwakeup *sw; |
|
/* RTEMS additions. */ |
sb = (optname == SO_SNDWAKEUP |
? &so->so_snd |
: &so->so_rcv); |
m->m_len = sizeof (struct sockwakeup); |
sw = mtod(m, struct sockwakeup *); |
sw->sw_pfn = sb->sb_wakeup; |
sw->sw_arg = sb->sb_wakeuparg; |
break; |
} |
|
default: |
(void)m_free(m); |
return (ENOPROTOOPT); |
} |
*mp = m; |
return (0); |
} |
} |
|
void |
sohasoutofband(so) |
register struct socket *so; |
{ |
#if 0 /* FIXME: For now we just ignore out of band data */ |
struct proc *p; |
|
if (so->so_pgid < 0) |
gsignal(-so->so_pgid, SIGURG); |
else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) |
psignal(p, SIGURG); |
selwakeup(&so->so_rcv.sb_sel); |
#endif |
} |
/uipc_mbuf.c
0,0 → 1,748
/* |
* Copyright (c) 1982, 1986, 1988, 1991, 1993 |
* The Regents of the University of California. All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
* |
* @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 |
* $Id: uipc_mbuf.c,v 1.2 2001-09-27 12:01:51 chris Exp $ |
*/ |
|
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/proc.h> |
#include <sys/malloc.h> |
#define MBTYPES |
#include <sys/mbuf.h> |
#include <sys/kernel.h> |
#include <sys/syslog.h> |
#include <sys/domain.h> |
#include <sys/protosw.h> |
|
#include <vm/vm.h> |
#include <vm/vm_param.h> |
#include <vm/vm_kern.h> |
#include <vm/vm_extern.h> |
|
static void mbinit __P((void *)) __attribute__ ((unused)); |
SYSINIT(mbuf, SI_SUB_MBUF, SI_ORDER_FIRST, mbinit, NULL) |
|
struct mbuf *mbutl; |
char *mclrefcnt; |
struct mbstat mbstat; |
struct mbuf *mmbfree; |
union mcluster *mclfree; |
int max_linkhdr; |
int max_protohdr; |
int max_hdr; |
int max_datalen; |
|
/* "number of clusters of pages" */ |
#define NCL_INIT 1 |
|
#define NMB_INIT 16 |
|
/* |
* When MGET failes, ask protocols to free space when short of memory, |
* then re-attempt to allocate an mbuf. |
*/ |
struct mbuf * |
m_retry(i, t) |
int i, t; |
{ |
register struct mbuf *m; |
|
m_reclaim(); |
#define m_retry(i, t) (struct mbuf *)0 |
MGET(m, i, t); |
#undef m_retry |
if (m != NULL) |
mbstat.m_wait++; |
else |
mbstat.m_drops++; |
return (m); |
} |
|
/* |
* As above; retry an MGETHDR. |
*/ |
struct mbuf * |
m_retryhdr(i, t) |
int i, t; |
{ |
register struct mbuf *m; |
|
m_reclaim(); |
#define m_retryhdr(i, t) (struct mbuf *)0 |
MGETHDR(m, i, t); |
#undef m_retryhdr |
if (m != NULL) |
mbstat.m_wait++; |
else |
mbstat.m_drops++; |
return (m); |
} |
|
void |
m_reclaim(void) |
{ |
register struct domain *dp; |
register struct protosw *pr; |
int s = splimp(); |
|
for (dp = domains; dp; dp = dp->dom_next) |
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
if (pr->pr_drain) |
(*pr->pr_drain)(); |
splx(s); |
mbstat.m_drain++; |
} |
|
/* |
* Space allocation routines. |
* These are also available as macros |
* for critical paths. |
*/ |
struct mbuf * |
m_get(nowait, type) |
int nowait, type; |
{ |
register struct mbuf *m; |
|
MGET(m, nowait, type); |
return (m); |
} |
|
struct mbuf * |
m_gethdr(nowait, type) |
int nowait, type; |
{ |
register struct mbuf *m; |
|
MGETHDR(m, nowait, type); |
return (m); |
} |
|
struct mbuf * |
m_getclr(nowait, type) |
int nowait, type; |
{ |
register struct mbuf *m; |
|
MGET(m, nowait, type); |
if (m == 0) |
return (0); |
bzero(mtod(m, caddr_t), MLEN); |
return (m); |
} |
|
struct mbuf * |
m_free(m) |
struct mbuf *m; |
{ |
register struct mbuf *n; |
|
MFREE(m, n); |
return (n); |
} |
|
void |
m_freem(m) |
register struct mbuf *m; |
{ |
register struct mbuf *n; |
|
if (m == NULL) |
return; |
do { |
MFREE(m, n); |
m = n; |
} while (m); |
} |
|
/* |
* Mbuffer utility routines. |
*/ |
|
/* |
* Lesser-used path for M_PREPEND: |
* allocate new mbuf to prepend to chain, |
* copy junk along. |
*/ |
struct mbuf * |
m_prepend(m, len, how) |
register struct mbuf *m; |
int len, how; |
{ |
struct mbuf *mn; |
|
MGET(mn, how, m->m_type); |
if (mn == (struct mbuf *)NULL) { |
m_freem(m); |
return ((struct mbuf *)NULL); |
} |
if (m->m_flags & M_PKTHDR) { |
M_COPY_PKTHDR(mn, m); |
m->m_flags &= ~M_PKTHDR; |
} |
mn->m_next = m; |
m = mn; |
if (len < MHLEN) |
MH_ALIGN(m, len); |
m->m_len = len; |
return (m); |
} |
|
/* |
* Make a copy of an mbuf chain starting "off0" bytes from the beginning, |
* continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf. |
* The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller. |
*/ |
static int MCFail; |
|
struct mbuf * |
m_copym(m, off0, len, wait) |
register struct mbuf *m; |
int off0, wait; |
register int len; |
{ |
register struct mbuf *n, **np; |
register int off = off0; |
struct mbuf *top; |
int copyhdr = 0; |
|
if (off < 0 || len < 0) |
panic("m_copym"); |
if (off == 0 && m->m_flags & M_PKTHDR) |
copyhdr = 1; |
while (off > 0) { |
if (m == 0) |
panic("m_copym"); |
if (off < m->m_len) |
break; |
off -= m->m_len; |
m = m->m_next; |
} |
np = ⊤ |
top = 0; |
while (len > 0) { |
if (m == 0) { |
if (len != M_COPYALL) |
panic("m_copym"); |
break; |
} |
MGET(n, wait, m->m_type); |
*np = n; |
if (n == 0) |
goto nospace; |
if (copyhdr) { |
M_COPY_PKTHDR(n, m); |
if (len == M_COPYALL) |
n->m_pkthdr.len -= off0; |
else |
n->m_pkthdr.len = len; |
copyhdr = 0; |
} |
n->m_len = min(len, m->m_len - off); |
if (m->m_flags & M_EXT) { |
n->m_data = m->m_data + off; |
if(!m->m_ext.ext_ref) |
mclrefcnt[mtocl(m->m_ext.ext_buf)]++; |
else |
(*(m->m_ext.ext_ref))(m->m_ext.ext_buf, |
m->m_ext.ext_size); |
n->m_ext = m->m_ext; |
n->m_flags |= M_EXT; |
} else |
bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t), |
(unsigned)n->m_len); |
if (len != M_COPYALL) |
len -= n->m_len; |
off = 0; |
m = m->m_next; |
np = &n->m_next; |
} |
if (top == 0) |
MCFail++; |
return (top); |
nospace: |
m_freem(top); |
MCFail++; |
return (0); |
} |
|
/* |
* Copy an entire packet, including header (which must be present). |
* An optimization of the common case `m_copym(m, 0, M_COPYALL, how)'. |
*/ |
struct mbuf * |
m_copypacket(m, how) |
struct mbuf *m; |
int how; |
{ |
struct mbuf *top, *n, *o; |
|
MGET(n, how, m->m_type); |
top = n; |
if (!n) |
goto nospace; |
|
M_COPY_PKTHDR(n, m); |
n->m_len = m->m_len; |
if (m->m_flags & M_EXT) { |
n->m_data = m->m_data; |
mclrefcnt[mtocl(m->m_ext.ext_buf)]++; |
n->m_ext = m->m_ext; |
n->m_flags |= M_EXT; |
} else { |
bcopy(mtod(m, char *), mtod(n, char *), n->m_len); |
} |
|
m = m->m_next; |
while (m) { |
MGET(o, how, m->m_type); |
if (!o) |
goto nospace; |
|
n->m_next = o; |
n = n->m_next; |
|
n->m_len = m->m_len; |
if (m->m_flags & M_EXT) { |
n->m_data = m->m_data; |
mclrefcnt[mtocl(m->m_ext.ext_buf)]++; |
n->m_ext = m->m_ext; |
n->m_flags |= M_EXT; |
} else { |
bcopy(mtod(m, char *), mtod(n, char *), n->m_len); |
} |
|
m = m->m_next; |
} |
return top; |
nospace: |
m_freem(top); |
MCFail++; |
return 0; |
} |
|
/* |
* Copy data from an mbuf chain starting "off" bytes from the beginning, |
* continuing for "len" bytes, into the indicated buffer. |
*/ |
void |
m_copydata(m, off, len, cp) |
register struct mbuf *m; |
register int off; |
register int len; |
caddr_t cp; |
{ |
register unsigned count; |
|
if (off < 0 || len < 0) |
panic("m_copydata"); |
while (off > 0) { |
if (m == 0) |
panic("m_copydata"); |
if (off < m->m_len) |
break; |
off -= m->m_len; |
m = m->m_next; |
} |
while (len > 0) { |
if (m == 0) |
panic("m_copydata"); |
count = min(m->m_len - off, len); |
bcopy(mtod(m, caddr_t) + off, cp, count); |
len -= count; |
cp += count; |
off = 0; |
m = m->m_next; |
} |
} |
|
/* |
* Concatenate mbuf chain n to m. |
* Both chains must be of the same type (e.g. MT_DATA). |
* Any m_pkthdr is not updated. |
*/ |
void |
m_cat(m, n) |
register struct mbuf *m, *n; |
{ |
while (m->m_next) |
m = m->m_next; |
while (n) { |
if (m->m_flags & M_EXT || |
m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) { |
/* just join the two chains */ |
m->m_next = n; |
return; |
} |
/* splat the data from one into the other */ |
bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, |
(u_int)n->m_len); |
m->m_len += n->m_len; |
n = m_free(n); |
} |
} |
|
void |
m_adj(mp, req_len) |
struct mbuf *mp; |
int req_len; |
{ |
register int len = req_len; |
register struct mbuf *m; |
register int count; |
|
if ((m = mp) == NULL) |
return; |
if (len >= 0) { |
/* |
* Trim from head. |
*/ |
while (m != NULL && len > 0) { |
if (m->m_len <= len) { |
len -= m->m_len; |
m->m_len = 0; |
m = m->m_next; |
} else { |
m->m_len -= len; |
m->m_data += len; |
len = 0; |
} |
} |
m = mp; |
if (mp->m_flags & M_PKTHDR) |
m->m_pkthdr.len -= (req_len - len); |
} else { |
/* |
* Trim from tail. Scan the mbuf chain, |
* calculating its length and finding the last mbuf. |
* If the adjustment only affects this mbuf, then just |
* adjust and return. Otherwise, rescan and truncate |
* after the remaining size. |
*/ |
len = -len; |
count = 0; |
for (;;) { |
count += m->m_len; |
if (m->m_next == (struct mbuf *)0) |
break; |
m = m->m_next; |
} |
if (m->m_len >= len) { |
m->m_len -= len; |
if (mp->m_flags & M_PKTHDR) |
mp->m_pkthdr.len -= len; |
return; |
} |
count -= len; |
if (count < 0) |
count = 0; |
/* |
* Correct length for chain is "count". |
* Find the mbuf with last data, adjust its length, |
* and toss data from remaining mbufs on chain. |
*/ |
m = mp; |
if (m->m_flags & M_PKTHDR) |
m->m_pkthdr.len = count; |
for (; m; m = m->m_next) { |
if (m->m_len >= count) { |
m->m_len = count; |
break; |
} |
count -= m->m_len; |
} |
while (m->m_next) |
(m = m->m_next) ->m_len = 0; |
} |
} |
|
/* |
* Rearange an mbuf chain so that len bytes are contiguous |
* and in the data area of an mbuf (so that mtod and dtom |
* will work for a structure of size len). Returns the resulting |
* mbuf chain on success, frees it and returns null on failure. |
* If there is room, it will add up to max_protohdr-len extra bytes to the |
* contiguous region in an attempt to avoid being called next time. |
*/ |
static int MPFail; |
|
struct mbuf * |
m_pullup(n, len) |
register struct mbuf *n; |
int len; |
{ |
register struct mbuf *m; |
register int count; |
int space; |
|
/* |
* If first mbuf has no cluster, and has room for len bytes |
* without shifting current data, pullup into it, |
* otherwise allocate a new mbuf to prepend to the chain. |
*/ |
if ((n->m_flags & M_EXT) == 0 && |
n->m_data + len < &n->m_dat[MLEN] && n->m_next) { |
if (n->m_len >= len) |
return (n); |
m = n; |
n = n->m_next; |
len -= m->m_len; |
} else { |
if (len > MHLEN) |
goto bad; |
MGET(m, M_DONTWAIT, n->m_type); |
if (m == 0) |
goto bad; |
m->m_len = 0; |
if (n->m_flags & M_PKTHDR) { |
M_COPY_PKTHDR(m, n); |
n->m_flags &= ~M_PKTHDR; |
} |
} |
space = &m->m_dat[MLEN] - (m->m_data + m->m_len); |
do { |
count = min(min(max(len, max_protohdr), space), n->m_len); |
bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, |
(unsigned)count); |
len -= count; |
m->m_len += count; |
n->m_len -= count; |
space -= count; |
if (n->m_len) |
n->m_data += count; |
else |
n = m_free(n); |
} while (len > 0 && n); |
if (len > 0) { |
(void) m_free(m); |
goto bad; |
} |
m->m_next = n; |
return (m); |
bad: |
m_freem(n); |
MPFail++; |
return (0); |
} |
|
/* |
* Partition an mbuf chain in two pieces, returning the tail -- |
* all but the first len0 bytes. In case of failure, it returns NULL and |
* attempts to restore the chain to its original state. |
*/ |
struct mbuf * |
m_split(m0, len0, wait) |
register struct mbuf *m0; |
int len0, wait; |
{ |
register struct mbuf *m, *n; |
unsigned len = len0, remain; |
|
for (m = m0; m && len > m->m_len; m = m->m_next) |
len -= m->m_len; |
if (m == 0) |
return (0); |
remain = m->m_len - len; |
if (m0->m_flags & M_PKTHDR) { |
MGETHDR(n, wait, m0->m_type); |
if (n == 0) |
return (0); |
n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; |
n->m_pkthdr.len = m0->m_pkthdr.len - len0; |
m0->m_pkthdr.len = len0; |
if (m->m_flags & M_EXT) |
goto extpacket; |
if (remain > MHLEN) { |
/* m can't be the lead packet */ |
MH_ALIGN(n, 0); |
n->m_next = m_split(m, len, wait); |
if (n->m_next == 0) { |
(void) m_free(n); |
return (0); |
} else |
return (n); |
} else |
MH_ALIGN(n, remain); |
} else if (remain == 0) { |
n = m->m_next; |
m->m_next = 0; |
return (n); |
} else { |
MGET(n, wait, m->m_type); |
if (n == 0) |
return (0); |
M_ALIGN(n, remain); |
} |
extpacket: |
if (m->m_flags & M_EXT) { |
n->m_flags |= M_EXT; |
n->m_ext = m->m_ext; |
if(!m->m_ext.ext_ref) |
mclrefcnt[mtocl(m->m_ext.ext_buf)]++; |
else |
(*(m->m_ext.ext_ref))(m->m_ext.ext_buf, |
m->m_ext.ext_size); |
m->m_ext.ext_size = 0; /* For Accounting XXXXXX danger */ |
n->m_data = m->m_data + len; |
} else { |
bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain); |
} |
n->m_len = remain; |
m->m_len = len; |
n->m_next = m->m_next; |
m->m_next = 0; |
return (n); |
} |
/* |
* Routine to copy from device local memory into mbufs. |
*/ |
struct mbuf * |
m_devget(buf, totlen, off0, ifp, copy) |
char *buf; |
int totlen, off0; |
struct ifnet *ifp; |
void (*copy) __P((char *from, caddr_t to, u_int len)); |
{ |
register struct mbuf *m; |
struct mbuf *top = 0, **mp = ⊤ |
register int off = off0, len; |
register char *cp; |
char *epkt; |
|
cp = buf; |
epkt = cp + totlen; |
if (off) { |
cp += off + 2 * sizeof(u_short); |
totlen -= 2 * sizeof(u_short); |
} |
MGETHDR(m, M_DONTWAIT, MT_DATA); |
if (m == 0) |
return (0); |
m->m_pkthdr.rcvif = ifp; |
m->m_pkthdr.len = totlen; |
m->m_len = MHLEN; |
|
while (totlen > 0) { |
if (top) { |
MGET(m, M_DONTWAIT, MT_DATA); |
if (m == 0) { |
m_freem(top); |
return (0); |
} |
m->m_len = MLEN; |
} |
len = min(totlen, epkt - cp); |
if (len >= MINCLSIZE) { |
MCLGET(m, M_DONTWAIT); |
if (m->m_flags & M_EXT) |
m->m_len = len = min(len, MCLBYTES); |
else |
len = m->m_len; |
} else { |
/* |
* Place initial small packet/header at end of mbuf. |
*/ |
if (len < m->m_len) { |
if (top == 0 && len + max_linkhdr <= m->m_len) |
m->m_data += max_linkhdr; |
m->m_len = len; |
} else |
len = m->m_len; |
} |
if (copy) |
copy(cp, mtod(m, caddr_t), (unsigned)len); |
else |
bcopy(cp, mtod(m, caddr_t), (unsigned)len); |
cp += len; |
*mp = m; |
mp = &m->m_next; |
totlen -= len; |
if (cp == epkt) |
cp = buf; |
} |
return (top); |
} |
|
/* |
* Copy data from a buffer back into the indicated mbuf chain, |
* starting "off" bytes from the beginning, extending the mbuf |
* chain if necessary. |
*/ |
void |
m_copyback(m0, off, len, cp) |
struct mbuf *m0; |
register int off; |
register int len; |
caddr_t cp; |
{ |
register int mlen; |
register struct mbuf *m = m0, *n; |
int totlen = 0; |
|
if (m0 == 0) |
return; |
while (off > (mlen = m->m_len)) { |
off -= mlen; |
totlen += mlen; |
if (m->m_next == 0) { |
n = m_getclr(M_DONTWAIT, m->m_type); |
if (n == 0) |
goto out; |
n->m_len = min(MLEN, len + off); |
m->m_next = n; |
} |
m = m->m_next; |
} |
while (len > 0) { |
mlen = min (m->m_len - off, len); |
bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); |
cp += mlen; |
len -= mlen; |
mlen += off; |
off = 0; |
totlen += mlen; |
if (len == 0) |
break; |
if (m->m_next == 0) { |
n = m_get(M_DONTWAIT, m->m_type); |
if (n == 0) |
break; |
n->m_len = min(MLEN, len); |
m->m_next = n; |
} |
m = m->m_next; |
} |
out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) |
m->m_pkthdr.len = totlen; |
} |
/kern_subr.c
0,0 → 1,206
/* |
* Copyright (c) 1982, 1986, 1991, 1993 |
* The Regents of the University of California. All rights reserved. |
* (c) UNIX System Laboratories, Inc. |
* All or some portions of this file are derived from material licensed |
* to the University of California by American Telephone and Telegraph |
* Co. or Unix System Laboratories, Inc. and are reproduced herein with |
* the permission of UNIX System Laboratories, Inc. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
* |
* @(#)kern_subr.c 8.3 (Berkeley) 1/21/94 |
* $Id: kern_subr.c,v 1.2 2001-09-27 12:01:51 chris Exp $ |
*/ |
|
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/proc.h> |
#include <sys/malloc.h> |
#include <sys/queue.h> |
|
int |
uiomove(cp, n, uio) |
register caddr_t cp; |
register int n; |
register struct uio *uio; |
{ |
register struct iovec *iov; |
u_int cnt; |
int error; |
|
#ifdef DIAGNOSTIC |
if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE) |
panic("uiomove: mode"); |
#endif |
while (n > 0 && uio->uio_resid) { |
iov = uio->uio_iov; |
cnt = iov->iov_len; |
if (cnt == 0) { |
uio->uio_iov++; |
uio->uio_iovcnt--; |
continue; |
} |
if (cnt > n) |
cnt = n; |
|
switch (uio->uio_segflg) { |
|
case UIO_USERSPACE: |
case UIO_USERISPACE: |
if (uio->uio_rw == UIO_READ) |
error = copyout(cp, iov->iov_base, cnt); |
else |
error = copyin(iov->iov_base, cp, cnt); |
if (error) |
return (error); |
break; |
|
case UIO_SYSSPACE: |
if (uio->uio_rw == UIO_READ) |
bcopy((caddr_t)cp, iov->iov_base, cnt); |
else |
bcopy(iov->iov_base, (caddr_t)cp, cnt); |
break; |
case UIO_NOCOPY: |
break; |
} |
iov->iov_base += cnt; |
iov->iov_len -= cnt; |
uio->uio_resid -= cnt; |
uio->uio_offset += cnt; |
cp += cnt; |
n -= cnt; |
} |
return (0); |
} |
|
#ifdef vax /* unused except by ct.c, other oddities XXX */ |
/* |
* Get next character written in by user from uio. |
*/ |
int |
uwritec(uio) |
struct uio *uio; |
{ |
register struct iovec *iov; |
register int c; |
|
if (uio->uio_resid <= 0) |
return (-1); |
again: |
if (uio->uio_iovcnt <= 0) |
panic("uwritec"); |
iov = uio->uio_iov; |
if (iov->iov_len == 0) { |
uio->uio_iov++; |
if (--uio->uio_iovcnt == 0) |
return (-1); |
goto again; |
} |
switch (uio->uio_segflg) { |
|
case UIO_USERSPACE: |
c = fubyte(iov->iov_base); |
break; |
|
case UIO_SYSSPACE: |
c = *(u_char *) iov->iov_base; |
break; |
|
case UIO_USERISPACE: |
c = fuibyte(iov->iov_base); |
break; |
} |
if (c < 0) |
return (-1); |
iov->iov_base++; |
iov->iov_len--; |
uio->uio_resid--; |
uio->uio_offset++; |
return (c); |
} |
#endif /* vax */ |
|
/* |
* General routine to allocate a hash table. |
*/ |
void * |
hashinit(elements, type, hashmask) |
int elements, type; |
u_long *hashmask; |
{ |
long hashsize; |
LIST_HEAD(generic, generic) *hashtbl; |
int i; |
|
if (elements <= 0) |
panic("hashinit: bad elements"); |
for (hashsize = 1; hashsize <= elements; hashsize <<= 1) |
continue; |
hashsize >>= 1; |
hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_WAITOK); |
for (i = 0; i < hashsize; i++) |
LIST_INIT(&hashtbl[i]); |
*hashmask = hashsize - 1; |
return (hashtbl); |
} |
|
#define NPRIMES 27 |
static int primes[] = { 1, 13, 31, 61, 127, 251, 509, 761, 1021, 1531, 2039, |
2557, 3067, 3583, 4093, 4603, 5119, 5623, 6143, 6653, |
7159, 7673, 8191, 12281, 16381, 24571, 32749 }; |
|
/* |
* General routine to allocate a prime number sized hash table. |
*/ |
void * |
phashinit(elements, type, nentries) |
int elements, type; |
u_long *nentries; |
{ |
long hashsize; |
LIST_HEAD(generic, generic) *hashtbl; |
int i; |
|
if (elements <= 0) |
panic("phashinit: bad elements"); |
for (i = 1, hashsize = primes[1]; hashsize <= elements;) { |
i++; |
if (i == NPRIMES) |
break; |
hashsize = primes[i]; |
} |
hashsize = primes[i - 1]; |
hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_WAITOK); |
for (i = 0; i < hashsize; i++) |
LIST_INIT(&hashtbl[i]); |
*nentries = hashsize; |
return (hashtbl); |
} |
/uipc_socket2.c
0,0 → 1,965
/* |
* This file has undergone several changes to reflect the |
* differences between the RTEMS and FreeBSD kernels. |
*/ |
|
/* |
* Copyright (c) 1982, 1986, 1988, 1990, 1993 |
* The Regents of the University of California. All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
* |
* @(#)uipc_socket2.c 8.1 (Berkeley) 6/10/93 |
* $Id: uipc_socket2.c,v 1.2 2001-09-27 12:01:51 chris Exp $ |
*/ |
|
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/kernel.h> |
#include <sys/proc.h> |
#include <sys/file.h> |
#include <sys/buf.h> |
#include <sys/malloc.h> |
#include <sys/mbuf.h> |
#include <sys/protosw.h> |
#include <sys/stat.h> |
#include <sys/socket.h> |
#include <sys/socketvar.h> |
#include <sys/signalvar.h> |
#include <sys/sysctl.h> |
|
/* |
* Primitive routines for operating on sockets and socket buffers |
*/ |
|
u_long sb_max = SB_MAX; /* XXX should be static */ |
SYSCTL_INT(_kern, KERN_MAXSOCKBUF, maxsockbuf, CTLFLAG_RW, &sb_max, 0, "") |
|
static u_long sb_efficiency = 8; /* parameter for sbreserve() */ |
SYSCTL_INT(_kern, OID_AUTO, sockbuf_waste_factor, CTLFLAG_RW, &sb_efficiency, |
0, ""); |
|
/* |
* Procedures to manipulate state flags of socket |
* and do appropriate wakeups. Normal sequence from the |
* active (originating) side is that soisconnecting() is |
* called during processing of connect() call, |
* resulting in an eventual call to soisconnected() if/when the |
* connection is established. When the connection is torn down |
* soisdisconnecting() is called during processing of disconnect() call, |
* and soisdisconnected() is called when the connection to the peer |
* is totally severed. The semantics of these routines are such that |
* connectionless protocols can call soisconnected() and soisdisconnected() |
* only, bypassing the in-progress calls when setting up a ``connection'' |
* takes no time. |
* |
* From the passive side, a socket is created with |
* two queues of sockets: so_q0 for connections in progress |
* and so_q for connections already made and awaiting user acceptance. |
* As a protocol is preparing incoming connections, it creates a socket |
* structure queued on so_q0 by calling sonewconn(). When the connection |
* is established, soisconnected() is called, and transfers the |
* socket structure to so_q, making it available to accept(). |
* |
* If a socket is closed with sockets on either |
* so_q0 or so_q, these sockets are dropped. |
* |
* If higher level protocols are implemented in |
* the kernel, the wakeups done here will sometimes |
* cause software-interrupt process scheduling. |
*/ |
|
void |
soisconnecting(so) |
register struct socket *so; |
{ |
|
so->so_state &= ~(SS_ISCONNECTED|SS_ISDISCONNECTING); |
so->so_state |= SS_ISCONNECTING; |
} |
|
void |
soisconnected(so) |
register struct socket *so; |
{ |
register struct socket *head = so->so_head; |
|
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); |
so->so_state |= SS_ISCONNECTED; |
if (head && (so->so_state & SS_INCOMP)) { |
TAILQ_REMOVE(&head->so_incomp, so, so_list); |
head->so_incqlen--; |
so->so_state &= ~SS_INCOMP; |
TAILQ_INSERT_TAIL(&head->so_comp, so, so_list); |
so->so_state |= SS_COMP; |
sorwakeup(head); |
soconnwakeup(head); |
} else { |
soconnwakeup(so); |
sorwakeup(so); |
sowwakeup(so); |
} |
} |
|
void |
soisdisconnecting(so) |
register struct socket *so; |
{ |
|
so->so_state &= ~SS_ISCONNECTING; |
so->so_state |= (SS_ISDISCONNECTING|SS_CANTRCVMORE|SS_CANTSENDMORE); |
soconnwakeup(so); |
sowwakeup(so); |
sorwakeup(so); |
} |
|
void |
soisdisconnected(so) |
register struct socket *so; |
{ |
|
so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING); |
so->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE); |
soconnwakeup(so); |
sowwakeup(so); |
sorwakeup(so); |
} |
|
/* |
* Return a random connection that hasn't been serviced yet and |
* is eligible for discard. There is a one in qlen chance that |
* we will return a null, saying that there are no dropable |
* requests. In this case, the protocol specific code should drop |
* the new request. This insures fairness. |
* |
* This may be used in conjunction with protocol specific queue |
* congestion routines. |
*/ |
struct socket * |
sodropablereq(head) |
register struct socket *head; |
{ |
register struct socket *so; |
unsigned int i, j, qlen, m; |
|
static int rnd; |
static long old_mono_secs; |
static unsigned int cur_cnt, old_cnt; |
|
if ((i = (m = rtems_bsdnet_seconds_since_boot()) - old_mono_secs) != 0) { |
old_mono_secs = m; |
old_cnt = cur_cnt / i; |
cur_cnt = 0; |
} |
|
so = TAILQ_FIRST(&head->so_incomp); |
if (!so) |
return (so); |
|
qlen = head->so_incqlen; |
if (++cur_cnt > qlen || old_cnt > qlen) { |
rnd = (314159 * rnd + 66329) & 0xffff; |
j = ((qlen + 1) * rnd) >> 16; |
|
while (j-- && so) |
so = TAILQ_NEXT(so, so_list); |
} |
|
return (so); |
} |
|
/* |
* When an attempt at a new connection is noted on a socket |
* which accepts connections, sonewconn is called. If the |
* connection is possible (subject to space constraints, etc.) |
* then we allocate a new structure, propoerly linked into the |
* data structure of the original socket, and return this. |
* Connstatus may be 0, or SO_ISCONFIRMING, or SO_ISCONNECTED. |
* |
* Currently, sonewconn() is defined as sonewconn1() in socketvar.h |
* to catch calls that are missing the (new) second parameter. |
*/ |
struct socket * |
sonewconn1(head, connstatus) |
register struct socket *head; |
int connstatus; |
{ |
register struct socket *so; |
|
if (head->so_qlen > 3 * head->so_qlimit / 2) |
return ((struct socket *)0); |
MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_DONTWAIT); |
if (so == NULL) |
return ((struct socket *)0); |
bzero((caddr_t)so, sizeof(*so)); |
so->so_head = head; |
so->so_type = head->so_type; |
so->so_options = head->so_options &~ SO_ACCEPTCONN; |
so->so_linger = head->so_linger; |
so->so_state = head->so_state | SS_NOFDREF; |
so->so_proto = head->so_proto; |
so->so_timeo = head->so_timeo; |
so->so_pgid = head->so_pgid; |
so->so_uid = head->so_uid; |
(void) soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat); |
if (connstatus) { |
TAILQ_INSERT_TAIL(&head->so_comp, so, so_list); |
so->so_state |= SS_COMP; |
} else { |
TAILQ_INSERT_TAIL(&head->so_incomp, so, so_list); |
so->so_state |= SS_INCOMP; |
head->so_incqlen++; |
} |
head->so_qlen++; |
if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0)) { |
if (so->so_state & SS_COMP) { |
TAILQ_REMOVE(&head->so_comp, so, so_list); |
} else { |
TAILQ_REMOVE(&head->so_incomp, so, so_list); |
head->so_incqlen--; |
} |
head->so_qlen--; |
(void) free((caddr_t)so, M_SOCKET); |
return ((struct socket *)0); |
} |
if (connstatus) { |
sorwakeup(head); |
soconnwakeup(head); |
so->so_state |= connstatus; |
} |
return (so); |
} |
|
/* |
* Socantsendmore indicates that no more data will be sent on the |
* socket; it would normally be applied to a socket when the user |
* informs the system that no more data is to be sent, by the protocol |
* code (in case PRU_SHUTDOWN). Socantrcvmore indicates that no more data |
* will be received, and will normally be applied to the socket by a |
* protocol when it detects that the peer will send no more data. |
* Data queued for reading in the socket may yet be read. |
*/ |
|
void |
socantsendmore(so) |
struct socket *so; |
{ |
|
so->so_state |= SS_CANTSENDMORE; |
sowwakeup(so); |
} |
|
void |
socantrcvmore(so) |
struct socket *so; |
{ |
|
so->so_state |= SS_CANTRCVMORE; |
sorwakeup(so); |
} |
|
/* |
* Socket buffer (struct sockbuf) utility routines. |
* |
* Each socket contains two socket buffers: one for sending data and |
* one for receiving data. Each buffer contains a queue of mbufs, |
* information about the number of mbufs and amount of data in the |
* queue, and other fields allowing select() statements and notification |
* on data availability to be implemented. |
* |
* Data stored in a socket buffer is maintained as a list of records. |
* Each record is a list of mbufs chained together with the m_next |
* field. Records are chained together with the m_nextpkt field. The upper |
* level routine soreceive() expects the following conventions to be |
* observed when placing information in the receive buffer: |
* |
* 1. If the protocol requires each message be preceded by the sender's |
* name, then a record containing that name must be present before |
* any associated data (mbuf's must be of type MT_SONAME). |
* 2. If the protocol supports the exchange of ``access rights'' (really |
* just additional data associated with the message), and there are |
* ``rights'' to be received, then a record containing this data |
* should be present (mbuf's must be of type MT_RIGHTS). |
* 3. If a name or rights record exists, then it must be followed by |
* a data record, perhaps of zero length. |
* |
* Before using a new socket structure it is first necessary to reserve |
* buffer space to the socket, by calling sbreserve(). This should commit |
* some of the available buffer space in the system buffer pool for the |
* socket (currently, it does nothing but enforce limits). The space |
* should be released by calling sbrelease() when the socket is destroyed. |
*/ |
|
int |
soreserve(so, sndcc, rcvcc) |
register struct socket *so; |
u_long sndcc, rcvcc; |
{ |
|
if (sbreserve(&so->so_snd, sndcc) == 0) |
goto bad; |
if (sbreserve(&so->so_rcv, rcvcc) == 0) |
goto bad2; |
if (so->so_rcv.sb_lowat == 0) |
so->so_rcv.sb_lowat = 1; |
if (so->so_snd.sb_lowat == 0) |
so->so_snd.sb_lowat = MCLBYTES; |
if (so->so_snd.sb_lowat > so->so_snd.sb_hiwat) |
so->so_snd.sb_lowat = so->so_snd.sb_hiwat; |
return (0); |
bad2: |
sbrelease(&so->so_snd); |
bad: |
return (ENOBUFS); |
} |
|
/* |
* Allot mbufs to a sockbuf. |
* Attempt to scale mbmax so that mbcnt doesn't become limiting |
* if buffering efficiency is near the normal case. |
*/ |
int |
sbreserve(sb, cc) |
struct sockbuf *sb; |
u_long cc; |
{ |
|
if (cc > sb_max * MCLBYTES / (MSIZE + MCLBYTES)) |
return (0); |
sb->sb_hiwat = cc; |
sb->sb_mbmax = min(cc * sb_efficiency, sb_max); |
if (sb->sb_lowat > sb->sb_hiwat) |
sb->sb_lowat = sb->sb_hiwat; |
return (1); |
} |
|
/* |
* Free mbufs held by a socket, and reserved mbuf space. |
*/ |
void |
sbrelease(sb) |
struct sockbuf *sb; |
{ |
|
sbflush(sb); |
sb->sb_hiwat = sb->sb_mbmax = 0; |
} |
|
/* |
* Routines to add and remove |
* data from an mbuf queue. |
* |
* The routines sbappend() or sbappendrecord() are normally called to |
* append new mbufs to a socket buffer, after checking that adequate |
* space is available, comparing the function sbspace() with the amount |
* of data to be added. sbappendrecord() differs from sbappend() in |
* that data supplied is treated as the beginning of a new record. |
* To place a sender's address, optional access rights, and data in a |
* socket receive buffer, sbappendaddr() should be used. To place |
* access rights and data in a socket receive buffer, sbappendrights() |
* should be used. In either case, the new data begins a new record. |
* Note that unlike sbappend() and sbappendrecord(), these routines check |
* for the caller that there will be enough space to store the data. |
* Each fails if there is not enough space, or if it cannot find mbufs |
* to store additional information in. |
* |
* Reliable protocols may use the socket send buffer to hold data |
* awaiting acknowledgement. Data is normally copied from a socket |
* send buffer in a protocol with m_copy for output to a peer, |
* and then removing the data from the socket buffer with sbdrop() |
* or sbdroprecord() when the data is acknowledged by the peer. |
*/ |
|
/* |
* Append mbuf chain m to the last record in the |
* socket buffer sb. The additional space associated |
* the mbuf chain is recorded in sb. Empty mbufs are |
* discarded and mbufs are compacted where possible. |
*/ |
void |
sbappend(sb, m) |
struct sockbuf *sb; |
struct mbuf *m; |
{ |
register struct mbuf *n; |
|
if (m == 0) |
return; |
n = sb->sb_mb; |
if (n) { |
while (n->m_nextpkt) |
n = n->m_nextpkt; |
do { |
if (n->m_flags & M_EOR) { |
sbappendrecord(sb, m); /* XXXXXX!!!! */ |
return; |
} |
} while (n->m_next && (n = n->m_next)); |
} |
sbcompress(sb, m, n); |
} |
|
#ifdef SOCKBUF_DEBUG |
void |
sbcheck(sb) |
register struct sockbuf *sb; |
{ |
register struct mbuf *m; |
register int len = 0, mbcnt = 0; |
|
for (m = sb->sb_mb; m; m = m->m_next) { |
len += m->m_len; |
mbcnt += MSIZE; |
if (m->m_flags & M_EXT) /*XXX*/ /* pretty sure this is bogus */ |
mbcnt += m->m_ext.ext_size; |
if (m->m_nextpkt) |
panic("sbcheck nextpkt"); |
} |
if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) { |
printf("cc %d != %d || mbcnt %d != %d\n", len, sb->sb_cc, |
mbcnt, sb->sb_mbcnt); |
panic("sbcheck"); |
} |
} |
#endif |
|
/* |
* As above, except the mbuf chain |
* begins a new record. |
*/ |
void |
sbappendrecord(sb, m0) |
register struct sockbuf *sb; |
register struct mbuf *m0; |
{ |
register struct mbuf *m; |
|
if (m0 == 0) |
return; |
m = sb->sb_mb; |
if (m) |
while (m->m_nextpkt) |
m = m->m_nextpkt; |
/* |
* Put the first mbuf on the queue. |
* Note this permits zero length records. |
*/ |
sballoc(sb, m0); |
if (m) |
m->m_nextpkt = m0; |
else |
sb->sb_mb = m0; |
m = m0->m_next; |
m0->m_next = 0; |
if (m && (m0->m_flags & M_EOR)) { |
m0->m_flags &= ~M_EOR; |
m->m_flags |= M_EOR; |
} |
sbcompress(sb, m, m0); |
} |
|
/* |
* As above except that OOB data |
* is inserted at the beginning of the sockbuf, |
* but after any other OOB data. |
*/ |
void |
sbinsertoob(sb, m0) |
register struct sockbuf *sb; |
register struct mbuf *m0; |
{ |
register struct mbuf *m; |
register struct mbuf **mp; |
|
if (m0 == 0) |
return; |
for (mp = &sb->sb_mb; *mp ; mp = &((*mp)->m_nextpkt)) { |
m = *mp; |
again: |
switch (m->m_type) { |
|
case MT_OOBDATA: |
continue; /* WANT next train */ |
|
case MT_CONTROL: |
m = m->m_next; |
if (m) |
goto again; /* inspect THIS train further */ |
} |
break; |
} |
/* |
* Put the first mbuf on the queue. |
* Note this permits zero length records. |
*/ |
sballoc(sb, m0); |
m0->m_nextpkt = *mp; |
*mp = m0; |
m = m0->m_next; |
m0->m_next = 0; |
if (m && (m0->m_flags & M_EOR)) { |
m0->m_flags &= ~M_EOR; |
m->m_flags |= M_EOR; |
} |
sbcompress(sb, m, m0); |
} |
|
/* |
* Append address and data, and optionally, control (ancillary) data |
* to the receive queue of a socket. If present, |
* m0 must include a packet header with total length. |
* Returns 0 if no space in sockbuf or insufficient mbufs. |
*/ |
int |
sbappendaddr(sb, asa, m0, control) |
register struct sockbuf *sb; |
struct sockaddr *asa; |
struct mbuf *m0, *control; |
{ |
register struct mbuf *m, *n; |
int space = asa->sa_len; |
|
if (m0 && (m0->m_flags & M_PKTHDR) == 0) |
panic("sbappendaddr"); |
if (m0) |
space += m0->m_pkthdr.len; |
for (n = control; n; n = n->m_next) { |
space += n->m_len; |
if (n->m_next == 0) /* keep pointer to last control buf */ |
break; |
} |
if (space > sbspace(sb)) |
return (0); |
if (asa->sa_len > MLEN) |
return (0); |
MGET(m, M_DONTWAIT, MT_SONAME); |
if (m == 0) |
return (0); |
m->m_len = asa->sa_len; |
bcopy((caddr_t)asa, mtod(m, caddr_t), asa->sa_len); |
if (n) |
n->m_next = m0; /* concatenate data to control */ |
else |
control = m0; |
m->m_next = control; |
for (n = m; n; n = n->m_next) |
sballoc(sb, n); |
n = sb->sb_mb; |
if (n) { |
while (n->m_nextpkt) |
n = n->m_nextpkt; |
n->m_nextpkt = m; |
} else |
sb->sb_mb = m; |
return (1); |
} |
|
int |
sbappendcontrol(sb, m0, control) |
struct sockbuf *sb; |
struct mbuf *control, *m0; |
{ |
register struct mbuf *m, *n; |
int space = 0; |
|
if (control == 0) |
panic("sbappendcontrol"); |
for (m = control; ; m = m->m_next) { |
space += m->m_len; |
if (m->m_next == 0) |
break; |
} |
n = m; /* save pointer to last control buffer */ |
for (m = m0; m; m = m->m_next) |
space += m->m_len; |
if (space > sbspace(sb)) |
return (0); |
n->m_next = m0; /* concatenate data to control */ |
for (m = control; m; m = m->m_next) |
sballoc(sb, m); |
n = sb->sb_mb; |
if (n) { |
while (n->m_nextpkt) |
n = n->m_nextpkt; |
n->m_nextpkt = control; |
} else |
sb->sb_mb = control; |
return (1); |
} |
|
/* |
* Compress mbuf chain m into the socket |
* buffer sb following mbuf n. If n |
* is null, the buffer is presumed empty. |
*/ |
void |
sbcompress(sb, m, n) |
register struct sockbuf *sb; |
register struct mbuf *m, *n; |
{ |
register int eor = 0; |
register struct mbuf *o; |
|
while (m) { |
eor |= m->m_flags & M_EOR; |
if (m->m_len == 0 && |
(eor == 0 || |
(((o = m->m_next) || (o = n)) && |
o->m_type == m->m_type))) { |
m = m_free(m); |
continue; |
} |
if (n && (n->m_flags & (M_EXT | M_EOR)) == 0 && |
(n->m_data + n->m_len + m->m_len) < &n->m_dat[MLEN] && |
n->m_type == m->m_type) { |
bcopy(mtod(m, caddr_t), mtod(n, caddr_t) + n->m_len, |
(unsigned)m->m_len); |
n->m_len += m->m_len; |
sb->sb_cc += m->m_len; |
m = m_free(m); |
continue; |
} |
if (n) |
n->m_next = m; |
else |
sb->sb_mb = m; |
sballoc(sb, m); |
n = m; |
m->m_flags &= ~M_EOR; |
m = m->m_next; |
n->m_next = 0; |
} |
if (eor) { |
if (n) |
n->m_flags |= eor; |
else |
printf("semi-panic: sbcompress\n"); |
} |
} |
|
/* |
* Free all mbufs in a sockbuf. |
* Check that all resources are reclaimed. |
*/ |
void |
sbflush(sb) |
register struct sockbuf *sb; |
{ |
|
if (sb->sb_flags & SB_LOCK) |
panic("sbflush"); |
while (sb->sb_mbcnt) |
sbdrop(sb, (int)sb->sb_cc); |
if (sb->sb_cc || sb->sb_mb) |
panic("sbflush 2"); |
} |
|
/* |
* Drop data from (the front of) a sockbuf. |
*/ |
void |
sbdrop(sb, len) |
register struct sockbuf *sb; |
register int len; |
{ |
register struct mbuf *m, *mn; |
struct mbuf *next; |
|
next = (m = sb->sb_mb) ? m->m_nextpkt : 0; |
while (len > 0) { |
if (m == 0) { |
if (next == 0) |
panic("sbdrop"); |
m = next; |
next = m->m_nextpkt; |
continue; |
} |
if (m->m_len > len) { |
m->m_len -= len; |
m->m_data += len; |
sb->sb_cc -= len; |
break; |
} |
len -= m->m_len; |
sbfree(sb, m); |
MFREE(m, mn); |
m = mn; |
} |
while (m && m->m_len == 0) { |
sbfree(sb, m); |
MFREE(m, mn); |
m = mn; |
} |
if (m) { |
sb->sb_mb = m; |
m->m_nextpkt = next; |
} else |
sb->sb_mb = next; |
} |
|
/* |
* Drop a record off the front of a sockbuf |
* and move the next record to the front. |
*/ |
void |
sbdroprecord(sb) |
register struct sockbuf *sb; |
{ |
register struct mbuf *m, *mn; |
|
m = sb->sb_mb; |
if (m) { |
sb->sb_mb = m->m_nextpkt; |
do { |
sbfree(sb, m); |
MFREE(m, mn); |
m = mn; |
} while (m); |
} |
} |
|
/* |
* Create a "control" mbuf containing the specified data |
* with the specified type for presentation on a socket buffer. |
*/ |
struct mbuf * |
sbcreatecontrol(p, size, type, level) |
caddr_t p; |
register int size; |
int type, level; |
{ |
register struct cmsghdr *cp; |
struct mbuf *m; |
|
if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL) |
return ((struct mbuf *) NULL); |
cp = mtod(m, struct cmsghdr *); |
/* XXX check size? */ |
(void)memcpy(CMSG_DATA(cp), p, size); |
size += sizeof(*cp); |
m->m_len = size; |
cp->cmsg_len = size; |
cp->cmsg_level = level; |
cp->cmsg_type = type; |
return (m); |
} |
|
#ifdef PRU_OLDSTYLE |
/* |
* The following routines mediate between the old-style `pr_usrreq' |
* protocol implementations and the new-style `struct pr_usrreqs' |
* calling convention. |
*/ |
|
/* syntactic sugar */ |
#define nomb (struct mbuf *)0 |
|
static int |
old_abort(struct socket *so) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_ABORT, nomb, nomb, nomb); |
} |
|
static int |
old_accept(struct socket *so, struct mbuf *nam) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_ACCEPT, nomb, nam, nomb); |
} |
|
static int |
old_attach(struct socket *so, int proto) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_ATTACH, nomb, |
(struct mbuf *)proto, /* XXX */ |
nomb); |
} |
|
static int |
old_bind(struct socket *so, struct mbuf *nam) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_BIND, nomb, nam, nomb); |
} |
|
static int |
old_connect(struct socket *so, struct mbuf *nam) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_CONNECT, nomb, nam, nomb); |
} |
|
static int |
old_connect2(struct socket *so1, struct socket *so2) |
{ |
return so1->so_proto->pr_ousrreq(so1, PRU_CONNECT2, nomb, |
(struct mbuf *)so2, nomb); |
} |
|
static int |
old_control(struct socket *so, int cmd, caddr_t data, struct ifnet *ifp) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_CONTROL, (struct mbuf *)cmd, |
(struct mbuf *)data, |
(struct mbuf *)ifp); |
} |
|
static int |
old_detach(struct socket *so) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_DETACH, nomb, nomb, nomb); |
} |
|
static int |
old_disconnect(struct socket *so) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_DISCONNECT, nomb, nomb, nomb); |
} |
|
static int |
old_listen(struct socket *so) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_LISTEN, nomb, nomb, nomb); |
} |
|
static int |
old_peeraddr(struct socket *so, struct mbuf *nam) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_PEERADDR, nomb, nam, nomb); |
} |
|
static int |
old_rcvd(struct socket *so, int flags) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_RCVD, nomb, |
(struct mbuf *)flags, /* XXX */ |
nomb); |
} |
|
static int |
old_rcvoob(struct socket *so, struct mbuf *m, int flags) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_RCVOOB, m, |
(struct mbuf *)flags, /* XXX */ |
nomb); |
} |
|
static int |
old_send(struct socket *so, int flags, struct mbuf *m, struct mbuf *addr, |
struct mbuf *control) |
{ |
int req; |
|
if (flags & PRUS_OOB) { |
req = PRU_SENDOOB; |
} else if(flags & PRUS_EOF) { |
req = PRU_SEND_EOF; |
} else { |
req = PRU_SEND; |
} |
return so->so_proto->pr_ousrreq(so, req, m, addr, control); |
} |
|
static int |
old_sense(struct socket *so, struct stat *sb) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_SENSE, (struct mbuf *)sb, |
nomb, nomb); |
} |
|
static int |
old_shutdown(struct socket *so) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_SHUTDOWN, nomb, nomb, nomb); |
} |
|
static int |
old_sockaddr(struct socket *so, struct mbuf *nam) |
{ |
return so->so_proto->pr_ousrreq(so, PRU_SOCKADDR, nomb, nam, nomb); |
} |
|
struct pr_usrreqs pru_oldstyle = { |
old_abort, old_accept, old_attach, old_bind, old_connect, |
old_connect2, old_control, old_detach, old_disconnect, |
old_listen, old_peeraddr, old_rcvd, old_rcvoob, old_send, |
old_sense, old_shutdown, old_sockaddr |
}; |
|
#endif /* PRU_OLDSTYLE */ |
|
/* |
* Some routines that return EOPNOTSUPP for entry points that are not |
* supported by a protocol. Fill in as needed. |
*/ |
int |
pru_accept_notsupp(struct socket *so, struct mbuf *nam) |
{ |
return EOPNOTSUPP; |
} |
|
int |
pru_connect2_notsupp(struct socket *so1, struct socket *so2) |
{ |
return EOPNOTSUPP; |
} |
|
int |
pru_control_notsupp(struct socket *so, int cmd, caddr_t data, |
struct ifnet *ifp) |
{ |
return EOPNOTSUPP; |
} |
|
int |
pru_listen_notsupp(struct socket *so) |
{ |
return EOPNOTSUPP; |
} |
|
int |
pru_rcvd_notsupp(struct socket *so, int flags) |
{ |
return EOPNOTSUPP; |
} |
|
int |
pru_rcvoob_notsupp(struct socket *so, struct mbuf *m, int flags) |
{ |
return EOPNOTSUPP; |
} |
|
/* |
* This isn't really a ``null'' operation, but it's the default one |
* and doesn't do anything destructive. |
*/ |
int |
pru_sense_null(struct socket *so, struct stat *sb) |
{ |
sb->st_blksize = so->so_snd.sb_hiwat; |
return 0; |
} |
|