clnt_vc.c revision 243882
1327952Sdim/* $NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 fvdl Exp $ */ 2286425Sdim 3286425Sdim/* 4286425Sdim * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 5286425Sdim * unrestricted use provided that this legend is included on all tape 6286425Sdim * media and as a part of the software program in whole or part. Users 7286425Sdim * may copy or modify Sun RPC without charge, but are not authorized 8286425Sdim * to license or distribute it to anyone else except as part of a product or 9286425Sdim * program developed by the user. 10314564Sdim * 11314564Sdim * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 12314564Sdim * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 13314564Sdim * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 14314564Sdim * 15286425Sdim * Sun RPC is provided with no support and without any obligation on the 16286425Sdim * part of Sun Microsystems, Inc. to assist in its use, correction, 17327952Sdim * modification or enhancement. 18286425Sdim * 19321369Sdim * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 20314564Sdim * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 21314564Sdim * OR ANY PART THEREOF. 22314564Sdim * 23314564Sdim * In no event will Sun Microsystems, Inc. be liable for any lost revenue 24286425Sdim * or profits or other special, indirect and consequential damages, even if 25286425Sdim * Sun has been advised of the possibility of such damages. 26286425Sdim * 27314564Sdim * Sun Microsystems, Inc. 28286425Sdim * 2550 Garcia Avenue 29314564Sdim * Mountain View, California 94043 30286425Sdim */ 31327952Sdim 32314564Sdim#if defined(LIBC_SCCS) && !defined(lint) 33309124Sdimstatic char *sccsid2 = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; 34286425Sdimstatic char *sccsid = "@(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC"; 35286425Sdimstatic char sccsid3[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro"; 36314564Sdim#endif 37321369Sdim#include <sys/cdefs.h> 38314564Sdim__FBSDID("$FreeBSD: head/sys/rpc/clnt_vc.c 243882 2012-12-05 08:04:20Z glebius $"); 39314564Sdim 40314564Sdim/* 41314564Sdim * clnt_tcp.c, Implements a TCP/IP based, client side RPC. 42314564Sdim * 43314564Sdim * Copyright (C) 1984, Sun Microsystems, Inc. 44286425Sdim * 45286425Sdim * TCP based RPC supports 'batched calls'. 46321369Sdim * A sequence of calls may be batched-up in a send buffer. The rpc call 47321369Sdim * return immediately to the client even though the call was not necessarily 48286425Sdim * sent. The batching occurs if the results' xdr routine is NULL (0) AND 49286425Sdim * the rpc timeout value is zero (see clnt.h, rpc). 50286425Sdim * 51286425Sdim * Clients should NOT casually batch calls that in fact return results; that is, 52286425Sdim * the server side should be aware that a call is batched and not produce any 53286425Sdim * return message. Batched calls that produce many result messages can 54286425Sdim * deadlock (netlock) the client and the server.... 55286425Sdim * 56286425Sdim * Now go hang yourself. 57286425Sdim */ 58341825Sdim 59341825Sdim#include <sys/param.h> 60341825Sdim#include <sys/systm.h> 61341825Sdim#include <sys/lock.h> 62341825Sdim#include <sys/malloc.h> 63341825Sdim#include <sys/mbuf.h> 64286425Sdim#include <sys/mutex.h> 65286425Sdim#include <sys/pcpu.h> 66286425Sdim#include <sys/proc.h> 67286425Sdim#include <sys/protosw.h> 68286425Sdim#include <sys/socket.h> 69286425Sdim#include <sys/socketvar.h> 70286425Sdim#include <sys/syslog.h> 71286425Sdim#include <sys/time.h> 72286425Sdim#include <sys/uio.h> 73286425Sdim 74286425Sdim#include <net/vnet.h> 75286425Sdim 76286425Sdim#include <netinet/tcp.h> 77286425Sdim 78286425Sdim#include <rpc/rpc.h> 79314564Sdim#include <rpc/rpc_com.h> 80314564Sdim 81314564Sdim#define MCALL_MSG_SIZE 24 82286425Sdim 83314564Sdimstruct cmessage { 84286425Sdim struct cmsghdr cmsg; 85314564Sdim struct cmsgcred cmcred; 86286425Sdim}; 87286425Sdim 88286425Sdimstatic enum clnt_stat clnt_vc_call(CLIENT *, struct rpc_callextra *, 89314564Sdim rpcproc_t, struct mbuf *, struct mbuf **, struct timeval); 90286425Sdimstatic void clnt_vc_geterr(CLIENT *, struct rpc_err *); 91286425Sdimstatic bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *); 92286425Sdimstatic void clnt_vc_abort(CLIENT *); 93296417Sdimstatic bool_t clnt_vc_control(CLIENT *, u_int, void *); 94286425Sdimstatic void clnt_vc_close(CLIENT *); 95341825Sdimstatic void clnt_vc_destroy(CLIENT *); 96286425Sdimstatic bool_t time_not_ok(struct timeval *); 97286425Sdimstatic int clnt_vc_soupcall(struct socket *so, void *arg, int waitflag); 98286425Sdim 99286425Sdimstatic struct clnt_ops clnt_vc_ops = { 100286425Sdim .cl_call = clnt_vc_call, 101286425Sdim .cl_abort = clnt_vc_abort, 102286425Sdim .cl_geterr = clnt_vc_geterr, 103286425Sdim .cl_freeres = clnt_vc_freeres, 104286425Sdim .cl_close = clnt_vc_close, 105286425Sdim .cl_destroy = clnt_vc_destroy, 106286425Sdim .cl_control = clnt_vc_control 107286425Sdim}; 108286425Sdim 109286425Sdim/* 110286425Sdim * A pending RPC request which awaits a reply. Requests which have 111286425Sdim * received their reply will have cr_xid set to zero and cr_mrep to 112286425Sdim * the mbuf chain of the reply. 113286425Sdim */ 114286425Sdimstruct ct_request { 115286425Sdim TAILQ_ENTRY(ct_request) cr_link; 116286425Sdim uint32_t cr_xid; /* XID of request */ 117286425Sdim struct mbuf *cr_mrep; /* reply received by upcall */ 118286425Sdim int cr_error; /* any error from upcall */ 119286425Sdim char cr_verf[MAX_AUTH_BYTES]; /* reply verf */ 120286425Sdim}; 121286425Sdim 122286425SdimTAILQ_HEAD(ct_request_list, ct_request); 123286425Sdim 124286425Sdimstruct ct_data { 125286425Sdim struct mtx ct_lock; 126286425Sdim int ct_threads; /* number of threads in clnt_vc_call */ 127286425Sdim bool_t ct_closing; /* TRUE if we are closing */ 128286425Sdim bool_t ct_closed; /* TRUE if we are closed */ 129286425Sdim struct socket *ct_socket; /* connection socket */ 130286425Sdim bool_t ct_closeit; /* close it on destroy */ 131286425Sdim struct timeval ct_wait; /* wait interval in milliseconds */ 132286425Sdim struct sockaddr_storage ct_addr; /* remote addr */ 133286425Sdim struct rpc_err ct_error; 134286425Sdim uint32_t ct_xid; 135286425Sdim char ct_mcallc[MCALL_MSG_SIZE]; /* marshalled callmsg */ 136286425Sdim size_t ct_mpos; /* pos after marshal */ 137286425Sdim const char *ct_waitchan; 138286425Sdim int ct_waitflag; 139286425Sdim struct mbuf *ct_record; /* current reply record */ 140286425Sdim size_t ct_record_resid; /* how much left of reply to read */ 141286425Sdim bool_t ct_record_eor; /* true if reading last fragment */ 142286425Sdim struct ct_request_list ct_pending; 143286425Sdim int ct_upcallrefs; /* Ref cnt of upcalls in prog. */ 144286425Sdim}; 145286425Sdim 146286425Sdimstatic void clnt_vc_upcallsdone(struct ct_data *); 147286425Sdim 148286425Sdimstatic const char clnt_vc_errstr[] = "%s : %s"; 149286425Sdimstatic const char clnt_vc_str[] = "clnt_vc_create"; 150286425Sdimstatic const char clnt_read_vc_str[] = "read_vc"; 151286425Sdimstatic const char __no_mem_str[] = "out of memory"; 152286425Sdim 153286425Sdim/* 154286425Sdim * Create a client handle for a connection. 155286425Sdim * Default options are set, which the user can change using clnt_control()'s. 156286425Sdim * The rpc/vc package does buffering similar to stdio, so the client 157286425Sdim * must pick send and receive buffer sizes, 0 => use the default. 158286425Sdim * NB: fd is copied into a private area. 159286425Sdim * NB: The rpch->cl_auth is set null authentication. Caller may wish to 160286425Sdim * set this something more useful. 161286425Sdim * 162286425Sdim * fd should be an open socket 163286425Sdim */ 164286425SdimCLIENT * 165314564Sdimclnt_vc_create( 166286425Sdim struct socket *so, /* open file descriptor */ 167286425Sdim struct sockaddr *raddr, /* servers address */ 168286425Sdim const rpcprog_t prog, /* program number */ 169314564Sdim const rpcvers_t vers, /* version number */ 170286425Sdim size_t sendsz, /* buffer recv size */ 171286425Sdim size_t recvsz, /* buffer send size */ 172286425Sdim int intrflag) /* interruptible */ 173286425Sdim{ 174286425Sdim CLIENT *cl; /* client handle */ 175286425Sdim struct ct_data *ct = NULL; /* client handle */ 176286425Sdim struct timeval now; 177286425Sdim struct rpc_msg call_msg; 178314564Sdim static uint32_t disrupt; 179286425Sdim struct __rpc_sockinfo si; 180286425Sdim XDR xdrs; 181314564Sdim int error, interrupted, one = 1, sleep_flag; 182286425Sdim struct sockopt sopt; 183286425Sdim 184286425Sdim if (disrupt == 0) 185286425Sdim disrupt = (uint32_t)(long)raddr; 186286425Sdim 187286425Sdim cl = (CLIENT *)mem_alloc(sizeof (*cl)); 188286425Sdim ct = (struct ct_data *)mem_alloc(sizeof (*ct)); 189286425Sdim 190327952Sdim mtx_init(&ct->ct_lock, "ct->ct_lock", NULL, MTX_DEF); 191286425Sdim ct->ct_threads = 0; 192286425Sdim ct->ct_closing = FALSE; 193286425Sdim ct->ct_closed = FALSE; 194286425Sdim ct->ct_upcallrefs = 0; 195286425Sdim 196286425Sdim if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) { 197286425Sdim error = soconnect(so, raddr, curthread); 198314564Sdim SOCK_LOCK(so); 199314564Sdim interrupted = 0; 200286425Sdim sleep_flag = PSOCK; 201327952Sdim if (intrflag != 0) 202286425Sdim sleep_flag |= (PCATCH | PBDRY); 203286425Sdim while ((so->so_state & SS_ISCONNECTING) 204286425Sdim && so->so_error == 0) { 205286425Sdim error = msleep(&so->so_timeo, SOCK_MTX(so), 206286425Sdim sleep_flag, "connec", 0); 207286425Sdim if (error) { 208286425Sdim if (error == EINTR || error == ERESTART) 209286425Sdim interrupted = 1; 210314564Sdim break; 211314564Sdim } 212286425Sdim } 213286425Sdim if (error == 0) { 214286425Sdim error = so->so_error; 215286425Sdim so->so_error = 0; 216286425Sdim } 217314564Sdim SOCK_UNLOCK(so); 218286425Sdim if (error) { 219286425Sdim if (!interrupted) 220286425Sdim so->so_state &= ~SS_ISCONNECTING; 221286425Sdim rpc_createerr.cf_stat = RPC_SYSTEMERROR; 222286425Sdim rpc_createerr.cf_error.re_errno = error; 223286425Sdim goto err; 224286425Sdim } 225286425Sdim } 226286425Sdim 227286425Sdim if (!__rpc_socket2sockinfo(so, &si)) { 228286425Sdim goto err; 229286425Sdim } 230286425Sdim 231286425Sdim if (so->so_proto->pr_flags & PR_CONNREQUIRED) { 232286425Sdim bzero(&sopt, sizeof(sopt)); 233314564Sdim sopt.sopt_dir = SOPT_SET; 234286425Sdim sopt.sopt_level = SOL_SOCKET; 235286425Sdim sopt.sopt_name = SO_KEEPALIVE; 236314564Sdim sopt.sopt_val = &one; 237286425Sdim sopt.sopt_valsize = sizeof(one); 238286425Sdim sosetopt(so, &sopt); 239286425Sdim } 240314564Sdim 241286425Sdim if (so->so_proto->pr_protocol == IPPROTO_TCP) { 242286425Sdim bzero(&sopt, sizeof(sopt)); 243286425Sdim sopt.sopt_dir = SOPT_SET; 244286425Sdim sopt.sopt_level = IPPROTO_TCP; 245286425Sdim sopt.sopt_name = TCP_NODELAY; 246286425Sdim sopt.sopt_val = &one; 247286425Sdim sopt.sopt_valsize = sizeof(one); 248286425Sdim sosetopt(so, &sopt); 249286425Sdim } 250286425Sdim 251286425Sdim ct->ct_closeit = FALSE; 252286425Sdim 253286425Sdim /* 254286425Sdim * Set up private data struct 255286425Sdim */ 256286425Sdim ct->ct_socket = so; 257286425Sdim ct->ct_wait.tv_sec = -1; 258286425Sdim ct->ct_wait.tv_usec = -1; 259286425Sdim memcpy(&ct->ct_addr, raddr, raddr->sa_len); 260286425Sdim 261286425Sdim /* 262314564Sdim * Initialize call message 263286425Sdim */ 264286425Sdim getmicrotime(&now); 265286425Sdim ct->ct_xid = ((uint32_t)++disrupt) ^ __RPC_GETXID(&now); 266286425Sdim call_msg.rm_xid = ct->ct_xid; 267286425Sdim call_msg.rm_direction = CALL; 268286425Sdim call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 269314564Sdim call_msg.rm_call.cb_prog = (uint32_t)prog; 270286425Sdim call_msg.rm_call.cb_vers = (uint32_t)vers; 271286425Sdim 272286425Sdim /* 273286425Sdim * pre-serialize the static part of the call msg and stash it away 274314564Sdim */ 275286425Sdim xdrmem_create(&xdrs, ct->ct_mcallc, MCALL_MSG_SIZE, 276314564Sdim XDR_ENCODE); 277286425Sdim if (! xdr_callhdr(&xdrs, &call_msg)) { 278286425Sdim if (ct->ct_closeit) { 279286425Sdim soclose(ct->ct_socket); 280286425Sdim } 281286425Sdim goto err; 282286425Sdim } 283286425Sdim ct->ct_mpos = XDR_GETPOS(&xdrs); 284327952Sdim XDR_DESTROY(&xdrs); 285327952Sdim ct->ct_waitchan = "rpcrecv"; 286286425Sdim ct->ct_waitflag = 0; 287286425Sdim 288286425Sdim /* 289286425Sdim * Create a client handle which uses xdrrec for serialization 290286425Sdim * and authnone for authentication. 291286425Sdim */ 292286425Sdim sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz); 293286425Sdim recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz); 294314564Sdim error = soreserve(ct->ct_socket, sendsz, recvsz); 295286425Sdim if (error != 0) { 296314564Sdim if (ct->ct_closeit) { 297286425Sdim soclose(ct->ct_socket); 298286425Sdim } 299286425Sdim goto err; 300286425Sdim } 301286425Sdim cl->cl_refs = 1; 302286425Sdim cl->cl_ops = &clnt_vc_ops; 303286425Sdim cl->cl_private = ct; 304286425Sdim cl->cl_auth = authnone_create(); 305286425Sdim 306286425Sdim SOCKBUF_LOCK(&ct->ct_socket->so_rcv); 307286425Sdim soupcall_set(ct->ct_socket, SO_RCV, clnt_vc_soupcall, ct); 308286425Sdim SOCKBUF_UNLOCK(&ct->ct_socket->so_rcv); 309286425Sdim 310286425Sdim ct->ct_record = NULL; 311286425Sdim ct->ct_record_resid = 0; 312314564Sdim TAILQ_INIT(&ct->ct_pending); 313286425Sdim return (cl); 314314564Sdim 315286425Sdimerr: 316286425Sdim if (cl) { 317286425Sdim if (ct) { 318286425Sdim mtx_destroy(&ct->ct_lock); 319286425Sdim mem_free(ct, sizeof (struct ct_data)); 320286425Sdim } 321286425Sdim if (cl) 322314564Sdim mem_free(cl, sizeof (CLIENT)); 323286425Sdim } 324286425Sdim return ((CLIENT *)NULL); 325286425Sdim} 326286425Sdim 327286425Sdimstatic enum clnt_stat 328286425Sdimclnt_vc_call( 329286425Sdim CLIENT *cl, /* client handle */ 330286425Sdim struct rpc_callextra *ext, /* call metadata */ 331286425Sdim rpcproc_t proc, /* procedure number */ 332286425Sdim struct mbuf *args, /* pointer to args */ 333286425Sdim struct mbuf **resultsp, /* pointer to results */ 334286425Sdim struct timeval utimeout) 335286425Sdim{ 336286425Sdim struct ct_data *ct = (struct ct_data *) cl->cl_private; 337286425Sdim AUTH *auth; 338286425Sdim struct rpc_err *errp; 339286425Sdim enum clnt_stat stat; 340286425Sdim XDR xdrs; 341286425Sdim struct rpc_msg reply_msg; 342286425Sdim bool_t ok; 343286425Sdim int nrefreshes = 2; /* number of times to refresh cred */ 344286425Sdim struct timeval timeout; 345286425Sdim uint32_t xid; 346286425Sdim struct mbuf *mreq = NULL, *results; 347286425Sdim struct ct_request *cr; 348286425Sdim int error; 349286425Sdim 350286425Sdim cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK); 351286425Sdim 352286425Sdim mtx_lock(&ct->ct_lock); 353286425Sdim 354286425Sdim if (ct->ct_closing || ct->ct_closed) { 355286425Sdim mtx_unlock(&ct->ct_lock); 356286425Sdim free(cr, M_RPC); 357286425Sdim return (RPC_CANTSEND); 358286425Sdim } 359286425Sdim ct->ct_threads++; 360286425Sdim 361286425Sdim if (ext) { 362286425Sdim auth = ext->rc_auth; 363286425Sdim errp = &ext->rc_err; 364286425Sdim } else { 365286425Sdim auth = cl->cl_auth; 366286425Sdim errp = &ct->ct_error; 367286425Sdim } 368286425Sdim 369286425Sdim cr->cr_mrep = NULL; 370286425Sdim cr->cr_error = 0; 371286425Sdim 372286425Sdim if (ct->ct_wait.tv_usec == -1) { 373286425Sdim timeout = utimeout; /* use supplied timeout */ 374286425Sdim } else { 375286425Sdim timeout = ct->ct_wait; /* use default timeout */ 376314564Sdim } 377286425Sdim 378286425Sdimcall_again: 379327952Sdim mtx_assert(&ct->ct_lock, MA_OWNED); 380341825Sdim 381314564Sdim ct->ct_xid++; 382286425Sdim xid = ct->ct_xid; 383341825Sdim 384341825Sdim mtx_unlock(&ct->ct_lock); 385314564Sdim 386286425Sdim /* 387286425Sdim * Leave space to pre-pend the record mark. 388314564Sdim */ 389286425Sdim MGETHDR(mreq, M_WAITOK, MT_DATA); 390286425Sdim mreq->m_data += sizeof(uint32_t); 391286425Sdim KASSERT(ct->ct_mpos + sizeof(uint32_t) <= MHLEN, 392286425Sdim ("RPC header too big")); 393314564Sdim bcopy(ct->ct_mcallc, mreq->m_data, ct->ct_mpos); 394286425Sdim mreq->m_len = ct->ct_mpos; 395286425Sdim 396286425Sdim /* 397286425Sdim * The XID is the first thing in the request. 398327952Sdim */ 399327952Sdim *mtod(mreq, uint32_t *) = htonl(xid); 400327952Sdim 401286425Sdim xdrmbuf_create(&xdrs, mreq, XDR_ENCODE); 402286425Sdim 403286425Sdim errp->re_status = stat = RPC_SUCCESS; 404286425Sdim 405286425Sdim if ((! XDR_PUTINT32(&xdrs, &proc)) || 406286425Sdim (! AUTH_MARSHALL(auth, xid, &xdrs, 407286425Sdim m_copym(args, 0, M_COPYALL, M_WAITOK)))) { 408314564Sdim errp->re_status = stat = RPC_CANTENCODEARGS; 409286425Sdim mtx_lock(&ct->ct_lock); 410286425Sdim goto out; 411286425Sdim } 412286425Sdim mreq->m_pkthdr.len = m_length(mreq, NULL); 413286425Sdim 414286425Sdim /* 415286425Sdim * Prepend a record marker containing the packet length. 416286425Sdim */ 417314564Sdim M_PREPEND(mreq, sizeof(uint32_t), M_WAITOK); 418286425Sdim *mtod(mreq, uint32_t *) = 419314564Sdim htonl(0x80000000 | (mreq->m_pkthdr.len - sizeof(uint32_t))); 420286425Sdim 421286425Sdim cr->cr_xid = xid; 422286425Sdim mtx_lock(&ct->ct_lock); 423286425Sdim /* 424286425Sdim * Check to see if the other end has already started to close down 425286425Sdim * the connection. The upcall will have set ct_error.re_status 426286425Sdim * to RPC_CANTRECV if this is the case. 427286425Sdim * If the other end starts to close down the connection after this 428286425Sdim * point, it will be detected later when cr_error is checked, 429286425Sdim * since the request is in the ct_pending queue. 430286425Sdim */ 431327952Sdim if (ct->ct_error.re_status == RPC_CANTRECV) { 432286425Sdim if (errp != &ct->ct_error) { 433286425Sdim errp->re_errno = ct->ct_error.re_errno; 434286425Sdim errp->re_status = RPC_CANTRECV; 435286425Sdim } 436286425Sdim stat = RPC_CANTRECV; 437314564Sdim goto out; 438286425Sdim } 439286425Sdim TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link); 440286425Sdim mtx_unlock(&ct->ct_lock); 441286425Sdim 442286425Sdim /* 443286425Sdim * sosend consumes mreq. 444286425Sdim */ 445341825Sdim error = sosend(ct->ct_socket, NULL, NULL, mreq, NULL, 0, curthread); 446341825Sdim mreq = NULL; 447341825Sdim if (error == EMSGSIZE) { 448341825Sdim SOCKBUF_LOCK(&ct->ct_socket->so_snd); 449341825Sdim sbwait(&ct->ct_socket->so_snd); 450286425Sdim SOCKBUF_UNLOCK(&ct->ct_socket->so_snd); 451286425Sdim AUTH_VALIDATE(auth, xid, NULL, NULL); 452286425Sdim mtx_lock(&ct->ct_lock); 453286425Sdim TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 454341825Sdim goto call_again; 455341825Sdim } 456286425Sdim 457286425Sdim reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL; 458314564Sdim reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf; 459286425Sdim reply_msg.acpted_rply.ar_verf.oa_length = 0; 460286425Sdim reply_msg.acpted_rply.ar_results.where = NULL; 461286425Sdim reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; 462286425Sdim 463286425Sdim mtx_lock(&ct->ct_lock); 464286425Sdim if (error) { 465286425Sdim TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 466314564Sdim errp->re_errno = error; 467286425Sdim errp->re_status = stat = RPC_CANTSEND; 468286425Sdim goto out; 469286425Sdim } 470286425Sdim 471286425Sdim /* 472286425Sdim * Check to see if we got an upcall while waiting for the 473286425Sdim * lock. In both these cases, the request has been removed 474314564Sdim * from ct->ct_pending. 475286425Sdim */ 476314564Sdim if (cr->cr_error) { 477314564Sdim TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 478286425Sdim errp->re_errno = cr->cr_error; 479286425Sdim errp->re_status = stat = RPC_CANTRECV; 480286425Sdim goto out; 481286425Sdim } 482286425Sdim if (cr->cr_mrep) { 483286425Sdim TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 484327952Sdim goto got_reply; 485286425Sdim } 486286425Sdim 487286425Sdim /* 488286425Sdim * Hack to provide rpc-based message passing 489327952Sdim */ 490286425Sdim if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { 491314564Sdim TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 492286425Sdim errp->re_status = stat = RPC_TIMEDOUT; 493286425Sdim goto out; 494314564Sdim } 495286425Sdim 496286425Sdim error = msleep(cr, &ct->ct_lock, ct->ct_waitflag, ct->ct_waitchan, 497286425Sdim tvtohz(&timeout)); 498314564Sdim 499286425Sdim TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); 500286425Sdim 501314564Sdim if (error) { 502286425Sdim /* 503286425Sdim * The sleep returned an error so our request is still 504286425Sdim * on the list. Turn the error code into an 505314564Sdim * appropriate client status. 506327952Sdim */ 507286425Sdim errp->re_errno = error; 508286425Sdim switch (error) { 509314564Sdim case EINTR: 510314564Sdim case ERESTART: 511286425Sdim stat = RPC_INTR; 512286425Sdim break; 513314564Sdim case EWOULDBLOCK: 514314564Sdim stat = RPC_TIMEDOUT; 515286425Sdim break; 516286425Sdim default: 517286425Sdim stat = RPC_CANTRECV; 518286425Sdim } 519286425Sdim errp->re_status = stat; 520314564Sdim goto out; 521314564Sdim } else { 522286425Sdim /* 523327952Sdim * We were woken up by the upcall. If the 524286425Sdim * upcall had a receive error, report that, 525286425Sdim * otherwise we have a reply. 526286425Sdim */ 527286425Sdim if (cr->cr_error) { 528286425Sdim errp->re_errno = cr->cr_error; 529286425Sdim errp->re_status = stat = RPC_CANTRECV; 530286425Sdim goto out; 531286425Sdim } 532286425Sdim } 533286425Sdim 534286425Sdimgot_reply: 535286425Sdim /* 536286425Sdim * Now decode and validate the response. We need to drop the 537286425Sdim * lock since xdr_replymsg may end up sleeping in malloc. 538286425Sdim */ 539286425Sdim mtx_unlock(&ct->ct_lock); 540286425Sdim 541286425Sdim if (ext && ext->rc_feedback) 542286425Sdim ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg); 543286425Sdim 544286425Sdim xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE); 545286425Sdim ok = xdr_replymsg(&xdrs, &reply_msg); 546286425Sdim cr->cr_mrep = NULL; 547286425Sdim 548286425Sdim if (ok) { 549286425Sdim if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && 550286425Sdim (reply_msg.acpted_rply.ar_stat == SUCCESS)) 551286425Sdim errp->re_status = stat = RPC_SUCCESS; 552286425Sdim else 553286425Sdim stat = _seterr_reply(&reply_msg, errp); 554286425Sdim 555286425Sdim if (stat == RPC_SUCCESS) { 556286425Sdim results = xdrmbuf_getall(&xdrs); 557286425Sdim if (!AUTH_VALIDATE(auth, xid, 558286425Sdim &reply_msg.acpted_rply.ar_verf, 559327952Sdim &results)) { 560327952Sdim errp->re_status = stat = RPC_AUTHERROR; 561286425Sdim errp->re_why = AUTH_INVALIDRESP; 562286425Sdim } else { 563286425Sdim KASSERT(results, 564327952Sdim ("auth validated but no result")); 565327952Sdim *resultsp = results; 566286425Sdim } 567286425Sdim } /* end successful completion */ 568286425Sdim /* 569286425Sdim * If unsuccesful AND error is an authentication error 570286425Sdim * then refresh credentials and try again, else break 571286425Sdim */ 572286425Sdim else if (stat == RPC_AUTHERROR) 573286425Sdim /* maybe our credentials need to be refreshed ... */ 574286425Sdim if (nrefreshes > 0 && 575286425Sdim AUTH_REFRESH(auth, &reply_msg)) { 576286425Sdim nrefreshes--; 577314564Sdim XDR_DESTROY(&xdrs); 578286425Sdim mtx_lock(&ct->ct_lock); 579327952Sdim goto call_again; 580327952Sdim } 581286425Sdim /* end of unsuccessful completion */ 582327952Sdim } /* end of valid reply message */ 583327952Sdim else { 584286425Sdim errp->re_status = stat = RPC_CANTDECODERES; 585327952Sdim } 586286425Sdim XDR_DESTROY(&xdrs); 587286425Sdim mtx_lock(&ct->ct_lock); 588286425Sdimout: 589286425Sdim mtx_assert(&ct->ct_lock, MA_OWNED); 590286425Sdim 591286425Sdim KASSERT(stat != RPC_SUCCESS || *resultsp, 592286425Sdim ("RPC_SUCCESS without reply")); 593286425Sdim 594286425Sdim if (mreq) 595327952Sdim m_freem(mreq); 596327952Sdim if (cr->cr_mrep) 597327952Sdim m_freem(cr->cr_mrep); 598286425Sdim 599286425Sdim ct->ct_threads--; 600286425Sdim if (ct->ct_closing) 601286425Sdim wakeup(ct); 602327952Sdim 603327952Sdim mtx_unlock(&ct->ct_lock); 604327952Sdim 605286425Sdim if (auth && stat != RPC_SUCCESS) 606286425Sdim AUTH_VALIDATE(auth, xid, NULL, NULL); 607286425Sdim 608286425Sdim free(cr, M_RPC); 609286425Sdim 610286425Sdim return (stat); 611286425Sdim} 612286425Sdim 613286425Sdimstatic void 614286425Sdimclnt_vc_geterr(CLIENT *cl, struct rpc_err *errp) 615286425Sdim{ 616286425Sdim struct ct_data *ct = (struct ct_data *) cl->cl_private; 617286425Sdim 618286425Sdim *errp = ct->ct_error; 619286425Sdim} 620286425Sdim 621286425Sdimstatic bool_t 622286425Sdimclnt_vc_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr) 623286425Sdim{ 624286425Sdim XDR xdrs; 625286425Sdim bool_t dummy; 626286425Sdim 627286425Sdim xdrs.x_op = XDR_FREE; 628286425Sdim dummy = (*xdr_res)(&xdrs, res_ptr); 629327952Sdim 630327952Sdim return (dummy); 631327952Sdim} 632286425Sdim 633286425Sdim/*ARGSUSED*/ 634286425Sdimstatic void 635344779Sdimclnt_vc_abort(CLIENT *cl) 636286425Sdim{ 637286425Sdim} 638286425Sdim 639286425Sdimstatic bool_t 640286425Sdimclnt_vc_control(CLIENT *cl, u_int request, void *info) 641286425Sdim{ 642286425Sdim struct ct_data *ct = (struct ct_data *)cl->cl_private; 643286425Sdim void *infop = info; 644286425Sdim 645286425Sdim mtx_lock(&ct->ct_lock); 646286425Sdim 647286425Sdim switch (request) { 648286425Sdim case CLSET_FD_CLOSE: 649286425Sdim ct->ct_closeit = TRUE; 650286425Sdim mtx_unlock(&ct->ct_lock); 651286425Sdim return (TRUE); 652286425Sdim case CLSET_FD_NCLOSE: 653286425Sdim ct->ct_closeit = FALSE; 654286425Sdim mtx_unlock(&ct->ct_lock); 655286425Sdim return (TRUE); 656286425Sdim default: 657286425Sdim break; 658286425Sdim } 659286425Sdim 660286425Sdim /* for other requests which use info */ 661286425Sdim if (info == NULL) { 662286425Sdim mtx_unlock(&ct->ct_lock); 663286425Sdim return (FALSE); 664286425Sdim } 665286425Sdim switch (request) { 666286425Sdim case CLSET_TIMEOUT: 667286425Sdim if (time_not_ok((struct timeval *)info)) { 668286425Sdim mtx_unlock(&ct->ct_lock); 669286425Sdim return (FALSE); 670286425Sdim } 671286425Sdim ct->ct_wait = *(struct timeval *)infop; 672286425Sdim break; 673286425Sdim case CLGET_TIMEOUT: 674286425Sdim *(struct timeval *)infop = ct->ct_wait; 675286425Sdim break; 676286425Sdim case CLGET_SERVER_ADDR: 677286425Sdim (void) memcpy(info, &ct->ct_addr, (size_t)ct->ct_addr.ss_len); 678286425Sdim break; 679286425Sdim case CLGET_SVC_ADDR: 680286425Sdim /* 681286425Sdim * Slightly different semantics to userland - we use 682286425Sdim * sockaddr instead of netbuf. 683286425Sdim */ 684286425Sdim memcpy(info, &ct->ct_addr, ct->ct_addr.ss_len); 685286425Sdim break; 686286425Sdim case CLSET_SVC_ADDR: /* set to new address */ 687286425Sdim mtx_unlock(&ct->ct_lock); 688286425Sdim return (FALSE); 689286425Sdim case CLGET_XID: 690286425Sdim *(uint32_t *)info = ct->ct_xid; 691286425Sdim break; 692286425Sdim case CLSET_XID: 693286425Sdim /* This will set the xid of the NEXT call */ 694286425Sdim /* decrement by 1 as clnt_vc_call() increments once */ 695286425Sdim ct->ct_xid = *(uint32_t *)info - 1; 696286425Sdim break; 697286425Sdim case CLGET_VERS: 698286425Sdim /* 699286425Sdim * This RELIES on the information that, in the call body, 700286425Sdim * the version number field is the fifth field from the 701286425Sdim * begining of the RPC header. MUST be changed if the 702286425Sdim * call_struct is changed 703286425Sdim */ 704286425Sdim *(uint32_t *)info = 705286425Sdim ntohl(*(uint32_t *)(void *)(ct->ct_mcallc + 706286425Sdim 4 * BYTES_PER_XDR_UNIT)); 707286425Sdim break; 708286425Sdim 709286425Sdim case CLSET_VERS: 710286425Sdim *(uint32_t *)(void *)(ct->ct_mcallc + 711286425Sdim 4 * BYTES_PER_XDR_UNIT) = 712286425Sdim htonl(*(uint32_t *)info); 713286425Sdim break; 714286425Sdim 715286425Sdim case CLGET_PROG: 716286425Sdim /* 717286425Sdim * This RELIES on the information that, in the call body, 718286425Sdim * the program number field is the fourth field from the 719286425Sdim * begining of the RPC header. MUST be changed if the 720286425Sdim * call_struct is changed 721286425Sdim */ 722286425Sdim *(uint32_t *)info = 723286425Sdim ntohl(*(uint32_t *)(void *)(ct->ct_mcallc + 724286425Sdim 3 * BYTES_PER_XDR_UNIT)); 725286425Sdim break; 726286425Sdim 727286425Sdim case CLSET_PROG: 728286425Sdim *(uint32_t *)(void *)(ct->ct_mcallc + 729286425Sdim 3 * BYTES_PER_XDR_UNIT) = 730286425Sdim htonl(*(uint32_t *)info); 731286425Sdim break; 732286425Sdim 733286425Sdim case CLSET_WAITCHAN: 734286425Sdim ct->ct_waitchan = (const char *)info; 735286425Sdim break; 736286425Sdim 737286425Sdim case CLGET_WAITCHAN: 738286425Sdim *(const char **) info = ct->ct_waitchan; 739286425Sdim break; 740286425Sdim 741286425Sdim case CLSET_INTERRUPTIBLE: 742286425Sdim if (*(int *) info) 743286425Sdim ct->ct_waitflag = PCATCH | PBDRY; 744286425Sdim else 745286425Sdim ct->ct_waitflag = 0; 746286425Sdim break; 747286425Sdim 748286425Sdim case CLGET_INTERRUPTIBLE: 749286425Sdim if (ct->ct_waitflag) 750286425Sdim *(int *) info = TRUE; 751286425Sdim else 752286425Sdim *(int *) info = FALSE; 753286425Sdim break; 754286425Sdim 755286425Sdim default: 756286425Sdim mtx_unlock(&ct->ct_lock); 757286425Sdim return (FALSE); 758286425Sdim } 759286425Sdim 760286425Sdim mtx_unlock(&ct->ct_lock); 761286425Sdim return (TRUE); 762286425Sdim} 763327952Sdim 764327952Sdimstatic void 765327952Sdimclnt_vc_close(CLIENT *cl) 766286425Sdim{ 767286425Sdim struct ct_data *ct = (struct ct_data *) cl->cl_private; 768286425Sdim struct ct_request *cr; 769286425Sdim 770286425Sdim mtx_lock(&ct->ct_lock); 771286425Sdim 772286425Sdim if (ct->ct_closed) { 773286425Sdim mtx_unlock(&ct->ct_lock); 774286425Sdim return; 775286425Sdim } 776286425Sdim 777286425Sdim if (ct->ct_closing) { 778286425Sdim while (ct->ct_closing) 779286425Sdim msleep(ct, &ct->ct_lock, 0, "rpcclose", 0); 780286425Sdim KASSERT(ct->ct_closed, ("client should be closed")); 781286425Sdim mtx_unlock(&ct->ct_lock); 782286425Sdim return; 783286425Sdim } 784286425Sdim 785286425Sdim if (ct->ct_socket) { 786286425Sdim ct->ct_closing = TRUE; 787286425Sdim mtx_unlock(&ct->ct_lock); 788286425Sdim 789286425Sdim SOCKBUF_LOCK(&ct->ct_socket->so_rcv); 790286425Sdim soupcall_clear(ct->ct_socket, SO_RCV); 791286425Sdim clnt_vc_upcallsdone(ct); 792286425Sdim SOCKBUF_UNLOCK(&ct->ct_socket->so_rcv); 793286425Sdim 794286425Sdim /* 795286425Sdim * Abort any pending requests and wait until everyone 796286425Sdim * has finished with clnt_vc_call. 797286425Sdim */ 798327952Sdim mtx_lock(&ct->ct_lock); 799286425Sdim TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) { 800286425Sdim cr->cr_xid = 0; 801286425Sdim cr->cr_error = ESHUTDOWN; 802286425Sdim wakeup(cr); 803286425Sdim } 804327952Sdim 805327952Sdim while (ct->ct_threads) 806286425Sdim msleep(ct, &ct->ct_lock, 0, "rpcclose", 0); 807286425Sdim } 808286425Sdim 809286425Sdim ct->ct_closing = FALSE; 810327952Sdim ct->ct_closed = TRUE; 811327952Sdim mtx_unlock(&ct->ct_lock); 812286425Sdim wakeup(ct); 813286425Sdim} 814286425Sdim 815327952Sdimstatic void 816286425Sdimclnt_vc_destroy(CLIENT *cl) 817286425Sdim{ 818286425Sdim struct ct_data *ct = (struct ct_data *) cl->cl_private; 819286425Sdim struct socket *so = NULL; 820286425Sdim 821286425Sdim clnt_vc_close(cl); 822286425Sdim 823286425Sdim mtx_lock(&ct->ct_lock); 824286425Sdim 825286425Sdim if (ct->ct_socket) { 826286425Sdim if (ct->ct_closeit) { 827286425Sdim so = ct->ct_socket; 828286425Sdim } 829286425Sdim } 830286425Sdim 831286425Sdim mtx_unlock(&ct->ct_lock); 832286425Sdim 833286425Sdim mtx_destroy(&ct->ct_lock); 834286425Sdim if (so) { 835286425Sdim soshutdown(so, SHUT_WR); 836286425Sdim soclose(so); 837286425Sdim } 838286425Sdim mem_free(ct, sizeof(struct ct_data)); 839286425Sdim if (cl->cl_netid && cl->cl_netid[0]) 840286425Sdim mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); 841286425Sdim if (cl->cl_tp && cl->cl_tp[0]) 842286425Sdim mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); 843286425Sdim mem_free(cl, sizeof(CLIENT)); 844286425Sdim} 845286425Sdim 846286425Sdim/* 847286425Sdim * Make sure that the time is not garbage. -1 value is disallowed. 848286425Sdim * Note this is different from time_not_ok in clnt_dg.c 849286425Sdim */ 850286425Sdimstatic bool_t 851286425Sdimtime_not_ok(struct timeval *t) 852286425Sdim{ 853286425Sdim return (t->tv_sec <= -1 || t->tv_sec > 100000000 || 854286425Sdim t->tv_usec <= -1 || t->tv_usec > 1000000); 855286425Sdim} 856286425Sdim 857286425Sdimint 858286425Sdimclnt_vc_soupcall(struct socket *so, void *arg, int waitflag) 859286425Sdim{ 860286425Sdim struct ct_data *ct = (struct ct_data *) arg; 861286425Sdim struct uio uio; 862286425Sdim struct mbuf *m; 863327952Sdim struct ct_request *cr; 864286425Sdim int error, rcvflag, foundreq; 865286425Sdim uint32_t xid, header; 866286425Sdim bool_t do_read; 867286425Sdim 868327952Sdim ct->ct_upcallrefs++; 869286425Sdim uio.uio_td = curthread; 870286425Sdim do { 871286425Sdim /* 872286425Sdim * If ct_record_resid is zero, we are waiting for a 873286425Sdim * record mark. 874286425Sdim */ 875286425Sdim if (ct->ct_record_resid == 0) { 876286425Sdim 877286425Sdim /* 878286425Sdim * Make sure there is either a whole record 879286425Sdim * mark in the buffer or there is some other 880286425Sdim * error condition 881286425Sdim */ 882286425Sdim do_read = FALSE; 883286425Sdim if (so->so_rcv.sb_cc >= sizeof(uint32_t) 884286425Sdim || (so->so_rcv.sb_state & SBS_CANTRCVMORE) 885286425Sdim || so->so_error) 886286425Sdim do_read = TRUE; 887286425Sdim 888286425Sdim if (!do_read) 889286425Sdim break; 890286425Sdim 891286425Sdim SOCKBUF_UNLOCK(&so->so_rcv); 892286425Sdim uio.uio_resid = sizeof(uint32_t); 893286425Sdim m = NULL; 894286425Sdim rcvflag = MSG_DONTWAIT | MSG_SOCALLBCK; 895286425Sdim error = soreceive(so, NULL, &uio, &m, NULL, &rcvflag); 896286425Sdim SOCKBUF_LOCK(&so->so_rcv); 897286425Sdim 898286425Sdim if (error == EWOULDBLOCK) 899286425Sdim break; 900286425Sdim 901286425Sdim /* 902286425Sdim * If there was an error, wake up all pending 903286425Sdim * requests. 904286425Sdim */ 905286425Sdim if (error || uio.uio_resid > 0) { 906286425Sdim wakeup_all: 907286425Sdim mtx_lock(&ct->ct_lock); 908286425Sdim if (!error) { 909286425Sdim /* 910286425Sdim * We must have got EOF trying 911286425Sdim * to read from the stream. 912286425Sdim */ 913286425Sdim error = ECONNRESET; 914286425Sdim } 915327952Sdim ct->ct_error.re_status = RPC_CANTRECV; 916327952Sdim ct->ct_error.re_errno = error; 917286425Sdim TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) { 918286425Sdim cr->cr_error = error; 919286425Sdim wakeup(cr); 920286425Sdim } 921286425Sdim mtx_unlock(&ct->ct_lock); 922286425Sdim break; 923286425Sdim } 924286425Sdim m_copydata(m, 0, sizeof(uint32_t), (char *)&header); 925286425Sdim header = ntohl(header); 926286425Sdim ct->ct_record = NULL; 927286425Sdim ct->ct_record_resid = header & 0x7fffffff; 928286425Sdim ct->ct_record_eor = ((header & 0x80000000) != 0); 929286425Sdim m_freem(m); 930286425Sdim } else { 931286425Sdim /* 932327952Sdim * Wait until the socket has the whole record 933286425Sdim * buffered. 934286425Sdim */ 935286425Sdim do_read = FALSE; 936286425Sdim if (so->so_rcv.sb_cc >= ct->ct_record_resid 937286425Sdim || (so->so_rcv.sb_state & SBS_CANTRCVMORE) 938286425Sdim || so->so_error) 939286425Sdim do_read = TRUE; 940286425Sdim 941286425Sdim if (!do_read) 942286425Sdim break; 943286425Sdim 944286425Sdim /* 945286425Sdim * We have the record mark. Read as much as 946286425Sdim * the socket has buffered up to the end of 947286425Sdim * this record. 948286425Sdim */ 949286425Sdim SOCKBUF_UNLOCK(&so->so_rcv); 950286425Sdim uio.uio_resid = ct->ct_record_resid; 951286425Sdim m = NULL; 952286425Sdim rcvflag = MSG_DONTWAIT | MSG_SOCALLBCK; 953286425Sdim error = soreceive(so, NULL, &uio, &m, NULL, &rcvflag); 954286425Sdim SOCKBUF_LOCK(&so->so_rcv); 955286425Sdim 956286425Sdim if (error == EWOULDBLOCK) 957286425Sdim break; 958286425Sdim 959286425Sdim if (error || uio.uio_resid == ct->ct_record_resid) 960286425Sdim goto wakeup_all; 961286425Sdim 962286425Sdim /* 963286425Sdim * If we have part of the record already, 964286425Sdim * chain this bit onto the end. 965286425Sdim */ 966286425Sdim if (ct->ct_record) 967341825Sdim m_last(ct->ct_record)->m_next = m; 968341825Sdim else 969341825Sdim ct->ct_record = m; 970286425Sdim 971286425Sdim ct->ct_record_resid = uio.uio_resid; 972286425Sdim 973286425Sdim /* 974286425Sdim * If we have the entire record, see if we can 975286425Sdim * match it to a request. 976286425Sdim */ 977286425Sdim if (ct->ct_record_resid == 0 978286425Sdim && ct->ct_record_eor) { 979286425Sdim /* 980321369Sdim * The XID is in the first uint32_t of 981321369Sdim * the reply. 982286425Sdim */ 983286425Sdim if (ct->ct_record->m_len < sizeof(xid) && 984286425Sdim m_length(ct->ct_record, NULL) < 985286425Sdim sizeof(xid)) { 986286425Sdim m_freem(ct->ct_record); 987286425Sdim break; 988286425Sdim } 989286425Sdim m_copydata(ct->ct_record, 0, sizeof(xid), 990286425Sdim (char *)&xid); 991286425Sdim xid = ntohl(xid); 992286425Sdim 993286425Sdim mtx_lock(&ct->ct_lock); 994286425Sdim foundreq = 0; 995286425Sdim TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) { 996286425Sdim if (cr->cr_xid == xid) { 997286425Sdim /* 998286425Sdim * This one 999286425Sdim * matches. We leave 1000286425Sdim * the reply mbuf in 1001286425Sdim * cr->cr_mrep. Set 1002286425Sdim * the XID to zero so 1003286425Sdim * that we will ignore 1004286425Sdim * any duplicaed 1005286425Sdim * replies. 1006286425Sdim */ 1007286425Sdim cr->cr_xid = 0; 1008286425Sdim cr->cr_mrep = ct->ct_record; 1009286425Sdim cr->cr_error = 0; 1010286425Sdim foundreq = 1; 1011286425Sdim wakeup(cr); 1012286425Sdim break; 1013286425Sdim } 1014286425Sdim } 1015286425Sdim mtx_unlock(&ct->ct_lock); 1016286425Sdim 1017286425Sdim if (!foundreq) 1018286425Sdim m_freem(ct->ct_record); 1019286425Sdim ct->ct_record = NULL; 1020286425Sdim } 1021286425Sdim } 1022286425Sdim } while (m); 1023286425Sdim ct->ct_upcallrefs--; 1024286425Sdim if (ct->ct_upcallrefs < 0) 1025286425Sdim panic("rpcvc upcall refcnt"); 1026286425Sdim if (ct->ct_upcallrefs == 0) 1027286425Sdim wakeup(&ct->ct_upcallrefs); 1028286425Sdim return (SU_OK); 1029286425Sdim} 1030286425Sdim 1031286425Sdim/* 1032286425Sdim * Wait for all upcalls in progress to complete. 1033286425Sdim */ 1034286425Sdimstatic void 1035286425Sdimclnt_vc_upcallsdone(struct ct_data *ct) 1036286425Sdim{ 1037286425Sdim 1038286425Sdim SOCKBUF_LOCK_ASSERT(&ct->ct_socket->so_rcv); 1039286425Sdim 1040286425Sdim while (ct->ct_upcallrefs > 0) 1041286425Sdim (void) msleep(&ct->ct_upcallrefs, 1042286425Sdim SOCKBUF_MTX(&ct->ct_socket->so_rcv), 0, "rpcvcup", 0); 1043286425Sdim} 1044286425Sdim