1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate * 22*0Sstevel@tonic-gate * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 23*0Sstevel@tonic-gate * Use is subject to license terms. 24*0Sstevel@tonic-gate */ 25*0Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 26*0Sstevel@tonic-gate /* All Rights Reserved */ 27*0Sstevel@tonic-gate /* 28*0Sstevel@tonic-gate * Portions of this source code were derived from Berkeley 29*0Sstevel@tonic-gate * 4.3 BSD under license from the Regents of the University of 30*0Sstevel@tonic-gate * California. 31*0Sstevel@tonic-gate */ 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate /* 36*0Sstevel@tonic-gate * clnt_raw.c 37*0Sstevel@tonic-gate * 38*0Sstevel@tonic-gate * Memory based rpc for simple testing and timing. 39*0Sstevel@tonic-gate * Interface to create an rpc client and server in the same process. 40*0Sstevel@tonic-gate * This lets us similate rpc and get round trip overhead, without 41*0Sstevel@tonic-gate * any interference from the kernel. 42*0Sstevel@tonic-gate */ 43*0Sstevel@tonic-gate #include "mt.h" 44*0Sstevel@tonic-gate #include "rpc_mt.h" 45*0Sstevel@tonic-gate #include <rpc/rpc.h> 46*0Sstevel@tonic-gate #include <rpc/trace.h> 47*0Sstevel@tonic-gate #include <rpc/raw.h> 48*0Sstevel@tonic-gate #include <syslog.h> 49*0Sstevel@tonic-gate 50*0Sstevel@tonic-gate extern mutex_t clntraw_lock; 51*0Sstevel@tonic-gate #define MCALL_MSG_SIZE 24 52*0Sstevel@tonic-gate #ifndef UDPMSGSIZE 53*0Sstevel@tonic-gate #define UDPMSGSIZE 8800 54*0Sstevel@tonic-gate #endif 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate /* 57*0Sstevel@tonic-gate * This is the "network" we will be moving stuff over. 58*0Sstevel@tonic-gate */ 59*0Sstevel@tonic-gate static struct clnt_raw_private { 60*0Sstevel@tonic-gate CLIENT client_object; 61*0Sstevel@tonic-gate XDR xdr_stream; 62*0Sstevel@tonic-gate char *raw_buf; /* should be shared with server handle */ 63*0Sstevel@tonic-gate char mashl_callmsg[MCALL_MSG_SIZE]; 64*0Sstevel@tonic-gate uint_t mcnt; 65*0Sstevel@tonic-gate } *clnt_raw_private; 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gate static struct clnt_ops *clnt_raw_ops(); 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate extern char *calloc(); 70*0Sstevel@tonic-gate extern void free(); 71*0Sstevel@tonic-gate extern void svc_getreq_common(int); 72*0Sstevel@tonic-gate extern bool_t xdr_opaque_auth(); 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate /* 75*0Sstevel@tonic-gate * Create a client handle for memory based rpc. 76*0Sstevel@tonic-gate */ 77*0Sstevel@tonic-gate CLIENT * 78*0Sstevel@tonic-gate clnt_raw_create(rpcprog_t prog, rpcvers_t vers) 79*0Sstevel@tonic-gate { 80*0Sstevel@tonic-gate struct clnt_raw_private *clp; 81*0Sstevel@tonic-gate struct rpc_msg call_msg; 82*0Sstevel@tonic-gate XDR *xdrs; 83*0Sstevel@tonic-gate CLIENT *client; 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate /* VARIABLES PROTECTED BY clntraw_lock: clp */ 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate trace3(TR_clnt_raw_create, 0, prog, vers); 88*0Sstevel@tonic-gate mutex_lock(&clntraw_lock); 89*0Sstevel@tonic-gate clp = clnt_raw_private; 90*0Sstevel@tonic-gate if (clp == NULL) { 91*0Sstevel@tonic-gate /* LINTED pointer alignment */ 92*0Sstevel@tonic-gate clp = (struct clnt_raw_private *)calloc(1, sizeof (*clp)); 93*0Sstevel@tonic-gate if (clp == NULL) { 94*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 95*0Sstevel@tonic-gate trace3(TR_clnt_raw_create, 1, prog, vers); 96*0Sstevel@tonic-gate return ((CLIENT *)NULL); 97*0Sstevel@tonic-gate } 98*0Sstevel@tonic-gate if (_rawcombuf == NULL) { 99*0Sstevel@tonic-gate _rawcombuf = (char *)calloc(UDPMSGSIZE, sizeof (char)); 100*0Sstevel@tonic-gate if (_rawcombuf == NULL) { 101*0Sstevel@tonic-gate syslog(LOG_ERR, "clnt_raw_create: " 102*0Sstevel@tonic-gate "out of memory."); 103*0Sstevel@tonic-gate if (clp) 104*0Sstevel@tonic-gate free(clp); 105*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 106*0Sstevel@tonic-gate trace3(TR_clnt_raw_create, 1, prog, vers); 107*0Sstevel@tonic-gate return ((CLIENT *)NULL); 108*0Sstevel@tonic-gate } 109*0Sstevel@tonic-gate } 110*0Sstevel@tonic-gate clp->raw_buf = _rawcombuf; /* Share it with the server */ 111*0Sstevel@tonic-gate clnt_raw_private = clp; 112*0Sstevel@tonic-gate } 113*0Sstevel@tonic-gate xdrs = &clp->xdr_stream; 114*0Sstevel@tonic-gate client = &clp->client_object; 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate /* 117*0Sstevel@tonic-gate * pre-serialize the static part of the call msg and stash it away 118*0Sstevel@tonic-gate */ 119*0Sstevel@tonic-gate call_msg.rm_direction = CALL; 120*0Sstevel@tonic-gate call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 121*0Sstevel@tonic-gate call_msg.rm_call.cb_prog = prog; 122*0Sstevel@tonic-gate call_msg.rm_call.cb_vers = vers; 123*0Sstevel@tonic-gate xdrmem_create(xdrs, clp->mashl_callmsg, MCALL_MSG_SIZE, XDR_ENCODE); 124*0Sstevel@tonic-gate if (! xdr_callhdr(xdrs, &call_msg)) 125*0Sstevel@tonic-gate (void) syslog(LOG_ERR, 126*0Sstevel@tonic-gate (const char *) "clnt_raw_create : \ 127*0Sstevel@tonic-gate Fatal header serialization error."); 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate clp->mcnt = XDR_GETPOS(xdrs); 130*0Sstevel@tonic-gate XDR_DESTROY(xdrs); 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate /* 133*0Sstevel@tonic-gate * Set xdrmem for client/server shared buffer 134*0Sstevel@tonic-gate */ 135*0Sstevel@tonic-gate xdrmem_create(xdrs, clp->raw_buf, UDPMSGSIZE, XDR_FREE); 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate /* 138*0Sstevel@tonic-gate * create client handle 139*0Sstevel@tonic-gate */ 140*0Sstevel@tonic-gate client->cl_ops = clnt_raw_ops(); 141*0Sstevel@tonic-gate client->cl_auth = authnone_create(); 142*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 143*0Sstevel@tonic-gate trace3(TR_clnt_raw_create, 1, prog, vers); 144*0Sstevel@tonic-gate return (client); 145*0Sstevel@tonic-gate } 146*0Sstevel@tonic-gate 147*0Sstevel@tonic-gate /*ARGSUSED*/ 148*0Sstevel@tonic-gate static enum clnt_stat 149*0Sstevel@tonic-gate clnt_raw_call(CLIENT *h, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp, 150*0Sstevel@tonic-gate xdrproc_t xresults, caddr_t resultsp, struct timeval timeout) 151*0Sstevel@tonic-gate { 152*0Sstevel@tonic-gate struct clnt_raw_private *clp; 153*0Sstevel@tonic-gate XDR *xdrs; 154*0Sstevel@tonic-gate struct rpc_msg msg; 155*0Sstevel@tonic-gate enum clnt_stat status; 156*0Sstevel@tonic-gate struct rpc_err error; 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate trace3(TR_clnt_raw_call, 0, h, proc); 159*0Sstevel@tonic-gate mutex_lock(&clntraw_lock); 160*0Sstevel@tonic-gate clp = clnt_raw_private; 161*0Sstevel@tonic-gate xdrs = &clp->xdr_stream; 162*0Sstevel@tonic-gate if (clp == NULL) { 163*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 164*0Sstevel@tonic-gate trace3(TR_clnt_raw_call, 1, h, proc); 165*0Sstevel@tonic-gate return (RPC_FAILED); 166*0Sstevel@tonic-gate } 167*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate call_again: 170*0Sstevel@tonic-gate /* 171*0Sstevel@tonic-gate * send request 172*0Sstevel@tonic-gate */ 173*0Sstevel@tonic-gate xdrs->x_op = XDR_ENCODE; 174*0Sstevel@tonic-gate XDR_SETPOS(xdrs, 0); 175*0Sstevel@tonic-gate /* LINTED pointer alignment */ 176*0Sstevel@tonic-gate ((struct rpc_msg *)clp->mashl_callmsg)->rm_xid++; 177*0Sstevel@tonic-gate if ((! XDR_PUTBYTES(xdrs, clp->mashl_callmsg, clp->mcnt)) || 178*0Sstevel@tonic-gate (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) || 179*0Sstevel@tonic-gate (! AUTH_MARSHALL(h->cl_auth, xdrs)) || 180*0Sstevel@tonic-gate (! (*xargs)(xdrs, argsp))) { 181*0Sstevel@tonic-gate trace3(TR_clnt_raw_call, 1, h, proc); 182*0Sstevel@tonic-gate return (RPC_CANTENCODEARGS); 183*0Sstevel@tonic-gate } 184*0Sstevel@tonic-gate (void) XDR_GETPOS(xdrs); /* called just to cause overhead */ 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate /* 187*0Sstevel@tonic-gate * We have to call server input routine here because this is 188*0Sstevel@tonic-gate * all going on in one process. 189*0Sstevel@tonic-gate * By convention using FD_SETSIZE as the psuedo file descriptor. 190*0Sstevel@tonic-gate */ 191*0Sstevel@tonic-gate svc_getreq_common(FD_SETSIZE); 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate /* 194*0Sstevel@tonic-gate * get results 195*0Sstevel@tonic-gate */ 196*0Sstevel@tonic-gate xdrs->x_op = XDR_DECODE; 197*0Sstevel@tonic-gate XDR_SETPOS(xdrs, 0); 198*0Sstevel@tonic-gate msg.acpted_rply.ar_verf = _null_auth; 199*0Sstevel@tonic-gate msg.acpted_rply.ar_results.where = resultsp; 200*0Sstevel@tonic-gate msg.acpted_rply.ar_results.proc = xresults; 201*0Sstevel@tonic-gate if (! xdr_replymsg(xdrs, &msg)) { 202*0Sstevel@tonic-gate trace3(TR_clnt_raw_call, 1, h, proc); 203*0Sstevel@tonic-gate return (RPC_CANTDECODERES); 204*0Sstevel@tonic-gate } 205*0Sstevel@tonic-gate if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && 206*0Sstevel@tonic-gate (msg.acpted_rply.ar_stat == SUCCESS)) 207*0Sstevel@tonic-gate status = RPC_SUCCESS; 208*0Sstevel@tonic-gate else { 209*0Sstevel@tonic-gate __seterr_reply(&msg, &error); 210*0Sstevel@tonic-gate status = error.re_status; 211*0Sstevel@tonic-gate } 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate if (status == RPC_SUCCESS) { 214*0Sstevel@tonic-gate if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) { 215*0Sstevel@tonic-gate status = RPC_AUTHERROR; 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate /* end successful completion */ 218*0Sstevel@tonic-gate } else { 219*0Sstevel@tonic-gate if (AUTH_REFRESH(h->cl_auth, &msg)) 220*0Sstevel@tonic-gate goto call_again; 221*0Sstevel@tonic-gate /* end of unsuccessful completion */ 222*0Sstevel@tonic-gate } 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate if (status == RPC_SUCCESS) { 225*0Sstevel@tonic-gate if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) { 226*0Sstevel@tonic-gate status = RPC_AUTHERROR; 227*0Sstevel@tonic-gate } 228*0Sstevel@tonic-gate if (msg.acpted_rply.ar_verf.oa_base != NULL) { 229*0Sstevel@tonic-gate xdrs->x_op = XDR_FREE; 230*0Sstevel@tonic-gate (void) xdr_opaque_auth(xdrs, 231*0Sstevel@tonic-gate &(msg.acpted_rply.ar_verf)); 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate } 234*0Sstevel@tonic-gate trace3(TR_clnt_raw_call, 1, h, proc); 235*0Sstevel@tonic-gate return (status); 236*0Sstevel@tonic-gate } 237*0Sstevel@tonic-gate 238*0Sstevel@tonic-gate /*ARGSUSED*/ 239*0Sstevel@tonic-gate static enum clnt_stat 240*0Sstevel@tonic-gate clnt_raw_send(CLIENT *h, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp) 241*0Sstevel@tonic-gate { 242*0Sstevel@tonic-gate struct clnt_raw_private *clp; 243*0Sstevel@tonic-gate XDR *xdrs; 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate trace3(TR_clnt_raw_send, 0, h, proc); 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate mutex_lock(&clntraw_lock); 248*0Sstevel@tonic-gate clp = clnt_raw_private; 249*0Sstevel@tonic-gate xdrs = &clp->xdr_stream; 250*0Sstevel@tonic-gate if (clp == NULL) { 251*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 252*0Sstevel@tonic-gate trace3(TR_clnt_raw_send, 1, h, proc); 253*0Sstevel@tonic-gate return (RPC_FAILED); 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate /* 258*0Sstevel@tonic-gate * send request 259*0Sstevel@tonic-gate */ 260*0Sstevel@tonic-gate xdrs->x_op = XDR_ENCODE; 261*0Sstevel@tonic-gate XDR_SETPOS(xdrs, 0); 262*0Sstevel@tonic-gate /* LINTED pointer alignment */ 263*0Sstevel@tonic-gate ((struct rpc_msg *)clp->mashl_callmsg)->rm_xid++; 264*0Sstevel@tonic-gate if ((! XDR_PUTBYTES(xdrs, clp->mashl_callmsg, clp->mcnt)) || 265*0Sstevel@tonic-gate (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) || 266*0Sstevel@tonic-gate (! AUTH_MARSHALL(h->cl_auth, xdrs)) || 267*0Sstevel@tonic-gate (! (*xargs)(xdrs, argsp))) { 268*0Sstevel@tonic-gate trace3(TR_clnt_raw_send, 1, h, proc); 269*0Sstevel@tonic-gate return (RPC_CANTENCODEARGS); 270*0Sstevel@tonic-gate } 271*0Sstevel@tonic-gate (void) XDR_GETPOS(xdrs); /* called just to cause overhead */ 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate /* 274*0Sstevel@tonic-gate * We have to call server input routine here because this is 275*0Sstevel@tonic-gate * all going on in one process. 276*0Sstevel@tonic-gate * By convention using FD_SETSIZE as the psuedo file descriptor. 277*0Sstevel@tonic-gate */ 278*0Sstevel@tonic-gate svc_getreq_common(FD_SETSIZE); 279*0Sstevel@tonic-gate 280*0Sstevel@tonic-gate return (RPC_SUCCESS); 281*0Sstevel@tonic-gate } 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate /*ARGSUSED*/ 284*0Sstevel@tonic-gate static void 285*0Sstevel@tonic-gate clnt_raw_geterr(CLIENT *cl, struct rpc_err *errp) 286*0Sstevel@tonic-gate { 287*0Sstevel@tonic-gate trace1(TR_clnt_raw_geterr, 0); 288*0Sstevel@tonic-gate trace1(TR_clnt_raw_geterr, 1); 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate 291*0Sstevel@tonic-gate /*ARGSUSED*/ 292*0Sstevel@tonic-gate static bool_t 293*0Sstevel@tonic-gate clnt_raw_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr) 294*0Sstevel@tonic-gate { 295*0Sstevel@tonic-gate struct clnt_raw_private *clp; 296*0Sstevel@tonic-gate XDR *xdrs; 297*0Sstevel@tonic-gate static bool_t dummy; 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate trace2(TR_clnt_raw_freeres, 0, cl); 300*0Sstevel@tonic-gate mutex_lock(&clntraw_lock); 301*0Sstevel@tonic-gate clp = clnt_raw_private; 302*0Sstevel@tonic-gate xdrs = &clp->xdr_stream; 303*0Sstevel@tonic-gate if (clp == NULL) { 304*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 305*0Sstevel@tonic-gate trace2(TR_clnt_raw_freeres, 1, cl); 306*0Sstevel@tonic-gate return (FALSE); 307*0Sstevel@tonic-gate } 308*0Sstevel@tonic-gate mutex_unlock(&clntraw_lock); 309*0Sstevel@tonic-gate xdrs->x_op = XDR_FREE; 310*0Sstevel@tonic-gate dummy = (*xdr_res)(xdrs, res_ptr); 311*0Sstevel@tonic-gate trace2(TR_clnt_raw_freeres, 1, cl); 312*0Sstevel@tonic-gate return (dummy); 313*0Sstevel@tonic-gate } 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate /*ARGSUSED*/ 316*0Sstevel@tonic-gate static void 317*0Sstevel@tonic-gate clnt_raw_abort(CLIENT *cl, struct rpc_err *errp) 318*0Sstevel@tonic-gate { 319*0Sstevel@tonic-gate trace1(TR_clnt_raw_abort, 0); 320*0Sstevel@tonic-gate trace1(TR_clnt_raw_abort, 1); 321*0Sstevel@tonic-gate } 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate /*ARGSUSED*/ 324*0Sstevel@tonic-gate static bool_t 325*0Sstevel@tonic-gate clnt_raw_control(CLIENT *cl, int request, char *info) 326*0Sstevel@tonic-gate { 327*0Sstevel@tonic-gate trace1(TR_clnt_raw_control, 0); 328*0Sstevel@tonic-gate trace1(TR_clnt_raw_control, 1); 329*0Sstevel@tonic-gate return (FALSE); 330*0Sstevel@tonic-gate } 331*0Sstevel@tonic-gate 332*0Sstevel@tonic-gate /*ARGSUSED*/ 333*0Sstevel@tonic-gate static void 334*0Sstevel@tonic-gate clnt_raw_destroy(CLIENT *cl) 335*0Sstevel@tonic-gate { 336*0Sstevel@tonic-gate trace1(TR_clnt_raw_destroy, 0); 337*0Sstevel@tonic-gate trace1(TR_clnt_raw_destroy, 1); 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate static struct clnt_ops * 341*0Sstevel@tonic-gate clnt_raw_ops(void) 342*0Sstevel@tonic-gate { 343*0Sstevel@tonic-gate static struct clnt_ops ops; 344*0Sstevel@tonic-gate extern mutex_t ops_lock; 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate /* VARIABLES PROTECTED BY ops_lock: ops */ 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate trace1(TR_clnt_raw_ops, 0); 349*0Sstevel@tonic-gate mutex_lock(&ops_lock); 350*0Sstevel@tonic-gate if (ops.cl_call == NULL) { 351*0Sstevel@tonic-gate ops.cl_call = clnt_raw_call; 352*0Sstevel@tonic-gate ops.cl_send = clnt_raw_send; 353*0Sstevel@tonic-gate ops.cl_abort = clnt_raw_abort; 354*0Sstevel@tonic-gate ops.cl_geterr = clnt_raw_geterr; 355*0Sstevel@tonic-gate ops.cl_freeres = clnt_raw_freeres; 356*0Sstevel@tonic-gate ops.cl_destroy = clnt_raw_destroy; 357*0Sstevel@tonic-gate ops.cl_control = clnt_raw_control; 358*0Sstevel@tonic-gate } 359*0Sstevel@tonic-gate mutex_unlock(&ops_lock); 360*0Sstevel@tonic-gate trace1(TR_clnt_raw_ops, 1); 361*0Sstevel@tonic-gate return (&ops); 362*0Sstevel@tonic-gate } 363