📄 clnt_kudp.c
字号:
#ifdef KERNEL/* * Copyright (c) 1987 by Sun Microsystems, Inc. * * @(#)clnt_kudp.c 1.1 92/07/30 * * Implements a kernel UPD/IP based, client side RPC. * * Copyright (C) 1988, Sun Microsystems, Inc. */#include <sys/param.h>#include <sys/systm.h>#include <sys/user.h>#include <sys/kernel.h>#include <sys/proc.h>#include <sys/socket.h>#include <sys/socketvar.h>#include <sys/mbuf.h>#include <net/if.h>#include <net/route.h>#include <netinet/in.h>#include <netinet/in_pcb.h>#include <rpc/types.h>#include <rpc/xdr.h>#include <rpc/auth.h>#include <rpc/clnt.h>#include <rpc/rpc_msg.h>struct mbuf *ku_recvfrom();int ckuwakeup();enum clnt_stat clntkudp_callit();void clntkudp_abort();void clntkudp_error();bool_t clntkudp_freeres();bool_t clntkudp_control();void clntkudp_destroy();void xdrmbuf_init();/* * Operations vector for UDP/IP based RPC */static struct clnt_ops udp_ops = { clntkudp_callit, /* do rpc call */ clntkudp_abort, /* abort call */ clntkudp_error, /* return error status */ clntkudp_freeres, /* free results */ clntkudp_destroy, /* destroy rpc handle */ clntkudp_control /* the ioctl() of rpc */};/* * Private data per rpc handle. This structure is allocated by * clntkudp_create, and freed by cku_destroy. */struct cku_private { u_int cku_flags; /* see below */ CLIENT cku_client; /* client handle */ int cku_retrys; /* request retrys */ struct socket *cku_sock; /* open udp socket */ struct sockaddr_in cku_addr; /* remote address */ struct rpc_err cku_err; /* error status */ XDR cku_outxdr; /* xdr routine for output */ XDR cku_inxdr; /* xdr routine for input */ u_int cku_outpos; /* position of in output mbuf */ char *cku_outbuf; /* output buffer */ char *cku_inbuf; /* input buffer */ struct mbuf *cku_inmbuf; /* input mbuf */ struct ucred *cku_cred; /* credentials */ struct rpc_timers *cku_timers; /* for estimating RTT */ struct rpc_timers *cku_timeall; /* for estimating RTT */ void (*cku_feedback)(); caddr_t cku_feedarg; /* argument for feedback func */ u_long cku_xid; /* current XID */};struct { int rccalls; int rcbadcalls; int rcretrans; int rcbadxids; int rctimeouts; int rcwaits; int rcnewcreds; int rcbadverfs; int rctimers;} rcstat;#define ptoh(p) (&((p)->cku_client))#define htop(h) ((struct cku_private *)((h)->cl_private))/* cku_flags */#define CKU_TIMEDOUT 0x001#define CKU_BUSY 0x002#define CKU_WANTED 0x004#define CKU_BUFBUSY 0x008#define CKU_BUFWANTED 0x010/* Times to retry */#define RECVTRIES 2#define SNDTRIES 4u_long clntxid; /* transaction id used by all clients */staticnoop(){}staticbuffree(p) struct cku_private *p;{ p->cku_flags &= ~CKU_BUFBUSY; if (p->cku_flags & CKU_BUFWANTED) { p->cku_flags &= ~CKU_BUFWANTED; wakeup((caddr_t)&p->cku_outbuf); }}/* * Create an rpc handle for a udp rpc connection. * Allocates space for the handle structure and the private data, and * opens a socket. Note sockets and handles are one to one. */CLIENT *clntkudp_create(addr, pgm, vers, retrys, cred) struct sockaddr_in *addr; u_long pgm; u_long vers; int retrys; struct ucred *cred;{ register CLIENT *h; register struct cku_private *p; int error = 0; struct rpc_msg call_msg; struct mbuf *m, *mclgetx(); extern int nfs_portmon;#ifdef RPCDEBUG rpc_debug(4, "clntkudp_create(%X, %d, %d, %d\n", addr->sin_addr.s_addr, pgm, vers, retrys);#endif p = (struct cku_private *)kmem_zalloc(sizeof *p); h = ptoh(p); if (!clntxid) { clntxid = (time.tv_usec ^ time.tv_sec); } /* handle */ h->cl_ops = &udp_ops; h->cl_private = (caddr_t) p; h->cl_auth = authkern_create(); /* call message, just used to pre-serialize below */ call_msg.rm_xid = 0; call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = pgm; call_msg.rm_call.cb_vers = vers; /* private */ clntkudp_init(h, addr, retrys, cred); p->cku_outbuf = (char *)kmem_alloc((u_int)UDPMSGSIZE); m = mclgetx(noop, 0, p->cku_outbuf, UDPMSGSIZE, M_WAIT); if (m == NULL) goto bad; xdrmbuf_init(&p->cku_outxdr, m, XDR_ENCODE); /* pre-serialize call message header */ if (! xdr_callhdr(&(p->cku_outxdr), &call_msg)) { printf("clntkudp_create - Fatal header serialization error."); (void) m_freem(m); goto bad; } p->cku_outpos = XDR_GETPOS(&(p->cku_outxdr)); (void) m_free(m); /* open udp socket */ error = socreate(AF_INET, &p->cku_sock, SOCK_DGRAM, IPPROTO_UDP); if (error) { printf("clntkudp_create: socket creation problem, %d", error); goto bad; } if (error = bindresvport(p->cku_sock)) { printf("clntkudp_create: socket bind problem, %d", error); goto bad; } return (h);bad: kmem_free((caddr_t)p->cku_outbuf, (u_int)UDPMSGSIZE); kmem_free((caddr_t)p, (u_int)sizeof (struct cku_private));#ifdef RPCDEBUG rpc_debug(4, "create failed\n");#endif return ((CLIENT *)NULL);}clntkudp_init(h, addr, retrys, cred) CLIENT *h; struct sockaddr_in *addr; int retrys; struct ucred *cred;{ struct cku_private *p = htop(h); p->cku_retrys = retrys; p->cku_addr = *addr; p->cku_cred = cred; p->cku_xid = 0; p->cku_flags &= (CKU_BUFBUSY | CKU_BUFWANTED);}/* * set the timers. Return current retransmission timeout. */clntkudp_settimers(h, t, all, minimum, feedback, arg, xid) CLIENT *h; struct rpc_timers *t, *all; unsigned int minimum; void (*feedback)(); caddr_t arg; u_long xid;{ struct cku_private *p = htop(h); int value; p->cku_feedback = feedback; p->cku_feedarg = arg; p->cku_timers = t; p->cku_timeall = all; if (xid) p->cku_xid = xid; else p->cku_xid = alloc_xid(); value = all->rt_rtxcur; value += t->rt_rtxcur; if (value < minimum) return (minimum); rcstat.rctimers++; return (value);}/* * Time out back off function. tim is in hz */#define MAXTIMO (20 * hz)#define backoff(tim) ((((tim) << 1) > MAXTIMO) ? MAXTIMO : ((tim) << 1))#if defined(ASYNCHIO) && defined(LWP)extern int runthreads;#endif /* ASYNCHIO && LWP *//* * Call remote procedure. * Most of the work of rpc is done here. We serialize what is left * of the header (some was pre-serialized in the handle), serialize * the arguments, and send it off. We wait for a reply or a time out. * Timeout causes an immediate return, other packet problems may cause * a retry on the receive. When a good packet is received we deserialize * it, and check verification. A bad reply code will cause one retry * with full (longhand) credentials. * If "ignorebad" is true, rpc replies with remote errors are ignored. */enum clnt_statclntkudp_callit_addr(h, procnum, xdr_args, argsp, xdr_results, resultsp, wait, sin, ignorebad) register CLIENT *h; u_long procnum; xdrproc_t xdr_args; caddr_t argsp; xdrproc_t xdr_results; caddr_t resultsp; struct timeval wait; struct sockaddr_in *sin; int ignorebad;{ register struct cku_private *p = htop(h); register XDR *xdrs; register struct socket *so = p->cku_sock; int rtries; int stries = p->cku_retrys; int s; struct ucred *tmpcred; struct mbuf *m; int timohz; u_long xid; u_int rempos = 0; int refreshes = 2; /* number of times to refresh credential */ int round_trip; /* time the RPC */ int interrupted; /* return from sleep() */ int smask; /* saved signal mask*/ struct proc *pp = u.u_procp;# define time_in_hz (time.tv_sec*hz + time.tv_usec/(1000000/hz))#ifdef RPCDEBUG rpc_debug(4, "cku_callit\n");#endif rcstat.rccalls++; while (p->cku_flags & CKU_BUSY) { rcstat.rcwaits++; p->cku_flags |= CKU_WANTED; (void) sleep((caddr_t)h, PZERO-2); } p->cku_flags |= CKU_BUSY; /* * Set credentials into the u structure */ tmpcred = u.u_cred; u.u_cred = p->cku_cred; if (p->cku_xid == 0) xid = alloc_xid(); else xid = p->cku_xid; /* * This is dumb but easy: keep the time out in units of hz * so it is easy to call timeout and modify the value. */ timohz = wait.tv_sec * hz + (wait.tv_usec * hz) / 1000000;call_again: /* * Wait til buffer gets freed then make a type 2 mbuf point at it * The buffree routine clears CKU_BUFBUSY and does a wakeup when * the mbuf gets freed. */ s = splimp(); while (p->cku_flags & CKU_BUFBUSY) { p->cku_flags |= CKU_BUFWANTED; /* * This is a kludge to avoid deadlock in the case of a * loop-back call. The client can block wainting for * the server to free the mbuf while the server is blocked * waiting for the client to free the reply mbuf. Avoid this * by flushing the input queue every once in a while while * we are waiting. */ timeout(wakeup, (caddr_t)&p->cku_outbuf, hz); (void) sleep((caddr_t)&p->cku_outbuf, PZERO-3); if (!(p->cku_flags & CKU_BUFBUSY)) { /* probably woke up from buffree */ untimeout(wakeup, (caddr_t)&p->cku_outbuf); } sbflush(&so->so_rcv); } p->cku_flags |= CKU_BUFBUSY; (void) splx(s); m = mclgetx(buffree, (int)p, p->cku_outbuf, UDPMSGSIZE, M_WAIT); if (m == NULL) { p->cku_err.re_status = RPC_SYSTEMERROR; p->cku_err.re_errno = ENOBUFS; buffree(p); goto done; } xdrs = &p->cku_outxdr; /* * The transaction id is the first thing in the * preserialized output buffer. */ (*(u_long *)(p->cku_outbuf)) = xid; xdrmbuf_init(xdrs, m, XDR_ENCODE); if (rempos != 0) { XDR_SETPOS(xdrs, rempos); } else { /* * Serialize dynamic stuff into the output buffer. */ XDR_SETPOS(xdrs, p->cku_outpos); if ((! XDR_PUTLONG(xdrs, (long *)&procnum)) || (! AUTH_MARSHALL(h->cl_auth, xdrs)) || (! (*xdr_args)(xdrs, argsp))) { p->cku_err.re_status = RPC_CANTENCODEARGS; p->cku_err.re_errno = EIO; (void) m_freem(m); goto done; } rempos = XDR_GETPOS(xdrs); } m->m_len = rempos; round_trip = time_in_hz; if ((p->cku_err.re_errno = ku_sendto_mbuf(so, m, &p->cku_addr)) != 0) { p->cku_err.re_status = RPC_CANTSEND; p->cku_err.re_errno = EIO; goto done; }recv_again: for (rtries = RECVTRIES; rtries; rtries--) { s = splnet(); while (so->so_rcv.sb_cc == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -