📄 uipc_sock.c
字号:
/* uipc_sock.c - uipc socket routines *//* Copyright 1984-1996 Wind River Systems, Inc. */#include "copyright_wrs.h"/* * 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.6 (Berkeley) 5/2/95 *//*modification history--------------------01i,24sep98,ham considered if(m->m_extSize<max_hdr) in sosend() SPR#22209.01h,26aug98,n_s optimized processing of uio buffer in sosend. spr #22246.01g,26aug98,n_s added return val check for MALLOC in socreate and for mBufClGet in soreceive. spr #22238.01f,11aug97,vin fixed problems in sosend, adjusted space in sosend01e,23jan97,vin added fix for SPR7502.01d,03dec96,vin replaced calloc(..) with MALLOC(..), free(..) with FREE(..)01c,22nov96,vin added cluster support, replaced m_get with mBufClGet and m_gethdr with mHdrClGet.01b,29aug96,vin added zerocopy interface. 01a,03mar96,vin created from BSD4.4lite2. Integrated with 02u of uipc_sock.c*/#include "vxWorks.h"#include "semLib.h"#include "memLib.h"#include "errno.h"#include "net/mbuf.h"#include "net/domain.h"#include "net/protosw.h"#include "sys/socket.h"#include "sys/times.h"#include "net/socketvar.h"#include "sys/ioctl.h"#include "net/uio.h"#include "net/route.h"#include "netinet/in.h"#include "net/if.h"#include "net/systm.h"#include "selectLib.h"#include "sockLib.h"/* * 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*/intsocreate(dom, aso, type, proto) int dom; struct socket **aso; register int type; int proto;{ 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_usrreq == 0) return (EPROTONOSUPPORT); if (prp->pr_type != type) return (EPROTOTYPE); MALLOC(so, struct socket *, sizeof(*so), MT_SOCKET, M_WAIT); if (so == (struct socket *) NULL) { return (ENOBUFS); } bzero((caddr_t)so, sizeof(*so)); so->so_options = 0; so->so_type = type; /* all sockets will be priveledged */ so->so_state = SS_PRIV; so->so_proto = prp; /* initialize socket semaphores */ semBInit (&so->so_timeoSem, SEM_Q_PRIORITY, SEM_EMPTY); semBInit (&so->so_rcv.sb_Sem, SEM_Q_PRIORITY, SEM_EMPTY); semBInit (&so->so_snd.sb_Sem, SEM_Q_PRIORITY, SEM_EMPTY); so->so_rcv.sb_want = 0; so->so_snd.sb_want = 0; /* initialize the select stuff */ selWakeupListInit (&so->so_selWakeupList); error = (*prp->pr_usrreq)(so, PRU_ATTACH, (struct mbuf *)0, (struct mbuf *)(long)proto, (struct mbuf *)0); if (error) { so->so_state |= SS_NOFDREF; sofree(so); return (error); } *aso = so; return (0);}intsobind(so, nam) struct socket *so; struct mbuf *nam;{ int s = splnet(); int error; error = (*so->so_proto->pr_usrreq)(so, PRU_BIND, (struct mbuf *)0, nam, (struct mbuf *)0); splx(s); return (error);}intsolisten(so, backlog) register struct socket *so; int backlog;{ int s = splnet(), error; error = (*so->so_proto->pr_usrreq)(so, PRU_LISTEN, (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); if (error) { splx(s); return (error); } if (so->so_q == 0) so->so_options |= SO_ACCEPTCONN; if (backlog < 0) backlog = 0; so->so_qlimit = min(backlog, SOMAXCONN); splx(s); return (0);}intsofree(so) register struct socket *so;{ if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) return; if (so->so_head) { if (!soqremque(so, 0) && !soqremque(so, 1)) panic("sofree dq"); so->so_head = 0; } sbrelease(&so->so_snd); sorflush(so); FREE(so, MT_SOCKET); }/* * Close a socket on last file table reference removal. * Initiate disconnect if connected. * Free socket when disconnect complete. */intsoclose(so) register struct socket *so;{ int s = splnet(); /* conservative */ int error = 0; if (so->so_options & SO_ACCEPTCONN) { while (so->so_q0) (void) soabort(so->so_q0); while (so->so_q) (void) soabort(so->so_q); } 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) ksleep(&so->so_timeoSem); } }drop: if (so->so_pcb) { int error2 = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); 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... */intsoabort(so) struct socket *so;{ return ( (*so->so_proto->pr_usrreq)(so, PRU_ABORT, (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0));}intsoaccept(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_usrreq)(so, PRU_ACCEPT, (struct mbuf *)0, nam, (struct mbuf *)0); splx(s); return (error);}intsoconnect(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_usrreq)(so, PRU_CONNECT, (struct mbuf *)0, nam, (struct mbuf *)0); splx(s); return (error);}intsoconnect2(so1, so2) register struct socket *so1; struct socket *so2;{ int s = splnet(); int error; error = (*so1->so_proto->pr_usrreq)(so1, PRU_CONNECT2, (struct mbuf *)0, (struct mbuf *)so2, (struct mbuf *)0); splx(s); return (error);}intsodisconnect(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_usrreq)(so, PRU_DISCONNECT, (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0);bad: splx(s); return (error);}#define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_DONTWAIT : M_WAIT)/* * 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. * * WRS mods removed sblock and sbunlock and replaced with splnet and splx * which should serve the purpose. * Removed null check of uio so that blocking can be implemented for zbufs * also. * CAVEAT: the zbuf length cannot be more than the socket high water mark. * The user should implement his flow control. * So this call would block only if zbuf length is bigger the space available * in the socket buffer and less than the socket higt water mark. * Added a flag canWait which is set to M_DONTWAIT if the socket option SS_NBIO * is set. This prevents blocking if the user chose SS_NBIO and for some reason * if the system runs out of mbufs. canWait defaults to M_WAIT -(vinai). */intsosend(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; register int canWait; 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. */ if (resid < 0) return (EINVAL); dontroute = (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && (so->so_proto->pr_flags & PR_ATOMIC); if (control) clen = control->m_len; canWait = (so->so_state & SS_NBIO) ? M_DONTWAIT : M_WAIT;#define snderr(errno) { error = errno; splx(s); goto out; } s = splnet();restart: do { if (so->so_state & SS_CANTSENDMORE) snderr(EPIPE); if (so->so_error) snderr(so->so_error); if ((so->so_state & SS_ISCONNECTED) == 0) { if (so->so_proto->pr_flags & PR_CONNREQUIRED) { if ((so->so_state & SS_ISCONFIRMING) == 0 && !(resid == 0 && clen != 0)) snderr(ENOTCONN); } else if (addr == 0) snderr(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 && (atomic || space < so->so_snd.sb_lowat || space < clen)) { if (so->so_state & SS_NBIO) { if (flags & MSG_MBUF) top = NULL; /* don't free the zero copy mbuf */ snderr(EWOULDBLOCK); } sbwait(&so->so_snd); goto restart; } 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 { len = min(resid, space); if (top == 0) { m = mBufClGet(canWait, MT_DATA, len + max_hdr, FALSE); if (m == NULL) snderr(ENOBUFS); len = min(len, m->m_extSize); m->m_flags |= M_PKTHDR; m->m_pkthdr.len = 0; m->m_pkthdr.rcvif = (struct ifnet *)0; /* * the assumption here is that max_hdr is * always less than the minimum cluster size * available. Or don't set len, namely use * len which set by min() above. */ if (atomic && m->m_extSize > max_hdr) { len = min((m->m_extSize - max_hdr), len); m->m_data += max_hdr; } } else { m = mBufClGet(canWait, MT_DATA, len, FALSE); if (m == NULL) snderr(ENOBUFS); len = min(len, m->m_extSize); } space -= (m->m_extSize + MSIZE); 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; error = (*so->so_proto->pr_usrreq)(so, (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, top, addr, control); 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: splx(s); out: if (top) m_freem(top); if (control) m_freem(control); if (error != 0) netErrnoSet (error); 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. * * WRS mods: implement zero copy if out of band data is requested. */intsoreceive(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 = 0, s, offset; struct protosw *pr = so->so_proto; struct mbuf *nextrecord; int moff, type; int orig_resid = uio->uio_resid; mp = mp0; if (paddr) *paddr = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -