10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51676Sjpk * Common Development and Distribution License (the "License"). 61676Sjpk * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 228778SErik.Nordmark@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T 280Sstevel@tonic-gate * All Rights Reserved 290Sstevel@tonic-gate */ 300Sstevel@tonic-gate 310Sstevel@tonic-gate /* 320Sstevel@tonic-gate * Portions of this source code were derived from Berkeley 4.3 BSD 330Sstevel@tonic-gate * under license from the Regents of the University of California. 340Sstevel@tonic-gate */ 350Sstevel@tonic-gate 360Sstevel@tonic-gate 370Sstevel@tonic-gate /* 380Sstevel@tonic-gate * Implements a kernel based, client side RPC over Connection Oriented 390Sstevel@tonic-gate * Transports (COTS). 400Sstevel@tonic-gate */ 410Sstevel@tonic-gate 420Sstevel@tonic-gate /* 430Sstevel@tonic-gate * Much of this file has been re-written to let NFS work better over slow 440Sstevel@tonic-gate * transports. A description follows. 450Sstevel@tonic-gate * 460Sstevel@tonic-gate * One of the annoying things about kRPC/COTS is that it will temporarily 470Sstevel@tonic-gate * create more than one connection between a client and server. This 480Sstevel@tonic-gate * happens because when a connection is made, the end-points entry in the 490Sstevel@tonic-gate * linked list of connections (headed by cm_hd), is removed so that other 500Sstevel@tonic-gate * threads don't mess with it. Went ahead and bit the bullet by keeping 510Sstevel@tonic-gate * the endpoint on the connection list and introducing state bits, 520Sstevel@tonic-gate * condition variables etc. to the connection entry data structure (struct 530Sstevel@tonic-gate * cm_xprt). 540Sstevel@tonic-gate * 550Sstevel@tonic-gate * Here is a summary of the changes to cm-xprt: 560Sstevel@tonic-gate * 570Sstevel@tonic-gate * x_ctime is the timestamp of when the endpoint was last 580Sstevel@tonic-gate * connected or disconnected. If an end-point is ever disconnected 590Sstevel@tonic-gate * or re-connected, then any outstanding RPC request is presumed 600Sstevel@tonic-gate * lost, telling clnt_cots_kcallit that it needs to re-send the 610Sstevel@tonic-gate * request, not just wait for the original request's reply to 620Sstevel@tonic-gate * arrive. 630Sstevel@tonic-gate * 640Sstevel@tonic-gate * x_thread flag which tells us if a thread is doing a connection attempt. 650Sstevel@tonic-gate * 660Sstevel@tonic-gate * x_waitdis flag which tells us we are waiting a disconnect ACK. 670Sstevel@tonic-gate * 680Sstevel@tonic-gate * x_needdis flag which tells us we need to send a T_DISCONN_REQ 690Sstevel@tonic-gate * to kill the connection. 700Sstevel@tonic-gate * 710Sstevel@tonic-gate * x_needrel flag which tells us we need to send a T_ORDREL_REQ to 720Sstevel@tonic-gate * gracefully close the connection. 730Sstevel@tonic-gate * 740Sstevel@tonic-gate * #defined bitmasks for the all the b_* bits so that more 750Sstevel@tonic-gate * efficient (and at times less clumsy) masks can be used to 760Sstevel@tonic-gate * manipulated state in cases where multiple bits have to 770Sstevel@tonic-gate * set/cleared/checked in the same critical section. 780Sstevel@tonic-gate * 790Sstevel@tonic-gate * x_conn_cv and x_dis-_cv are new condition variables to let 800Sstevel@tonic-gate * threads knows when the connection attempt is done, and to let 810Sstevel@tonic-gate * the connecting thread know when the disconnect handshake is 820Sstevel@tonic-gate * done. 830Sstevel@tonic-gate * 840Sstevel@tonic-gate * Added the CONN_HOLD() macro so that all reference holds have the same 850Sstevel@tonic-gate * look and feel. 860Sstevel@tonic-gate * 870Sstevel@tonic-gate * In the private (cku_private) portion of the client handle, 880Sstevel@tonic-gate * 890Sstevel@tonic-gate * cku_flags replaces the cku_sent a boolean. cku_flags keeps 900Sstevel@tonic-gate * track of whether a request as been sent, and whether the 910Sstevel@tonic-gate * client's handles call record is on the dispatch list (so that 920Sstevel@tonic-gate * the reply can be matched by XID to the right client handle). 930Sstevel@tonic-gate * The idea of CKU_ONQUEUE is that we can exit clnt_cots_kcallit() 940Sstevel@tonic-gate * and still have the response find the right client handle so 950Sstevel@tonic-gate * that the retry of CLNT_CALL() gets the result. Testing, found 960Sstevel@tonic-gate * situations where if the timeout was increased, performance 970Sstevel@tonic-gate * degraded. This was due to us hitting a window where the thread 980Sstevel@tonic-gate * was back in rfscall() (probably printing server not responding) 990Sstevel@tonic-gate * while the response came back but no place to put it. 1000Sstevel@tonic-gate * 1010Sstevel@tonic-gate * cku_ctime is just a cache of x_ctime. If they match, 1020Sstevel@tonic-gate * clnt_cots_kcallit() won't to send a retry (unless the maximum 1030Sstevel@tonic-gate * receive count limit as been reached). If the don't match, then 1040Sstevel@tonic-gate * we assume the request has been lost, and a retry of the request 1050Sstevel@tonic-gate * is needed. 1060Sstevel@tonic-gate * 1070Sstevel@tonic-gate * cku_recv_attempts counts the number of receive count attempts 1080Sstevel@tonic-gate * after one try is sent on the wire. 1090Sstevel@tonic-gate * 1100Sstevel@tonic-gate * Added the clnt_delay() routine so that interruptible and 1110Sstevel@tonic-gate * noninterruptible delays are possible. 1120Sstevel@tonic-gate * 1130Sstevel@tonic-gate * CLNT_MIN_TIMEOUT has been bumped to 10 seconds from 3. This is used to 1140Sstevel@tonic-gate * control how long the client delays before returned after getting 1150Sstevel@tonic-gate * ECONNREFUSED. At 3 seconds, 8 client threads per mount really does bash 1160Sstevel@tonic-gate * a server that may be booting and not yet started nfsd. 1170Sstevel@tonic-gate * 1180Sstevel@tonic-gate * CLNT_MAXRECV_WITHOUT_RETRY is a new macro (value of 3) (with a tunable) 1190Sstevel@tonic-gate * Why don't we just wait forever (receive an infinite # of times)? 1200Sstevel@tonic-gate * Because the server may have rebooted. More insidious is that some 1210Sstevel@tonic-gate * servers (ours) will drop NFS/TCP requests in some cases. This is bad, 1220Sstevel@tonic-gate * but it is a reality. 1230Sstevel@tonic-gate * 1240Sstevel@tonic-gate * The case of a server doing orderly release really messes up the 1250Sstevel@tonic-gate * client's recovery, especially if the server's TCP implementation is 1260Sstevel@tonic-gate * buggy. It was found was that the kRPC/COTS client was breaking some 1270Sstevel@tonic-gate * TPI rules, such as not waiting for the acknowledgement of a 1280Sstevel@tonic-gate * T_DISCON_REQ (hence the added case statements T_ERROR_ACK, T_OK_ACK and 1290Sstevel@tonic-gate * T_DISCON_REQ in clnt_dispatch_notifyall()). 1300Sstevel@tonic-gate * 1310Sstevel@tonic-gate * One of things that we've seen is that a kRPC TCP endpoint goes into 1320Sstevel@tonic-gate * TIMEWAIT and a thus a reconnect takes a long time to satisfy because 1330Sstevel@tonic-gate * that the TIMEWAIT state takes a while to finish. If a server sends a 1340Sstevel@tonic-gate * T_ORDREL_IND, there is little point in an RPC client doing a 1350Sstevel@tonic-gate * T_ORDREL_REQ, because the RPC request isn't going to make it (the 1360Sstevel@tonic-gate * server is saying that it won't accept any more data). So kRPC was 1370Sstevel@tonic-gate * changed to send a T_DISCON_REQ when we get a T_ORDREL_IND. So now the 1380Sstevel@tonic-gate * connection skips the TIMEWAIT state and goes straight to a bound state 1390Sstevel@tonic-gate * that kRPC can quickly switch to connected. 1400Sstevel@tonic-gate * 1410Sstevel@tonic-gate * Code that issues TPI request must use waitforack() to wait for the 1420Sstevel@tonic-gate * corresponding ack (assuming there is one) in any future modifications. 1430Sstevel@tonic-gate * This works around problems that may be introduced by breaking TPI rules 1440Sstevel@tonic-gate * (by submitting new calls before earlier requests have been acked) in the 1450Sstevel@tonic-gate * case of a signal or other early return. waitforack() depends on 1460Sstevel@tonic-gate * clnt_dispatch_notifyconn() to issue the wakeup when the ack 1470Sstevel@tonic-gate * arrives, so adding new TPI calls may require corresponding changes 1480Sstevel@tonic-gate * to clnt_dispatch_notifyconn(). Presently, the timeout period is based on 1490Sstevel@tonic-gate * CLNT_MIN_TIMEOUT which is 10 seconds. If you modify this value, be sure 1500Sstevel@tonic-gate * not to set it too low or TPI ACKS will be lost. 1510Sstevel@tonic-gate */ 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate #include <sys/param.h> 1540Sstevel@tonic-gate #include <sys/types.h> 1550Sstevel@tonic-gate #include <sys/user.h> 1560Sstevel@tonic-gate #include <sys/systm.h> 1570Sstevel@tonic-gate #include <sys/sysmacros.h> 1580Sstevel@tonic-gate #include <sys/proc.h> 1590Sstevel@tonic-gate #include <sys/socket.h> 1600Sstevel@tonic-gate #include <sys/file.h> 1610Sstevel@tonic-gate #include <sys/stream.h> 1620Sstevel@tonic-gate #include <sys/strsubr.h> 1630Sstevel@tonic-gate #include <sys/stropts.h> 1640Sstevel@tonic-gate #include <sys/strsun.h> 1650Sstevel@tonic-gate #include <sys/timod.h> 1660Sstevel@tonic-gate #include <sys/tiuser.h> 1670Sstevel@tonic-gate #include <sys/tihdr.h> 1680Sstevel@tonic-gate #include <sys/t_kuser.h> 1690Sstevel@tonic-gate #include <sys/fcntl.h> 1700Sstevel@tonic-gate #include <sys/errno.h> 1710Sstevel@tonic-gate #include <sys/kmem.h> 1720Sstevel@tonic-gate #include <sys/debug.h> 1730Sstevel@tonic-gate #include <sys/systm.h> 1740Sstevel@tonic-gate #include <sys/kstat.h> 1750Sstevel@tonic-gate #include <sys/t_lock.h> 1760Sstevel@tonic-gate #include <sys/ddi.h> 1770Sstevel@tonic-gate #include <sys/cmn_err.h> 1780Sstevel@tonic-gate #include <sys/time.h> 1790Sstevel@tonic-gate #include <sys/isa_defs.h> 1800Sstevel@tonic-gate #include <sys/callb.h> 1810Sstevel@tonic-gate #include <sys/sunddi.h> 1820Sstevel@tonic-gate #include <sys/atomic.h> 1838205SSiddheshwar.Mahesh@Sun.COM #include <sys/sdt.h> 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate #include <netinet/in.h> 1860Sstevel@tonic-gate #include <netinet/tcp.h> 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate #include <rpc/types.h> 1890Sstevel@tonic-gate #include <rpc/xdr.h> 1900Sstevel@tonic-gate #include <rpc/auth.h> 1910Sstevel@tonic-gate #include <rpc/clnt.h> 1920Sstevel@tonic-gate #include <rpc/rpc_msg.h> 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate #define COTS_DEFAULT_ALLOCSIZE 2048 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate #define WIRE_HDR_SIZE 20 /* serialized call header, sans proc number */ 1970Sstevel@tonic-gate #define MSG_OFFSET 128 /* offset of call into the mblk */ 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate const char *kinet_ntop6(uchar_t *, char *, size_t); 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate static int clnt_cots_ksettimers(CLIENT *, struct rpc_timers *, 2020Sstevel@tonic-gate struct rpc_timers *, int, void(*)(int, int, caddr_t), caddr_t, uint32_t); 2030Sstevel@tonic-gate static enum clnt_stat clnt_cots_kcallit(CLIENT *, rpcproc_t, xdrproc_t, 2040Sstevel@tonic-gate caddr_t, xdrproc_t, caddr_t, struct timeval); 2050Sstevel@tonic-gate static void clnt_cots_kabort(CLIENT *); 2060Sstevel@tonic-gate static void clnt_cots_kerror(CLIENT *, struct rpc_err *); 2070Sstevel@tonic-gate static bool_t clnt_cots_kfreeres(CLIENT *, xdrproc_t, caddr_t); 2080Sstevel@tonic-gate static void clnt_cots_kdestroy(CLIENT *); 2090Sstevel@tonic-gate static bool_t clnt_cots_kcontrol(CLIENT *, int, char *); 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate /* List of transports managed by the connection manager. */ 2130Sstevel@tonic-gate struct cm_xprt { 2140Sstevel@tonic-gate TIUSER *x_tiptr; /* transport handle */ 2150Sstevel@tonic-gate queue_t *x_wq; /* send queue */ 2160Sstevel@tonic-gate clock_t x_time; /* last time we handed this xprt out */ 2170Sstevel@tonic-gate clock_t x_ctime; /* time we went to CONNECTED */ 2180Sstevel@tonic-gate int x_tidu_size; /* TIDU size of this transport */ 2190Sstevel@tonic-gate union { 2200Sstevel@tonic-gate struct { 2210Sstevel@tonic-gate unsigned int 2220Sstevel@tonic-gate #ifdef _BIT_FIELDS_HTOL 2230Sstevel@tonic-gate b_closing: 1, /* we've sent a ord rel on this conn */ 2240Sstevel@tonic-gate b_dead: 1, /* transport is closed or disconn */ 2250Sstevel@tonic-gate b_doomed: 1, /* too many conns, let this go idle */ 2260Sstevel@tonic-gate b_connected: 1, /* this connection is connected */ 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate b_ordrel: 1, /* do an orderly release? */ 2290Sstevel@tonic-gate b_thread: 1, /* thread doing connect */ 2300Sstevel@tonic-gate b_waitdis: 1, /* waiting for disconnect ACK */ 2310Sstevel@tonic-gate b_needdis: 1, /* need T_DISCON_REQ */ 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate b_needrel: 1, /* need T_ORDREL_REQ */ 2340Sstevel@tonic-gate b_early_disc: 1, /* got a T_ORDREL_IND or T_DISCON_IND */ 2350Sstevel@tonic-gate /* disconnect during connect */ 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate b_pad: 22; 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate #endif 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate #ifdef _BIT_FIELDS_LTOH 2420Sstevel@tonic-gate b_pad: 22, 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate b_early_disc: 1, /* got a T_ORDREL_IND or T_DISCON_IND */ 2450Sstevel@tonic-gate /* disconnect during connect */ 2460Sstevel@tonic-gate b_needrel: 1, /* need T_ORDREL_REQ */ 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate b_needdis: 1, /* need T_DISCON_REQ */ 2490Sstevel@tonic-gate b_waitdis: 1, /* waiting for disconnect ACK */ 2500Sstevel@tonic-gate b_thread: 1, /* thread doing connect */ 2510Sstevel@tonic-gate b_ordrel: 1, /* do an orderly release? */ 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate b_connected: 1, /* this connection is connected */ 2540Sstevel@tonic-gate b_doomed: 1, /* too many conns, let this go idle */ 2550Sstevel@tonic-gate b_dead: 1, /* transport is closed or disconn */ 2560Sstevel@tonic-gate b_closing: 1; /* we've sent a ord rel on this conn */ 2570Sstevel@tonic-gate #endif 2580Sstevel@tonic-gate } bit; unsigned int word; 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate #define x_closing x_state.bit.b_closing 2610Sstevel@tonic-gate #define x_dead x_state.bit.b_dead 2620Sstevel@tonic-gate #define x_doomed x_state.bit.b_doomed 2630Sstevel@tonic-gate #define x_connected x_state.bit.b_connected 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate #define x_ordrel x_state.bit.b_ordrel 2660Sstevel@tonic-gate #define x_thread x_state.bit.b_thread 2670Sstevel@tonic-gate #define x_waitdis x_state.bit.b_waitdis 2680Sstevel@tonic-gate #define x_needdis x_state.bit.b_needdis 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate #define x_needrel x_state.bit.b_needrel 2710Sstevel@tonic-gate #define x_early_disc x_state.bit.b_early_disc 2720Sstevel@tonic-gate 2730Sstevel@tonic-gate #define x_state_flags x_state.word 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate #define X_CLOSING 0x80000000 2760Sstevel@tonic-gate #define X_DEAD 0x40000000 2770Sstevel@tonic-gate #define X_DOOMED 0x20000000 2780Sstevel@tonic-gate #define X_CONNECTED 0x10000000 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate #define X_ORDREL 0x08000000 2810Sstevel@tonic-gate #define X_THREAD 0x04000000 2820Sstevel@tonic-gate #define X_WAITDIS 0x02000000 2830Sstevel@tonic-gate #define X_NEEDDIS 0x01000000 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate #define X_NEEDREL 0x00800000 2860Sstevel@tonic-gate #define X_EARLYDISC 0x00400000 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate #define X_BADSTATES (X_CLOSING | X_DEAD | X_DOOMED) 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate } x_state; 2910Sstevel@tonic-gate int x_ref; /* number of users of this xprt */ 2920Sstevel@tonic-gate int x_family; /* address family of transport */ 2930Sstevel@tonic-gate dev_t x_rdev; /* device number of transport */ 2940Sstevel@tonic-gate struct cm_xprt *x_next; 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate struct netbuf x_server; /* destination address */ 2970Sstevel@tonic-gate struct netbuf x_src; /* src address (for retries) */ 2980Sstevel@tonic-gate kmutex_t x_lock; /* lock on this entry */ 2990Sstevel@tonic-gate kcondvar_t x_cv; /* to signal when can be closed */ 3000Sstevel@tonic-gate kcondvar_t x_conn_cv; /* to signal when connection attempt */ 3010Sstevel@tonic-gate /* is complete */ 3020Sstevel@tonic-gate kstat_t *x_ksp; 3030Sstevel@tonic-gate 3040Sstevel@tonic-gate kcondvar_t x_dis_cv; /* to signal when disconnect attempt */ 3050Sstevel@tonic-gate /* is complete */ 3060Sstevel@tonic-gate zoneid_t x_zoneid; /* zone this xprt belongs to */ 3070Sstevel@tonic-gate }; 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate typedef struct cm_kstat_xprt { 3100Sstevel@tonic-gate kstat_named_t x_wq; 3110Sstevel@tonic-gate kstat_named_t x_server; 3120Sstevel@tonic-gate kstat_named_t x_family; 3130Sstevel@tonic-gate kstat_named_t x_rdev; 3140Sstevel@tonic-gate kstat_named_t x_time; 3150Sstevel@tonic-gate kstat_named_t x_state; 3160Sstevel@tonic-gate kstat_named_t x_ref; 3170Sstevel@tonic-gate kstat_named_t x_port; 3180Sstevel@tonic-gate } cm_kstat_xprt_t; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate static cm_kstat_xprt_t cm_kstat_template = { 3210Sstevel@tonic-gate { "write_queue", KSTAT_DATA_UINT32 }, 3220Sstevel@tonic-gate { "server", KSTAT_DATA_STRING }, 3230Sstevel@tonic-gate { "addr_family", KSTAT_DATA_UINT32 }, 3240Sstevel@tonic-gate { "device", KSTAT_DATA_UINT32 }, 3250Sstevel@tonic-gate { "time_stamp", KSTAT_DATA_UINT32 }, 3260Sstevel@tonic-gate { "status", KSTAT_DATA_UINT32 }, 3270Sstevel@tonic-gate { "ref_count", KSTAT_DATA_INT32 }, 3280Sstevel@tonic-gate { "port", KSTAT_DATA_UINT32 }, 3290Sstevel@tonic-gate }; 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate /* 3320Sstevel@tonic-gate * The inverse of this is connmgr_release(). 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate #define CONN_HOLD(Cm_entry) {\ 3350Sstevel@tonic-gate mutex_enter(&(Cm_entry)->x_lock); \ 3360Sstevel@tonic-gate (Cm_entry)->x_ref++; \ 3370Sstevel@tonic-gate mutex_exit(&(Cm_entry)->x_lock); \ 3380Sstevel@tonic-gate } 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate /* 3420Sstevel@tonic-gate * Private data per rpc handle. This structure is allocated by 3430Sstevel@tonic-gate * clnt_cots_kcreate, and freed by clnt_cots_kdestroy. 3440Sstevel@tonic-gate */ 3450Sstevel@tonic-gate typedef struct cku_private_s { 3460Sstevel@tonic-gate CLIENT cku_client; /* client handle */ 3470Sstevel@tonic-gate calllist_t cku_call; /* for dispatching calls */ 3480Sstevel@tonic-gate struct rpc_err cku_err; /* error status */ 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate struct netbuf cku_srcaddr; /* source address for retries */ 3510Sstevel@tonic-gate int cku_addrfmly; /* for binding port */ 3520Sstevel@tonic-gate struct netbuf cku_addr; /* remote address */ 3530Sstevel@tonic-gate dev_t cku_device; /* device to use */ 3540Sstevel@tonic-gate uint_t cku_flags; 3550Sstevel@tonic-gate #define CKU_ONQUEUE 0x1 3560Sstevel@tonic-gate #define CKU_SENT 0x2 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate bool_t cku_progress; /* for CLSET_PROGRESS */ 3590Sstevel@tonic-gate uint32_t cku_xid; /* current XID */ 3600Sstevel@tonic-gate clock_t cku_ctime; /* time stamp of when */ 3610Sstevel@tonic-gate /* connection was created */ 3620Sstevel@tonic-gate uint_t cku_recv_attempts; 3630Sstevel@tonic-gate XDR cku_outxdr; /* xdr routine for output */ 3640Sstevel@tonic-gate XDR cku_inxdr; /* xdr routine for input */ 3650Sstevel@tonic-gate char cku_rpchdr[WIRE_HDR_SIZE + 4]; 3660Sstevel@tonic-gate /* pre-serialized rpc header */ 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate uint_t cku_outbuflen; /* default output mblk length */ 3690Sstevel@tonic-gate struct cred *cku_cred; /* credentials */ 3700Sstevel@tonic-gate bool_t cku_nodelayonerr; 3710Sstevel@tonic-gate /* for CLSET_NODELAYONERR */ 3720Sstevel@tonic-gate int cku_useresvport; /* Use reserved port */ 3730Sstevel@tonic-gate struct rpc_cots_client *cku_stats; /* stats for zone */ 3740Sstevel@tonic-gate } cku_private_t; 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate static struct cm_xprt *connmgr_wrapconnect(struct cm_xprt *, 3770Sstevel@tonic-gate const struct timeval *, struct netbuf *, int, struct netbuf *, 3788778SErik.Nordmark@Sun.COM struct rpc_err *, bool_t, bool_t, cred_t *); 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate static bool_t connmgr_connect(struct cm_xprt *, queue_t *, struct netbuf *, 3810Sstevel@tonic-gate int, calllist_t *, int *, bool_t reconnect, 3828778SErik.Nordmark@Sun.COM const struct timeval *, bool_t, cred_t *); 3838778SErik.Nordmark@Sun.COM 384*10004Sdai.ngo@sun.com static void *connmgr_opt_getoff(mblk_t *mp, t_uscalar_t offset, 385*10004Sdai.ngo@sun.com t_uscalar_t length, uint_t align_size); 386*10004Sdai.ngo@sun.com static bool_t connmgr_setbufsz(calllist_t *e, queue_t *wq, cred_t *cr); 387*10004Sdai.ngo@sun.com static bool_t connmgr_getopt_int(queue_t *wq, int level, int name, int *val, 388*10004Sdai.ngo@sun.com calllist_t *e, cred_t *cr); 389*10004Sdai.ngo@sun.com static bool_t connmgr_setopt_int(queue_t *wq, int level, int name, int val, 390*10004Sdai.ngo@sun.com calllist_t *e, cred_t *cr); 3918778SErik.Nordmark@Sun.COM static bool_t connmgr_setopt(queue_t *, int, int, calllist_t *, cred_t *cr); 3920Sstevel@tonic-gate static void connmgr_sndrel(struct cm_xprt *); 3930Sstevel@tonic-gate static void connmgr_snddis(struct cm_xprt *); 3940Sstevel@tonic-gate static void connmgr_close(struct cm_xprt *); 3950Sstevel@tonic-gate static void connmgr_release(struct cm_xprt *); 3960Sstevel@tonic-gate static struct cm_xprt *connmgr_wrapget(struct netbuf *, const struct timeval *, 3970Sstevel@tonic-gate cku_private_t *); 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate static struct cm_xprt *connmgr_get(struct netbuf *, const struct timeval *, 4000Sstevel@tonic-gate struct netbuf *, int, struct netbuf *, struct rpc_err *, dev_t, 4018778SErik.Nordmark@Sun.COM bool_t, int, cred_t *); 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate static void connmgr_cancelconn(struct cm_xprt *); 4040Sstevel@tonic-gate static enum clnt_stat connmgr_cwait(struct cm_xprt *, const struct timeval *, 4050Sstevel@tonic-gate bool_t); 4060Sstevel@tonic-gate static void connmgr_dis_and_wait(struct cm_xprt *); 4070Sstevel@tonic-gate 4088205SSiddheshwar.Mahesh@Sun.COM static int clnt_dispatch_send(queue_t *, mblk_t *, calllist_t *, uint_t, 4090Sstevel@tonic-gate uint_t); 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate static int clnt_delay(clock_t, bool_t); 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate static int waitforack(calllist_t *, t_scalar_t, const struct timeval *, bool_t); 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate /* 4160Sstevel@tonic-gate * Operations vector for TCP/IP based RPC 4170Sstevel@tonic-gate */ 4180Sstevel@tonic-gate static struct clnt_ops tcp_ops = { 4190Sstevel@tonic-gate clnt_cots_kcallit, /* do rpc call */ 4200Sstevel@tonic-gate clnt_cots_kabort, /* abort call */ 4210Sstevel@tonic-gate clnt_cots_kerror, /* return error status */ 4220Sstevel@tonic-gate clnt_cots_kfreeres, /* free results */ 4230Sstevel@tonic-gate clnt_cots_kdestroy, /* destroy rpc handle */ 4240Sstevel@tonic-gate clnt_cots_kcontrol, /* the ioctl() of rpc */ 4250Sstevel@tonic-gate clnt_cots_ksettimers, /* set retry timers */ 4260Sstevel@tonic-gate }; 4270Sstevel@tonic-gate 4280Sstevel@tonic-gate static int rpc_kstat_instance = 0; /* keeps the current instance */ 4290Sstevel@tonic-gate /* number for the next kstat_create */ 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate static struct cm_xprt *cm_hd = NULL; 4320Sstevel@tonic-gate static kmutex_t connmgr_lock; /* for connection mngr's list of transports */ 4330Sstevel@tonic-gate 4340Sstevel@tonic-gate extern kmutex_t clnt_max_msg_lock; 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate static calllist_t *clnt_pending = NULL; 4370Sstevel@tonic-gate extern kmutex_t clnt_pending_lock; 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate static int clnt_cots_hash_size = DEFAULT_HASH_SIZE; 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate static call_table_t *cots_call_ht; 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate static const struct rpc_cots_client { 4440Sstevel@tonic-gate kstat_named_t rccalls; 4450Sstevel@tonic-gate kstat_named_t rcbadcalls; 4460Sstevel@tonic-gate kstat_named_t rcbadxids; 4470Sstevel@tonic-gate kstat_named_t rctimeouts; 4480Sstevel@tonic-gate kstat_named_t rcnewcreds; 4490Sstevel@tonic-gate kstat_named_t rcbadverfs; 4500Sstevel@tonic-gate kstat_named_t rctimers; 4510Sstevel@tonic-gate kstat_named_t rccantconn; 4520Sstevel@tonic-gate kstat_named_t rcnomem; 4530Sstevel@tonic-gate kstat_named_t rcintrs; 4540Sstevel@tonic-gate } cots_rcstat_tmpl = { 4550Sstevel@tonic-gate { "calls", KSTAT_DATA_UINT64 }, 4560Sstevel@tonic-gate { "badcalls", KSTAT_DATA_UINT64 }, 4570Sstevel@tonic-gate { "badxids", KSTAT_DATA_UINT64 }, 4580Sstevel@tonic-gate { "timeouts", KSTAT_DATA_UINT64 }, 4590Sstevel@tonic-gate { "newcreds", KSTAT_DATA_UINT64 }, 4600Sstevel@tonic-gate { "badverfs", KSTAT_DATA_UINT64 }, 4610Sstevel@tonic-gate { "timers", KSTAT_DATA_UINT64 }, 4620Sstevel@tonic-gate { "cantconn", KSTAT_DATA_UINT64 }, 4630Sstevel@tonic-gate { "nomem", KSTAT_DATA_UINT64 }, 4640Sstevel@tonic-gate { "interrupts", KSTAT_DATA_UINT64 } 4650Sstevel@tonic-gate }; 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate #define COTSRCSTAT_INCR(p, x) \ 4680Sstevel@tonic-gate atomic_add_64(&(p)->x.value.ui64, 1) 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate #define CLNT_MAX_CONNS 1 /* concurrent connections between clnt/srvr */ 4719806Sdai.ngo@sun.com int clnt_max_conns = CLNT_MAX_CONNS; 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate #define CLNT_MIN_TIMEOUT 10 /* seconds to wait after we get a */ 4740Sstevel@tonic-gate /* connection reset */ 4750Sstevel@tonic-gate #define CLNT_MIN_CONNTIMEOUT 5 /* seconds to wait for a connection */ 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate 4789806Sdai.ngo@sun.com int clnt_cots_min_tout = CLNT_MIN_TIMEOUT; 4799806Sdai.ngo@sun.com int clnt_cots_min_conntout = CLNT_MIN_CONNTIMEOUT; 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate /* 4820Sstevel@tonic-gate * Limit the number of times we will attempt to receive a reply without 4830Sstevel@tonic-gate * re-sending a response. 4840Sstevel@tonic-gate */ 4850Sstevel@tonic-gate #define CLNT_MAXRECV_WITHOUT_RETRY 3 4869806Sdai.ngo@sun.com uint_t clnt_cots_maxrecv = CLNT_MAXRECV_WITHOUT_RETRY; 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate uint_t *clnt_max_msg_sizep; 4890Sstevel@tonic-gate void (*clnt_stop_idle)(queue_t *wq); 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate #define ptoh(p) (&((p)->cku_client)) 4920Sstevel@tonic-gate #define htop(h) ((cku_private_t *)((h)->cl_private)) 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate /* 4950Sstevel@tonic-gate * Times to retry 4960Sstevel@tonic-gate */ 4970Sstevel@tonic-gate #define REFRESHES 2 /* authentication refreshes */ 4980Sstevel@tonic-gate 499681Srg137905 /* 500681Srg137905 * The following is used to determine the global default behavior for 501681Srg137905 * COTS when binding to a local port. 502681Srg137905 * 503681Srg137905 * If the value is set to 1 the default will be to select a reserved 504681Srg137905 * (aka privileged) port, if the value is zero the default will be to 505681Srg137905 * use non-reserved ports. Users of kRPC may override this by using 506681Srg137905 * CLNT_CONTROL() and CLSET_BINDRESVPORT. 507681Srg137905 */ 5089806Sdai.ngo@sun.com int clnt_cots_do_bindresvport = 1; 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate static zone_key_t zone_cots_key; 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate /* 513*10004Sdai.ngo@sun.com * Defaults TCP send and receive buffer size for RPC connections. 514*10004Sdai.ngo@sun.com * These values can be tuned by /etc/system. 515*10004Sdai.ngo@sun.com */ 516*10004Sdai.ngo@sun.com int rpc_send_bufsz = 1024*1024; 517*10004Sdai.ngo@sun.com int rpc_recv_bufsz = 1024*1024; 518*10004Sdai.ngo@sun.com /* 519*10004Sdai.ngo@sun.com * To use system-wide default for TCP send and receive buffer size, 520*10004Sdai.ngo@sun.com * use /etc/system to set rpc_default_tcp_bufsz to 1: 521*10004Sdai.ngo@sun.com * 522*10004Sdai.ngo@sun.com * set rpcmod:rpc_default_tcp_bufsz=1 523*10004Sdai.ngo@sun.com */ 524*10004Sdai.ngo@sun.com int rpc_default_tcp_bufsz = 0; 525*10004Sdai.ngo@sun.com 526*10004Sdai.ngo@sun.com /* 5270Sstevel@tonic-gate * We need to do this after all kernel threads in the zone have exited. 5280Sstevel@tonic-gate */ 5290Sstevel@tonic-gate /* ARGSUSED */ 5300Sstevel@tonic-gate static void 5310Sstevel@tonic-gate clnt_zone_destroy(zoneid_t zoneid, void *unused) 5320Sstevel@tonic-gate { 5330Sstevel@tonic-gate struct cm_xprt **cmp; 5340Sstevel@tonic-gate struct cm_xprt *cm_entry; 5350Sstevel@tonic-gate struct cm_xprt *freelist = NULL; 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate mutex_enter(&connmgr_lock); 5380Sstevel@tonic-gate cmp = &cm_hd; 5390Sstevel@tonic-gate while ((cm_entry = *cmp) != NULL) { 5400Sstevel@tonic-gate if (cm_entry->x_zoneid == zoneid) { 5410Sstevel@tonic-gate *cmp = cm_entry->x_next; 5420Sstevel@tonic-gate cm_entry->x_next = freelist; 5430Sstevel@tonic-gate freelist = cm_entry; 5440Sstevel@tonic-gate } else { 5450Sstevel@tonic-gate cmp = &cm_entry->x_next; 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate mutex_exit(&connmgr_lock); 5490Sstevel@tonic-gate while ((cm_entry = freelist) != NULL) { 5500Sstevel@tonic-gate freelist = cm_entry->x_next; 5510Sstevel@tonic-gate connmgr_close(cm_entry); 5520Sstevel@tonic-gate } 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate int 5560Sstevel@tonic-gate clnt_cots_kcreate(dev_t dev, struct netbuf *addr, int family, rpcprog_t prog, 5570Sstevel@tonic-gate rpcvers_t vers, uint_t max_msgsize, cred_t *cred, CLIENT **ncl) 5580Sstevel@tonic-gate { 5590Sstevel@tonic-gate CLIENT *h; 5600Sstevel@tonic-gate cku_private_t *p; 5610Sstevel@tonic-gate struct rpc_msg call_msg; 5620Sstevel@tonic-gate struct rpcstat *rpcstat; 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate RPCLOG(8, "clnt_cots_kcreate: prog %u\n", prog); 5650Sstevel@tonic-gate 566766Scarlsonj rpcstat = zone_getspecific(rpcstat_zone_key, rpc_zone()); 5670Sstevel@tonic-gate ASSERT(rpcstat != NULL); 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate /* Allocate and intialize the client handle. */ 5700Sstevel@tonic-gate p = kmem_zalloc(sizeof (*p), KM_SLEEP); 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate h = ptoh(p); 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate h->cl_private = (caddr_t)p; 5750Sstevel@tonic-gate h->cl_auth = authkern_create(); 5760Sstevel@tonic-gate h->cl_ops = &tcp_ops; 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate cv_init(&p->cku_call.call_cv, NULL, CV_DEFAULT, NULL); 5790Sstevel@tonic-gate mutex_init(&p->cku_call.call_lock, NULL, MUTEX_DEFAULT, NULL); 5800Sstevel@tonic-gate 5810Sstevel@tonic-gate /* 5820Sstevel@tonic-gate * If the current sanity check size in rpcmod is smaller 5830Sstevel@tonic-gate * than the size needed, then increase the sanity check. 5840Sstevel@tonic-gate */ 5850Sstevel@tonic-gate if (max_msgsize != 0 && clnt_max_msg_sizep != NULL && 5860Sstevel@tonic-gate max_msgsize > *clnt_max_msg_sizep) { 5870Sstevel@tonic-gate mutex_enter(&clnt_max_msg_lock); 5880Sstevel@tonic-gate if (max_msgsize > *clnt_max_msg_sizep) 5890Sstevel@tonic-gate *clnt_max_msg_sizep = max_msgsize; 5900Sstevel@tonic-gate mutex_exit(&clnt_max_msg_lock); 5910Sstevel@tonic-gate } 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate p->cku_outbuflen = COTS_DEFAULT_ALLOCSIZE; 5940Sstevel@tonic-gate 5950Sstevel@tonic-gate /* Preserialize the call message header */ 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate call_msg.rm_xid = 0; 5980Sstevel@tonic-gate call_msg.rm_direction = CALL; 5990Sstevel@tonic-gate call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 6000Sstevel@tonic-gate call_msg.rm_call.cb_prog = prog; 6010Sstevel@tonic-gate call_msg.rm_call.cb_vers = vers; 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate xdrmem_create(&p->cku_outxdr, p->cku_rpchdr, WIRE_HDR_SIZE, XDR_ENCODE); 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate if (!xdr_callhdr(&p->cku_outxdr, &call_msg)) { 6060Sstevel@tonic-gate RPCLOG0(1, "clnt_cots_kcreate - Fatal header serialization " 6070Sstevel@tonic-gate "error\n"); 6080Sstevel@tonic-gate auth_destroy(h->cl_auth); 6090Sstevel@tonic-gate kmem_free(p, sizeof (cku_private_t)); 6100Sstevel@tonic-gate RPCLOG0(1, "clnt_cots_kcreate: create failed error EINVAL\n"); 6110Sstevel@tonic-gate return (EINVAL); /* XXX */ 6120Sstevel@tonic-gate } 6130Sstevel@tonic-gate 6140Sstevel@tonic-gate /* 6150Sstevel@tonic-gate * The zalloc initialized the fields below. 6160Sstevel@tonic-gate * p->cku_xid = 0; 6170Sstevel@tonic-gate * p->cku_flags = 0; 6180Sstevel@tonic-gate * p->cku_srcaddr.len = 0; 6190Sstevel@tonic-gate * p->cku_srcaddr.maxlen = 0; 6200Sstevel@tonic-gate */ 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate p->cku_cred = cred; 6230Sstevel@tonic-gate p->cku_device = dev; 6240Sstevel@tonic-gate p->cku_addrfmly = family; 6250Sstevel@tonic-gate p->cku_addr.buf = kmem_zalloc(addr->maxlen, KM_SLEEP); 6260Sstevel@tonic-gate p->cku_addr.maxlen = addr->maxlen; 6270Sstevel@tonic-gate p->cku_addr.len = addr->len; 6280Sstevel@tonic-gate bcopy(addr->buf, p->cku_addr.buf, addr->len); 6290Sstevel@tonic-gate p->cku_stats = rpcstat->rpc_cots_client; 6300Sstevel@tonic-gate p->cku_useresvport = -1; /* value is has not been set */ 6310Sstevel@tonic-gate 6320Sstevel@tonic-gate *ncl = h; 6330Sstevel@tonic-gate return (0); 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate 6360Sstevel@tonic-gate /*ARGSUSED*/ 6370Sstevel@tonic-gate static void 6380Sstevel@tonic-gate clnt_cots_kabort(CLIENT *h) 6390Sstevel@tonic-gate { 6400Sstevel@tonic-gate } 6410Sstevel@tonic-gate 6420Sstevel@tonic-gate /* 6430Sstevel@tonic-gate * Return error info on this handle. 6440Sstevel@tonic-gate */ 6450Sstevel@tonic-gate static void 6460Sstevel@tonic-gate clnt_cots_kerror(CLIENT *h, struct rpc_err *err) 6470Sstevel@tonic-gate { 6480Sstevel@tonic-gate /* LINTED pointer alignment */ 6490Sstevel@tonic-gate cku_private_t *p = htop(h); 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate *err = p->cku_err; 6520Sstevel@tonic-gate } 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate static bool_t 6550Sstevel@tonic-gate clnt_cots_kfreeres(CLIENT *h, xdrproc_t xdr_res, caddr_t res_ptr) 6560Sstevel@tonic-gate { 6570Sstevel@tonic-gate /* LINTED pointer alignment */ 6580Sstevel@tonic-gate cku_private_t *p = htop(h); 6590Sstevel@tonic-gate XDR *xdrs; 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate xdrs = &(p->cku_outxdr); 6620Sstevel@tonic-gate xdrs->x_op = XDR_FREE; 6630Sstevel@tonic-gate return ((*xdr_res)(xdrs, res_ptr)); 6640Sstevel@tonic-gate } 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate static bool_t 6670Sstevel@tonic-gate clnt_cots_kcontrol(CLIENT *h, int cmd, char *arg) 6680Sstevel@tonic-gate { 6690Sstevel@tonic-gate cku_private_t *p = htop(h); 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate switch (cmd) { 6720Sstevel@tonic-gate case CLSET_PROGRESS: 6730Sstevel@tonic-gate p->cku_progress = TRUE; 6740Sstevel@tonic-gate return (TRUE); 6750Sstevel@tonic-gate 6760Sstevel@tonic-gate case CLSET_XID: 6770Sstevel@tonic-gate if (arg == NULL) 6780Sstevel@tonic-gate return (FALSE); 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate p->cku_xid = *((uint32_t *)arg); 6810Sstevel@tonic-gate return (TRUE); 6820Sstevel@tonic-gate 6830Sstevel@tonic-gate case CLGET_XID: 6840Sstevel@tonic-gate if (arg == NULL) 6850Sstevel@tonic-gate return (FALSE); 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate *((uint32_t *)arg) = p->cku_xid; 6880Sstevel@tonic-gate return (TRUE); 6890Sstevel@tonic-gate 6900Sstevel@tonic-gate case CLSET_NODELAYONERR: 6910Sstevel@tonic-gate if (arg == NULL) 6920Sstevel@tonic-gate return (FALSE); 6930Sstevel@tonic-gate 6940Sstevel@tonic-gate if (*((bool_t *)arg) == TRUE) { 6950Sstevel@tonic-gate p->cku_nodelayonerr = TRUE; 6960Sstevel@tonic-gate return (TRUE); 6970Sstevel@tonic-gate } 6980Sstevel@tonic-gate if (*((bool_t *)arg) == FALSE) { 6990Sstevel@tonic-gate p->cku_nodelayonerr = FALSE; 7000Sstevel@tonic-gate return (TRUE); 7010Sstevel@tonic-gate } 7020Sstevel@tonic-gate return (FALSE); 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate case CLGET_NODELAYONERR: 7050Sstevel@tonic-gate if (arg == NULL) 7060Sstevel@tonic-gate return (FALSE); 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate *((bool_t *)arg) = p->cku_nodelayonerr; 7090Sstevel@tonic-gate return (TRUE); 7100Sstevel@tonic-gate 7110Sstevel@tonic-gate case CLSET_BINDRESVPORT: 7120Sstevel@tonic-gate if (arg == NULL) 7130Sstevel@tonic-gate return (FALSE); 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate if (*(int *)arg != 1 && *(int *)arg != 0) 7160Sstevel@tonic-gate return (FALSE); 7170Sstevel@tonic-gate 7180Sstevel@tonic-gate p->cku_useresvport = *(int *)arg; 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate return (TRUE); 7210Sstevel@tonic-gate 7220Sstevel@tonic-gate case CLGET_BINDRESVPORT: 7230Sstevel@tonic-gate if (arg == NULL) 7240Sstevel@tonic-gate return (FALSE); 7250Sstevel@tonic-gate 7260Sstevel@tonic-gate *(int *)arg = p->cku_useresvport; 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate return (TRUE); 7290Sstevel@tonic-gate 7300Sstevel@tonic-gate default: 7310Sstevel@tonic-gate return (FALSE); 7320Sstevel@tonic-gate } 7330Sstevel@tonic-gate } 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate /* 7360Sstevel@tonic-gate * Destroy rpc handle. Frees the space used for output buffer, 7370Sstevel@tonic-gate * private data, and handle structure. 7380Sstevel@tonic-gate */ 7390Sstevel@tonic-gate static void 7400Sstevel@tonic-gate clnt_cots_kdestroy(CLIENT *h) 7410Sstevel@tonic-gate { 7420Sstevel@tonic-gate /* LINTED pointer alignment */ 7430Sstevel@tonic-gate cku_private_t *p = htop(h); 7440Sstevel@tonic-gate calllist_t *call = &p->cku_call; 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate RPCLOG(8, "clnt_cots_kdestroy h: %p\n", (void *)h); 7470Sstevel@tonic-gate RPCLOG(8, "clnt_cots_kdestroy h: xid=0x%x\n", p->cku_xid); 7480Sstevel@tonic-gate 7490Sstevel@tonic-gate if (p->cku_flags & CKU_ONQUEUE) { 7500Sstevel@tonic-gate RPCLOG(64, "clnt_cots_kdestroy h: removing call for xid 0x%x " 7510Sstevel@tonic-gate "from dispatch list\n", p->cku_xid); 7520Sstevel@tonic-gate call_table_remove(call); 7530Sstevel@tonic-gate } 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate if (call->call_reply) 7560Sstevel@tonic-gate freemsg(call->call_reply); 7570Sstevel@tonic-gate cv_destroy(&call->call_cv); 7580Sstevel@tonic-gate mutex_destroy(&call->call_lock); 7590Sstevel@tonic-gate 7600Sstevel@tonic-gate kmem_free(p->cku_srcaddr.buf, p->cku_srcaddr.maxlen); 7610Sstevel@tonic-gate kmem_free(p->cku_addr.buf, p->cku_addr.maxlen); 7620Sstevel@tonic-gate kmem_free(p, sizeof (*p)); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate 7650Sstevel@tonic-gate static int clnt_cots_pulls; 7660Sstevel@tonic-gate #define RM_HDR_SIZE 4 /* record mark header size */ 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate /* 7690Sstevel@tonic-gate * Call remote procedure. 7700Sstevel@tonic-gate */ 7710Sstevel@tonic-gate static enum clnt_stat 7720Sstevel@tonic-gate clnt_cots_kcallit(CLIENT *h, rpcproc_t procnum, xdrproc_t xdr_args, 7730Sstevel@tonic-gate caddr_t argsp, xdrproc_t xdr_results, caddr_t resultsp, struct timeval wait) 7740Sstevel@tonic-gate { 7750Sstevel@tonic-gate /* LINTED pointer alignment */ 7760Sstevel@tonic-gate cku_private_t *p = htop(h); 7770Sstevel@tonic-gate calllist_t *call = &p->cku_call; 7780Sstevel@tonic-gate XDR *xdrs; 7790Sstevel@tonic-gate struct rpc_msg reply_msg; 7800Sstevel@tonic-gate mblk_t *mp; 7810Sstevel@tonic-gate #ifdef RPCDEBUG 7820Sstevel@tonic-gate clock_t time_sent; 7830Sstevel@tonic-gate #endif 7840Sstevel@tonic-gate struct netbuf *retryaddr; 7850Sstevel@tonic-gate struct cm_xprt *cm_entry = NULL; 7860Sstevel@tonic-gate queue_t *wq; 7879675Sdai.ngo@sun.com int len, waitsecs, max_waitsecs; 7880Sstevel@tonic-gate int mpsize; 7890Sstevel@tonic-gate int refreshes = REFRESHES; 7900Sstevel@tonic-gate int interrupted; 7910Sstevel@tonic-gate int tidu_size; 7920Sstevel@tonic-gate enum clnt_stat status; 7930Sstevel@tonic-gate struct timeval cwait; 7940Sstevel@tonic-gate bool_t delay_first = FALSE; 7950Sstevel@tonic-gate clock_t ticks; 7960Sstevel@tonic-gate 7970Sstevel@tonic-gate RPCLOG(2, "clnt_cots_kcallit, procnum %u\n", procnum); 7980Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rccalls); 7990Sstevel@tonic-gate 8000Sstevel@tonic-gate RPCLOG(2, "clnt_cots_kcallit: wait.tv_sec: %ld\n", wait.tv_sec); 8010Sstevel@tonic-gate RPCLOG(2, "clnt_cots_kcallit: wait.tv_usec: %ld\n", wait.tv_usec); 8020Sstevel@tonic-gate /* 8030Sstevel@tonic-gate * Bug ID 1240234: 8040Sstevel@tonic-gate * Look out for zero length timeouts. We don't want to 8050Sstevel@tonic-gate * wait zero seconds for a connection to be established. 8060Sstevel@tonic-gate */ 8070Sstevel@tonic-gate if (wait.tv_sec < clnt_cots_min_conntout) { 8080Sstevel@tonic-gate cwait.tv_sec = clnt_cots_min_conntout; 8090Sstevel@tonic-gate cwait.tv_usec = 0; 8100Sstevel@tonic-gate RPCLOG(8, "clnt_cots_kcallit: wait.tv_sec (%ld) too low,", 8110Sstevel@tonic-gate wait.tv_sec); 8120Sstevel@tonic-gate RPCLOG(8, " setting to: %d\n", clnt_cots_min_conntout); 8130Sstevel@tonic-gate } else { 8140Sstevel@tonic-gate cwait = wait; 8150Sstevel@tonic-gate } 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate call_again: 8180Sstevel@tonic-gate if (cm_entry) { 8190Sstevel@tonic-gate connmgr_release(cm_entry); 8200Sstevel@tonic-gate cm_entry = NULL; 8210Sstevel@tonic-gate } 8220Sstevel@tonic-gate 8230Sstevel@tonic-gate mp = NULL; 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate /* 8260Sstevel@tonic-gate * If the call is not a retry, allocate a new xid and cache it 8270Sstevel@tonic-gate * for future retries. 8280Sstevel@tonic-gate * Bug ID 1246045: 8290Sstevel@tonic-gate * Treat call as a retry for purposes of binding the source 8300Sstevel@tonic-gate * port only if we actually attempted to send anything on 8310Sstevel@tonic-gate * the previous call. 8320Sstevel@tonic-gate */ 8330Sstevel@tonic-gate if (p->cku_xid == 0) { 8340Sstevel@tonic-gate p->cku_xid = alloc_xid(); 8356403Sgt29601 call->call_zoneid = rpc_zoneid(); 8366403Sgt29601 8370Sstevel@tonic-gate /* 8380Sstevel@tonic-gate * We need to ASSERT here that our xid != 0 because this 8390Sstevel@tonic-gate * determines whether or not our call record gets placed on 8400Sstevel@tonic-gate * the hash table or the linked list. By design, we mandate 8410Sstevel@tonic-gate * that RPC calls over cots must have xid's != 0, so we can 8420Sstevel@tonic-gate * ensure proper management of the hash table. 8430Sstevel@tonic-gate */ 8440Sstevel@tonic-gate ASSERT(p->cku_xid != 0); 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate retryaddr = NULL; 8470Sstevel@tonic-gate p->cku_flags &= ~CKU_SENT; 8480Sstevel@tonic-gate 8490Sstevel@tonic-gate if (p->cku_flags & CKU_ONQUEUE) { 8500Sstevel@tonic-gate RPCLOG(8, "clnt_cots_kcallit: new call, dequeuing old" 8510Sstevel@tonic-gate " one (%p)\n", (void *)call); 8520Sstevel@tonic-gate call_table_remove(call); 8530Sstevel@tonic-gate p->cku_flags &= ~CKU_ONQUEUE; 8540Sstevel@tonic-gate RPCLOG(64, "clnt_cots_kcallit: removing call from " 8550Sstevel@tonic-gate "dispatch list because xid was zero (now 0x%x)\n", 8560Sstevel@tonic-gate p->cku_xid); 8570Sstevel@tonic-gate } 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate if (call->call_reply != NULL) { 8600Sstevel@tonic-gate freemsg(call->call_reply); 8610Sstevel@tonic-gate call->call_reply = NULL; 8620Sstevel@tonic-gate } 8630Sstevel@tonic-gate } else if (p->cku_srcaddr.buf == NULL || p->cku_srcaddr.len == 0) { 8640Sstevel@tonic-gate retryaddr = NULL; 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate } else if (p->cku_flags & CKU_SENT) { 8670Sstevel@tonic-gate retryaddr = &p->cku_srcaddr; 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate } else { 8700Sstevel@tonic-gate /* 8710Sstevel@tonic-gate * Bug ID 1246045: Nothing was sent, so set retryaddr to 8720Sstevel@tonic-gate * NULL and let connmgr_get() bind to any source port it 8730Sstevel@tonic-gate * can get. 8740Sstevel@tonic-gate */ 8750Sstevel@tonic-gate retryaddr = NULL; 8760Sstevel@tonic-gate } 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate RPCLOG(64, "clnt_cots_kcallit: xid = 0x%x", p->cku_xid); 8790Sstevel@tonic-gate RPCLOG(64, " flags = 0x%x\n", p->cku_flags); 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate p->cku_err.re_status = RPC_TIMEDOUT; 8820Sstevel@tonic-gate p->cku_err.re_errno = p->cku_err.re_terrno = 0; 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate cm_entry = connmgr_wrapget(retryaddr, &cwait, p); 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate if (cm_entry == NULL) { 8870Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcallit: can't connect status %s\n", 8880Sstevel@tonic-gate clnt_sperrno(p->cku_err.re_status)); 8890Sstevel@tonic-gate 8900Sstevel@tonic-gate /* 8910Sstevel@tonic-gate * The reasons why we fail to create a connection are 8920Sstevel@tonic-gate * varied. In most cases we don't want the caller to 8930Sstevel@tonic-gate * immediately retry. This could have one or more 8940Sstevel@tonic-gate * bad effects. This includes flooding the net with 8950Sstevel@tonic-gate * connect requests to ports with no listener; a hard 8960Sstevel@tonic-gate * kernel loop due to all the "reserved" TCP ports being 8970Sstevel@tonic-gate * in use. 8980Sstevel@tonic-gate */ 8990Sstevel@tonic-gate delay_first = TRUE; 9000Sstevel@tonic-gate 9010Sstevel@tonic-gate /* 9020Sstevel@tonic-gate * Even if we end up returning EINTR, we still count a 9030Sstevel@tonic-gate * a "can't connect", because the connection manager 9040Sstevel@tonic-gate * might have been committed to waiting for or timing out on 9050Sstevel@tonic-gate * a connection. 9060Sstevel@tonic-gate */ 9070Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rccantconn); 9080Sstevel@tonic-gate switch (p->cku_err.re_status) { 9090Sstevel@tonic-gate case RPC_INTR: 9100Sstevel@tonic-gate p->cku_err.re_errno = EINTR; 9110Sstevel@tonic-gate 9120Sstevel@tonic-gate /* 9130Sstevel@tonic-gate * No need to delay because a UNIX signal(2) 9140Sstevel@tonic-gate * interrupted us. The caller likely won't 9150Sstevel@tonic-gate * retry the CLNT_CALL() and even if it does, 9160Sstevel@tonic-gate * we assume the caller knows what it is doing. 9170Sstevel@tonic-gate */ 9180Sstevel@tonic-gate delay_first = FALSE; 9190Sstevel@tonic-gate break; 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate case RPC_TIMEDOUT: 9220Sstevel@tonic-gate p->cku_err.re_errno = ETIMEDOUT; 9230Sstevel@tonic-gate 9240Sstevel@tonic-gate /* 9250Sstevel@tonic-gate * No need to delay because timed out already 9260Sstevel@tonic-gate * on the connection request and assume that the 9270Sstevel@tonic-gate * transport time out is longer than our minimum 9280Sstevel@tonic-gate * timeout, or least not too much smaller. 9290Sstevel@tonic-gate */ 9300Sstevel@tonic-gate delay_first = FALSE; 9310Sstevel@tonic-gate break; 9320Sstevel@tonic-gate 9330Sstevel@tonic-gate case RPC_SYSTEMERROR: 9340Sstevel@tonic-gate case RPC_TLIERROR: 9350Sstevel@tonic-gate /* 9360Sstevel@tonic-gate * We want to delay here because a transient 9370Sstevel@tonic-gate * system error has a better chance of going away 9380Sstevel@tonic-gate * if we delay a bit. If it's not transient, then 9390Sstevel@tonic-gate * we don't want end up in a hard kernel loop 9400Sstevel@tonic-gate * due to retries. 9410Sstevel@tonic-gate */ 9420Sstevel@tonic-gate ASSERT(p->cku_err.re_errno != 0); 9430Sstevel@tonic-gate break; 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate 9460Sstevel@tonic-gate case RPC_CANTCONNECT: 9470Sstevel@tonic-gate /* 9480Sstevel@tonic-gate * RPC_CANTCONNECT is set on T_ERROR_ACK which 9490Sstevel@tonic-gate * implies some error down in the TCP layer or 9500Sstevel@tonic-gate * below. If cku_nodelayonerror is set then we 9510Sstevel@tonic-gate * assume the caller knows not to try too hard. 9520Sstevel@tonic-gate */ 9530Sstevel@tonic-gate RPCLOG0(8, "clnt_cots_kcallit: connection failed,"); 9540Sstevel@tonic-gate RPCLOG0(8, " re_status=RPC_CANTCONNECT,"); 9550Sstevel@tonic-gate RPCLOG(8, " re_errno=%d,", p->cku_err.re_errno); 9560Sstevel@tonic-gate RPCLOG(8, " cku_nodelayonerr=%d", p->cku_nodelayonerr); 9570Sstevel@tonic-gate if (p->cku_nodelayonerr == TRUE) 9580Sstevel@tonic-gate delay_first = FALSE; 9590Sstevel@tonic-gate 9600Sstevel@tonic-gate p->cku_err.re_errno = EIO; 9610Sstevel@tonic-gate 9620Sstevel@tonic-gate break; 9630Sstevel@tonic-gate 9640Sstevel@tonic-gate case RPC_XPRTFAILED: 9650Sstevel@tonic-gate /* 9660Sstevel@tonic-gate * We want to delay here because we likely 9670Sstevel@tonic-gate * got a refused connection. 9680Sstevel@tonic-gate */ 9694457Svv149972 if (p->cku_err.re_errno == 0) 9704457Svv149972 p->cku_err.re_errno = EIO; 9714457Svv149972 9724457Svv149972 RPCLOG(1, "clnt_cots_kcallit: transport failed: %d\n", 9734457Svv149972 p->cku_err.re_errno); 9744457Svv149972 9754457Svv149972 break; 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate default: 9780Sstevel@tonic-gate /* 9790Sstevel@tonic-gate * We delay here because it is better to err 9800Sstevel@tonic-gate * on the side of caution. If we got here then 9810Sstevel@tonic-gate * status could have been RPC_SUCCESS, but we 9820Sstevel@tonic-gate * know that we did not get a connection, so 9830Sstevel@tonic-gate * force the rpc status to RPC_CANTCONNECT. 9840Sstevel@tonic-gate */ 9850Sstevel@tonic-gate p->cku_err.re_status = RPC_CANTCONNECT; 9860Sstevel@tonic-gate p->cku_err.re_errno = EIO; 9870Sstevel@tonic-gate break; 9880Sstevel@tonic-gate } 9890Sstevel@tonic-gate if (delay_first == TRUE) 9900Sstevel@tonic-gate ticks = clnt_cots_min_tout * drv_usectohz(1000000); 9910Sstevel@tonic-gate goto cots_done; 9920Sstevel@tonic-gate } 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate /* 9950Sstevel@tonic-gate * If we've never sent any request on this connection (send count 9960Sstevel@tonic-gate * is zero, or the connection has been reset), cache the 9970Sstevel@tonic-gate * the connection's create time and send a request (possibly a retry) 9980Sstevel@tonic-gate */ 9990Sstevel@tonic-gate if ((p->cku_flags & CKU_SENT) == 0 || 10000Sstevel@tonic-gate p->cku_ctime != cm_entry->x_ctime) { 10010Sstevel@tonic-gate p->cku_ctime = cm_entry->x_ctime; 10020Sstevel@tonic-gate 10030Sstevel@tonic-gate } else if ((p->cku_flags & CKU_SENT) && (p->cku_flags & CKU_ONQUEUE) && 10040Sstevel@tonic-gate (call->call_reply != NULL || 10050Sstevel@tonic-gate p->cku_recv_attempts < clnt_cots_maxrecv)) { 10060Sstevel@tonic-gate 10070Sstevel@tonic-gate /* 10080Sstevel@tonic-gate * If we've sent a request and our call is on the dispatch 10090Sstevel@tonic-gate * queue and we haven't made too many receive attempts, then 10100Sstevel@tonic-gate * don't re-send, just receive. 10110Sstevel@tonic-gate */ 10120Sstevel@tonic-gate p->cku_recv_attempts++; 10130Sstevel@tonic-gate goto read_again; 10140Sstevel@tonic-gate } 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate /* 10170Sstevel@tonic-gate * Now we create the RPC request in a STREAMS message. We have to do 10180Sstevel@tonic-gate * this after the call to connmgr_get so that we have the correct 10190Sstevel@tonic-gate * TIDU size for the transport. 10200Sstevel@tonic-gate */ 10210Sstevel@tonic-gate tidu_size = cm_entry->x_tidu_size; 10220Sstevel@tonic-gate len = MSG_OFFSET + MAX(tidu_size, RM_HDR_SIZE + WIRE_HDR_SIZE); 10230Sstevel@tonic-gate 10240Sstevel@tonic-gate while ((mp = allocb(len, BPRI_MED)) == NULL) { 10250Sstevel@tonic-gate if (strwaitbuf(len, BPRI_MED)) { 10260Sstevel@tonic-gate p->cku_err.re_status = RPC_SYSTEMERROR; 10270Sstevel@tonic-gate p->cku_err.re_errno = ENOSR; 10280Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rcnomem); 10290Sstevel@tonic-gate goto cots_done; 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate } 10320Sstevel@tonic-gate xdrs = &p->cku_outxdr; 10330Sstevel@tonic-gate xdrmblk_init(xdrs, mp, XDR_ENCODE, tidu_size); 10340Sstevel@tonic-gate mpsize = MBLKSIZE(mp); 10350Sstevel@tonic-gate ASSERT(mpsize >= len); 10360Sstevel@tonic-gate ASSERT(mp->b_rptr == mp->b_datap->db_base); 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate /* 10390Sstevel@tonic-gate * If the size of mblk is not appreciably larger than what we 10400Sstevel@tonic-gate * asked, then resize the mblk to exactly len bytes. The reason for 10410Sstevel@tonic-gate * this: suppose len is 1600 bytes, the tidu is 1460 bytes 10420Sstevel@tonic-gate * (from TCP over ethernet), and the arguments to the RPC require 10430Sstevel@tonic-gate * 2800 bytes. Ideally we want the protocol to render two 10440Sstevel@tonic-gate * ~1400 byte segments over the wire. However if allocb() gives us a 2k 10450Sstevel@tonic-gate * mblk, and we allocate a second mblk for the remainder, the protocol 10460Sstevel@tonic-gate * module may generate 3 segments over the wire: 10470Sstevel@tonic-gate * 1460 bytes for the first, 448 (2048 - 1600) for the second, and 10480Sstevel@tonic-gate * 892 for the third. If we "waste" 448 bytes in the first mblk, 10490Sstevel@tonic-gate * the XDR encoding will generate two ~1400 byte mblks, and the 10500Sstevel@tonic-gate * protocol module is more likely to produce properly sized segments. 10510Sstevel@tonic-gate */ 10520Sstevel@tonic-gate if ((mpsize >> 1) <= len) 10530Sstevel@tonic-gate mp->b_rptr += (mpsize - len); 10540Sstevel@tonic-gate 10550Sstevel@tonic-gate /* 10560Sstevel@tonic-gate * Adjust b_rptr to reserve space for the non-data protocol headers 10570Sstevel@tonic-gate * any downstream modules might like to add, and for the 10580Sstevel@tonic-gate * record marking header. 10590Sstevel@tonic-gate */ 10600Sstevel@tonic-gate mp->b_rptr += (MSG_OFFSET + RM_HDR_SIZE); 10610Sstevel@tonic-gate 10620Sstevel@tonic-gate if (h->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) { 10630Sstevel@tonic-gate /* Copy in the preserialized RPC header information. */ 10640Sstevel@tonic-gate bcopy(p->cku_rpchdr, mp->b_rptr, WIRE_HDR_SIZE); 10650Sstevel@tonic-gate 10660Sstevel@tonic-gate /* Use XDR_SETPOS() to set the b_wptr to past the RPC header. */ 10670Sstevel@tonic-gate XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base + 10680Sstevel@tonic-gate WIRE_HDR_SIZE)); 10690Sstevel@tonic-gate 10700Sstevel@tonic-gate ASSERT((mp->b_wptr - mp->b_rptr) == WIRE_HDR_SIZE); 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate /* Serialize the procedure number and the arguments. */ 10730Sstevel@tonic-gate if ((!XDR_PUTINT32(xdrs, (int32_t *)&procnum)) || 10740Sstevel@tonic-gate (!AUTH_MARSHALL(h->cl_auth, xdrs, p->cku_cred)) || 10750Sstevel@tonic-gate (!(*xdr_args)(xdrs, argsp))) { 10760Sstevel@tonic-gate p->cku_err.re_status = RPC_CANTENCODEARGS; 10770Sstevel@tonic-gate p->cku_err.re_errno = EIO; 10780Sstevel@tonic-gate goto cots_done; 10790Sstevel@tonic-gate } 10800Sstevel@tonic-gate 10810Sstevel@tonic-gate (*(uint32_t *)(mp->b_rptr)) = p->cku_xid; 10820Sstevel@tonic-gate } else { 10830Sstevel@tonic-gate uint32_t *uproc = (uint32_t *)&p->cku_rpchdr[WIRE_HDR_SIZE]; 10840Sstevel@tonic-gate IXDR_PUT_U_INT32(uproc, procnum); 10850Sstevel@tonic-gate 10860Sstevel@tonic-gate (*(uint32_t *)(&p->cku_rpchdr[0])) = p->cku_xid; 10870Sstevel@tonic-gate 10880Sstevel@tonic-gate /* Use XDR_SETPOS() to set the b_wptr. */ 10890Sstevel@tonic-gate XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base)); 10900Sstevel@tonic-gate 10910Sstevel@tonic-gate /* Serialize the procedure number and the arguments. */ 10920Sstevel@tonic-gate if (!AUTH_WRAP(h->cl_auth, p->cku_rpchdr, WIRE_HDR_SIZE+4, 10930Sstevel@tonic-gate xdrs, xdr_args, argsp)) { 10940Sstevel@tonic-gate p->cku_err.re_status = RPC_CANTENCODEARGS; 10950Sstevel@tonic-gate p->cku_err.re_errno = EIO; 10960Sstevel@tonic-gate goto cots_done; 10970Sstevel@tonic-gate } 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate 11000Sstevel@tonic-gate RPCLOG(2, "clnt_cots_kcallit: connected, sending call, tidu_size %d\n", 11010Sstevel@tonic-gate tidu_size); 11020Sstevel@tonic-gate 11030Sstevel@tonic-gate wq = cm_entry->x_wq; 11049675Sdai.ngo@sun.com waitsecs = 0; 11059675Sdai.ngo@sun.com 11069675Sdai.ngo@sun.com dispatch_again: 11078205SSiddheshwar.Mahesh@Sun.COM status = clnt_dispatch_send(wq, mp, call, p->cku_xid, 11086403Sgt29601 (p->cku_flags & CKU_ONQUEUE)); 11090Sstevel@tonic-gate 11109675Sdai.ngo@sun.com if ((status == RPC_CANTSEND) && (call->call_reason == ENOBUFS)) { 11119675Sdai.ngo@sun.com /* 11129675Sdai.ngo@sun.com * QFULL condition, allow some time for queue to drain 11139675Sdai.ngo@sun.com * and try again. Give up after waiting for all timeout 11149675Sdai.ngo@sun.com * specified for the call, or zone is going away. 11159675Sdai.ngo@sun.com */ 11169675Sdai.ngo@sun.com max_waitsecs = wait.tv_sec ? wait.tv_sec : clnt_cots_min_tout; 11179675Sdai.ngo@sun.com if ((waitsecs++ < max_waitsecs) && 11189675Sdai.ngo@sun.com !(zone_status_get(curproc->p_zone) >= 11199675Sdai.ngo@sun.com ZONE_IS_SHUTTING_DOWN)) { 11209675Sdai.ngo@sun.com 11219675Sdai.ngo@sun.com /* wait 1 sec for queue to drain */ 11229675Sdai.ngo@sun.com if (clnt_delay(drv_usectohz(1000000), 11239675Sdai.ngo@sun.com h->cl_nosignal) == EINTR) { 11249675Sdai.ngo@sun.com p->cku_err.re_errno = EINTR; 11259675Sdai.ngo@sun.com p->cku_err.re_status = RPC_INTR; 11269675Sdai.ngo@sun.com 11279675Sdai.ngo@sun.com goto cots_done; 11289675Sdai.ngo@sun.com } 11299675Sdai.ngo@sun.com 11309675Sdai.ngo@sun.com /* and try again */ 11319675Sdai.ngo@sun.com goto dispatch_again; 11329675Sdai.ngo@sun.com } 11338205SSiddheshwar.Mahesh@Sun.COM p->cku_err.re_status = status; 11349675Sdai.ngo@sun.com p->cku_err.re_errno = call->call_reason; 11358205SSiddheshwar.Mahesh@Sun.COM DTRACE_PROBE(krpc__e__clntcots__kcallit__cantsend); 11368205SSiddheshwar.Mahesh@Sun.COM 11378205SSiddheshwar.Mahesh@Sun.COM goto cots_done; 11388205SSiddheshwar.Mahesh@Sun.COM } 11398205SSiddheshwar.Mahesh@Sun.COM 11409675Sdai.ngo@sun.com if (waitsecs) { 11419675Sdai.ngo@sun.com /* adjust timeout to account for time wait to send */ 11429675Sdai.ngo@sun.com wait.tv_sec -= waitsecs; 11439675Sdai.ngo@sun.com if (wait.tv_sec < 0) { 11449675Sdai.ngo@sun.com /* pick up reply on next retry */ 11459675Sdai.ngo@sun.com wait.tv_sec = 0; 11469675Sdai.ngo@sun.com } 11479675Sdai.ngo@sun.com DTRACE_PROBE2(clnt_cots__sendwait, CLIENT *, h, 11489675Sdai.ngo@sun.com int, waitsecs); 11499675Sdai.ngo@sun.com } 11509675Sdai.ngo@sun.com 11510Sstevel@tonic-gate RPCLOG(64, "clnt_cots_kcallit: sent call for xid 0x%x\n", 11526403Sgt29601 (uint_t)p->cku_xid); 11530Sstevel@tonic-gate p->cku_flags = (CKU_ONQUEUE|CKU_SENT); 11540Sstevel@tonic-gate p->cku_recv_attempts = 1; 11550Sstevel@tonic-gate 11560Sstevel@tonic-gate #ifdef RPCDEBUG 11570Sstevel@tonic-gate time_sent = lbolt; 11580Sstevel@tonic-gate #endif 11590Sstevel@tonic-gate 11600Sstevel@tonic-gate /* 11610Sstevel@tonic-gate * Wait for a reply or a timeout. If there is no error or timeout, 11620Sstevel@tonic-gate * (both indicated by call_status), call->call_reply will contain 11630Sstevel@tonic-gate * the RPC reply message. 11640Sstevel@tonic-gate */ 11650Sstevel@tonic-gate read_again: 11660Sstevel@tonic-gate mutex_enter(&call->call_lock); 11670Sstevel@tonic-gate interrupted = 0; 11680Sstevel@tonic-gate if (call->call_status == RPC_TIMEDOUT) { 11690Sstevel@tonic-gate /* 11700Sstevel@tonic-gate * Indicate that the lwp is not to be stopped while waiting 11710Sstevel@tonic-gate * for this network traffic. This is to avoid deadlock while 11720Sstevel@tonic-gate * debugging a process via /proc and also to avoid recursive 11730Sstevel@tonic-gate * mutex_enter()s due to NFS page faults while stopping 11740Sstevel@tonic-gate * (NFS holds locks when it calls here). 11750Sstevel@tonic-gate */ 11760Sstevel@tonic-gate clock_t cv_wait_ret; 11770Sstevel@tonic-gate clock_t timout; 11780Sstevel@tonic-gate clock_t oldlbolt; 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate klwp_t *lwp = ttolwp(curthread); 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate if (lwp != NULL) 11830Sstevel@tonic-gate lwp->lwp_nostop++; 11840Sstevel@tonic-gate 11850Sstevel@tonic-gate oldlbolt = lbolt; 11860Sstevel@tonic-gate timout = wait.tv_sec * drv_usectohz(1000000) + 11870Sstevel@tonic-gate drv_usectohz(wait.tv_usec) + oldlbolt; 11880Sstevel@tonic-gate /* 11890Sstevel@tonic-gate * Iterate until the call_status is changed to something 11900Sstevel@tonic-gate * other that RPC_TIMEDOUT, or if cv_timedwait_sig() returns 11910Sstevel@tonic-gate * something <=0 zero. The latter means that we timed 11920Sstevel@tonic-gate * out. 11930Sstevel@tonic-gate */ 11940Sstevel@tonic-gate if (h->cl_nosignal) 11950Sstevel@tonic-gate while ((cv_wait_ret = cv_timedwait(&call->call_cv, 11960Sstevel@tonic-gate &call->call_lock, timout)) > 0 && 11976403Sgt29601 call->call_status == RPC_TIMEDOUT) 11986403Sgt29601 ; 11990Sstevel@tonic-gate else 12000Sstevel@tonic-gate while ((cv_wait_ret = cv_timedwait_sig( 12010Sstevel@tonic-gate &call->call_cv, 12020Sstevel@tonic-gate &call->call_lock, timout)) > 0 && 12036403Sgt29601 call->call_status == RPC_TIMEDOUT) 12046403Sgt29601 ; 12050Sstevel@tonic-gate 12060Sstevel@tonic-gate switch (cv_wait_ret) { 12070Sstevel@tonic-gate case 0: 12080Sstevel@tonic-gate /* 12090Sstevel@tonic-gate * If we got out of the above loop with 12100Sstevel@tonic-gate * cv_timedwait_sig() returning 0, then we were 12110Sstevel@tonic-gate * interrupted regardless what call_status is. 12120Sstevel@tonic-gate */ 12130Sstevel@tonic-gate interrupted = 1; 12140Sstevel@tonic-gate break; 12150Sstevel@tonic-gate case -1: 12160Sstevel@tonic-gate /* cv_timedwait_sig() timed out */ 12170Sstevel@tonic-gate break; 12180Sstevel@tonic-gate default: 12190Sstevel@tonic-gate 12200Sstevel@tonic-gate /* 12210Sstevel@tonic-gate * We were cv_signaled(). If we didn't 12220Sstevel@tonic-gate * get a successful call_status and returned 12230Sstevel@tonic-gate * before time expired, delay up to clnt_cots_min_tout 12240Sstevel@tonic-gate * seconds so that the caller doesn't immediately 12250Sstevel@tonic-gate * try to call us again and thus force the 12260Sstevel@tonic-gate * same condition that got us here (such 12270Sstevel@tonic-gate * as a RPC_XPRTFAILED due to the server not 12280Sstevel@tonic-gate * listening on the end-point. 12290Sstevel@tonic-gate */ 12300Sstevel@tonic-gate if (call->call_status != RPC_SUCCESS) { 12310Sstevel@tonic-gate clock_t curlbolt; 12320Sstevel@tonic-gate clock_t diff; 12330Sstevel@tonic-gate 12340Sstevel@tonic-gate curlbolt = ddi_get_lbolt(); 12350Sstevel@tonic-gate ticks = clnt_cots_min_tout * 12360Sstevel@tonic-gate drv_usectohz(1000000); 12370Sstevel@tonic-gate diff = curlbolt - oldlbolt; 12380Sstevel@tonic-gate if (diff < ticks) { 12390Sstevel@tonic-gate delay_first = TRUE; 12400Sstevel@tonic-gate if (diff > 0) 12410Sstevel@tonic-gate ticks -= diff; 12420Sstevel@tonic-gate } 12430Sstevel@tonic-gate } 12440Sstevel@tonic-gate break; 12450Sstevel@tonic-gate } 12460Sstevel@tonic-gate 12470Sstevel@tonic-gate if (lwp != NULL) 12480Sstevel@tonic-gate lwp->lwp_nostop--; 12490Sstevel@tonic-gate } 12500Sstevel@tonic-gate /* 12510Sstevel@tonic-gate * Get the reply message, if any. This will be freed at the end 12520Sstevel@tonic-gate * whether or not an error occurred. 12530Sstevel@tonic-gate */ 12540Sstevel@tonic-gate mp = call->call_reply; 12550Sstevel@tonic-gate call->call_reply = NULL; 12560Sstevel@tonic-gate 12570Sstevel@tonic-gate /* 12580Sstevel@tonic-gate * call_err is the error info when the call is on dispatch queue. 12590Sstevel@tonic-gate * cku_err is the error info returned to the caller. 12600Sstevel@tonic-gate * Sync cku_err with call_err for local message processing. 12610Sstevel@tonic-gate */ 12620Sstevel@tonic-gate 12630Sstevel@tonic-gate status = call->call_status; 12640Sstevel@tonic-gate p->cku_err = call->call_err; 12650Sstevel@tonic-gate mutex_exit(&call->call_lock); 12660Sstevel@tonic-gate 12670Sstevel@tonic-gate if (status != RPC_SUCCESS) { 12680Sstevel@tonic-gate switch (status) { 12690Sstevel@tonic-gate case RPC_TIMEDOUT: 12700Sstevel@tonic-gate if (interrupted) { 12710Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rcintrs); 12720Sstevel@tonic-gate p->cku_err.re_status = RPC_INTR; 12730Sstevel@tonic-gate p->cku_err.re_errno = EINTR; 12740Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcallit: xid 0x%x", 12750Sstevel@tonic-gate p->cku_xid); 12760Sstevel@tonic-gate RPCLOG(1, "signal interrupted at %ld", lbolt); 12770Sstevel@tonic-gate RPCLOG(1, ", was sent at %ld\n", time_sent); 12780Sstevel@tonic-gate } else { 12790Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rctimeouts); 12800Sstevel@tonic-gate p->cku_err.re_errno = ETIMEDOUT; 12810Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcallit: timed out at %ld", 12820Sstevel@tonic-gate lbolt); 12830Sstevel@tonic-gate RPCLOG(1, ", was sent at %ld\n", time_sent); 12840Sstevel@tonic-gate } 12850Sstevel@tonic-gate break; 12860Sstevel@tonic-gate 12870Sstevel@tonic-gate case RPC_XPRTFAILED: 12880Sstevel@tonic-gate if (p->cku_err.re_errno == 0) 12890Sstevel@tonic-gate p->cku_err.re_errno = EIO; 12900Sstevel@tonic-gate 12910Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcallit: transport failed: %d\n", 12920Sstevel@tonic-gate p->cku_err.re_errno); 12930Sstevel@tonic-gate break; 12940Sstevel@tonic-gate 12950Sstevel@tonic-gate case RPC_SYSTEMERROR: 12960Sstevel@tonic-gate ASSERT(p->cku_err.re_errno); 12970Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcallit: system error: %d\n", 12980Sstevel@tonic-gate p->cku_err.re_errno); 12990Sstevel@tonic-gate break; 13000Sstevel@tonic-gate 13010Sstevel@tonic-gate default: 13020Sstevel@tonic-gate p->cku_err.re_status = RPC_SYSTEMERROR; 13030Sstevel@tonic-gate p->cku_err.re_errno = EIO; 13040Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcallit: error: %s\n", 13050Sstevel@tonic-gate clnt_sperrno(status)); 13060Sstevel@tonic-gate break; 13070Sstevel@tonic-gate } 13080Sstevel@tonic-gate if (p->cku_err.re_status != RPC_TIMEDOUT) { 13090Sstevel@tonic-gate 13100Sstevel@tonic-gate if (p->cku_flags & CKU_ONQUEUE) { 13110Sstevel@tonic-gate call_table_remove(call); 13120Sstevel@tonic-gate p->cku_flags &= ~CKU_ONQUEUE; 13130Sstevel@tonic-gate } 13140Sstevel@tonic-gate 13150Sstevel@tonic-gate RPCLOG(64, "clnt_cots_kcallit: non TIMEOUT so xid 0x%x " 13160Sstevel@tonic-gate "taken off dispatch list\n", p->cku_xid); 13170Sstevel@tonic-gate if (call->call_reply) { 13180Sstevel@tonic-gate freemsg(call->call_reply); 13190Sstevel@tonic-gate call->call_reply = NULL; 13200Sstevel@tonic-gate } 13210Sstevel@tonic-gate } else if (wait.tv_sec != 0) { 13220Sstevel@tonic-gate /* 13230Sstevel@tonic-gate * We've sent the request over TCP and so we have 13240Sstevel@tonic-gate * every reason to believe it will get 13250Sstevel@tonic-gate * delivered. In which case returning a timeout is not 13260Sstevel@tonic-gate * appropriate. 13270Sstevel@tonic-gate */ 13280Sstevel@tonic-gate if (p->cku_progress == TRUE && 13290Sstevel@tonic-gate p->cku_recv_attempts < clnt_cots_maxrecv) { 13300Sstevel@tonic-gate p->cku_err.re_status = RPC_INPROGRESS; 13310Sstevel@tonic-gate } 13320Sstevel@tonic-gate } 13330Sstevel@tonic-gate goto cots_done; 13340Sstevel@tonic-gate } 13350Sstevel@tonic-gate 13360Sstevel@tonic-gate xdrs = &p->cku_inxdr; 13370Sstevel@tonic-gate xdrmblk_init(xdrs, mp, XDR_DECODE, 0); 13380Sstevel@tonic-gate 13390Sstevel@tonic-gate reply_msg.rm_direction = REPLY; 13400Sstevel@tonic-gate reply_msg.rm_reply.rp_stat = MSG_ACCEPTED; 13410Sstevel@tonic-gate reply_msg.acpted_rply.ar_stat = SUCCESS; 13420Sstevel@tonic-gate 13430Sstevel@tonic-gate reply_msg.acpted_rply.ar_verf = _null_auth; 13440Sstevel@tonic-gate /* 13450Sstevel@tonic-gate * xdr_results will be done in AUTH_UNWRAP. 13460Sstevel@tonic-gate */ 13470Sstevel@tonic-gate reply_msg.acpted_rply.ar_results.where = NULL; 13480Sstevel@tonic-gate reply_msg.acpted_rply.ar_results.proc = xdr_void; 13490Sstevel@tonic-gate 13500Sstevel@tonic-gate if (xdr_replymsg(xdrs, &reply_msg)) { 13510Sstevel@tonic-gate enum clnt_stat re_status; 13520Sstevel@tonic-gate 13530Sstevel@tonic-gate _seterr_reply(&reply_msg, &p->cku_err); 13540Sstevel@tonic-gate 13550Sstevel@tonic-gate re_status = p->cku_err.re_status; 13560Sstevel@tonic-gate if (re_status == RPC_SUCCESS) { 13570Sstevel@tonic-gate /* 13580Sstevel@tonic-gate * Reply is good, check auth. 13590Sstevel@tonic-gate */ 13600Sstevel@tonic-gate if (!AUTH_VALIDATE(h->cl_auth, 13616403Sgt29601 &reply_msg.acpted_rply.ar_verf)) { 13620Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rcbadverfs); 13630Sstevel@tonic-gate RPCLOG0(1, "clnt_cots_kcallit: validation " 13646403Sgt29601 "failure\n"); 13650Sstevel@tonic-gate freemsg(mp); 13660Sstevel@tonic-gate (void) xdr_rpc_free_verifier(xdrs, &reply_msg); 13670Sstevel@tonic-gate mutex_enter(&call->call_lock); 13680Sstevel@tonic-gate if (call->call_reply == NULL) 13690Sstevel@tonic-gate call->call_status = RPC_TIMEDOUT; 13700Sstevel@tonic-gate mutex_exit(&call->call_lock); 13710Sstevel@tonic-gate goto read_again; 13720Sstevel@tonic-gate } else if (!AUTH_UNWRAP(h->cl_auth, xdrs, 13736403Sgt29601 xdr_results, resultsp)) { 13740Sstevel@tonic-gate RPCLOG0(1, "clnt_cots_kcallit: validation " 13756403Sgt29601 "failure (unwrap)\n"); 13760Sstevel@tonic-gate p->cku_err.re_status = RPC_CANTDECODERES; 13770Sstevel@tonic-gate p->cku_err.re_errno = EIO; 13780Sstevel@tonic-gate } 13790Sstevel@tonic-gate } else { 13800Sstevel@tonic-gate /* set errno in case we can't recover */ 13810Sstevel@tonic-gate if (re_status != RPC_VERSMISMATCH && 13820Sstevel@tonic-gate re_status != RPC_AUTHERROR && 13830Sstevel@tonic-gate re_status != RPC_PROGVERSMISMATCH) 13840Sstevel@tonic-gate p->cku_err.re_errno = EIO; 13850Sstevel@tonic-gate 13860Sstevel@tonic-gate if (re_status == RPC_AUTHERROR) { 13870Sstevel@tonic-gate /* 1388589Srg137905 * Maybe our credential need to be refreshed 13890Sstevel@tonic-gate */ 1390571Srg137905 if (cm_entry) { 1391571Srg137905 /* 1392571Srg137905 * There is the potential that the 1393571Srg137905 * cm_entry has/will be marked dead, 1394589Srg137905 * so drop the connection altogether, 1395589Srg137905 * force REFRESH to establish new 1396589Srg137905 * connection. 1397571Srg137905 */ 1398589Srg137905 connmgr_cancelconn(cm_entry); 1399571Srg137905 cm_entry = NULL; 14000Sstevel@tonic-gate } 14010Sstevel@tonic-gate 1402571Srg137905 if ((refreshes > 0) && 1403571Srg137905 AUTH_REFRESH(h->cl_auth, &reply_msg, 14046403Sgt29601 p->cku_cred)) { 1405571Srg137905 refreshes--; 1406571Srg137905 (void) xdr_rpc_free_verifier(xdrs, 14076403Sgt29601 &reply_msg); 1408571Srg137905 freemsg(mp); 1409571Srg137905 mp = NULL; 1410571Srg137905 1411571Srg137905 if (p->cku_flags & CKU_ONQUEUE) { 1412571Srg137905 call_table_remove(call); 1413571Srg137905 p->cku_flags &= ~CKU_ONQUEUE; 1414571Srg137905 } 1415571Srg137905 1416571Srg137905 RPCLOG(64, 1417571Srg137905 "clnt_cots_kcallit: AUTH_ERROR, xid" 1418571Srg137905 " 0x%x removed off dispatch list\n", 1419571Srg137905 p->cku_xid); 1420571Srg137905 if (call->call_reply) { 1421571Srg137905 freemsg(call->call_reply); 1422571Srg137905 call->call_reply = NULL; 1423571Srg137905 } 1424571Srg137905 1425571Srg137905 COTSRCSTAT_INCR(p->cku_stats, 14266403Sgt29601 rcbadcalls); 1427571Srg137905 COTSRCSTAT_INCR(p->cku_stats, 14286403Sgt29601 rcnewcreds); 1429571Srg137905 goto call_again; 1430589Srg137905 } 1431589Srg137905 14320Sstevel@tonic-gate /* 14330Sstevel@tonic-gate * We have used the client handle to 14340Sstevel@tonic-gate * do an AUTH_REFRESH and the RPC status may 14350Sstevel@tonic-gate * be set to RPC_SUCCESS; Let's make sure to 14360Sstevel@tonic-gate * set it to RPC_AUTHERROR. 14370Sstevel@tonic-gate */ 14380Sstevel@tonic-gate p->cku_err.re_status = RPC_AUTHERROR; 1439589Srg137905 14400Sstevel@tonic-gate /* 14410Sstevel@tonic-gate * Map recoverable and unrecoverable 14420Sstevel@tonic-gate * authentication errors to appropriate errno 14430Sstevel@tonic-gate */ 14440Sstevel@tonic-gate switch (p->cku_err.re_why) { 1445342Snd150628 case AUTH_TOOWEAK: 1446342Snd150628 /* 1447571Srg137905 * This could be a failure where the 1448571Srg137905 * server requires use of a reserved 1449571Srg137905 * port, check and optionally set the 1450571Srg137905 * client handle useresvport trying 1451571Srg137905 * one more time. Next go round we 1452571Srg137905 * fall out with the tooweak error. 1453342Snd150628 */ 1454342Snd150628 if (p->cku_useresvport != 1) { 1455342Snd150628 p->cku_useresvport = 1; 1456342Snd150628 p->cku_xid = 0; 1457342Snd150628 (void) xdr_rpc_free_verifier 14586403Sgt29601 (xdrs, &reply_msg); 1459342Snd150628 freemsg(mp); 1460342Snd150628 goto call_again; 1461342Snd150628 } 1462342Snd150628 /* FALLTHRU */ 14630Sstevel@tonic-gate case AUTH_BADCRED: 14640Sstevel@tonic-gate case AUTH_BADVERF: 14650Sstevel@tonic-gate case AUTH_INVALIDRESP: 14660Sstevel@tonic-gate case AUTH_FAILED: 14670Sstevel@tonic-gate case RPCSEC_GSS_NOCRED: 14680Sstevel@tonic-gate case RPCSEC_GSS_FAILED: 14690Sstevel@tonic-gate p->cku_err.re_errno = EACCES; 14700Sstevel@tonic-gate break; 14710Sstevel@tonic-gate case AUTH_REJECTEDCRED: 14720Sstevel@tonic-gate case AUTH_REJECTEDVERF: 14730Sstevel@tonic-gate default: p->cku_err.re_errno = EIO; 14740Sstevel@tonic-gate break; 14750Sstevel@tonic-gate } 14760Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcallit : authentication" 14770Sstevel@tonic-gate " failed with RPC_AUTHERROR of type %d\n", 14780Sstevel@tonic-gate (int)p->cku_err.re_why); 14790Sstevel@tonic-gate } 14800Sstevel@tonic-gate } 14810Sstevel@tonic-gate } else { 14820Sstevel@tonic-gate /* reply didn't decode properly. */ 14830Sstevel@tonic-gate p->cku_err.re_status = RPC_CANTDECODERES; 14840Sstevel@tonic-gate p->cku_err.re_errno = EIO; 14850Sstevel@tonic-gate RPCLOG0(1, "clnt_cots_kcallit: decode failure\n"); 14860Sstevel@tonic-gate } 14870Sstevel@tonic-gate 14880Sstevel@tonic-gate (void) xdr_rpc_free_verifier(xdrs, &reply_msg); 14890Sstevel@tonic-gate 14900Sstevel@tonic-gate if (p->cku_flags & CKU_ONQUEUE) { 14910Sstevel@tonic-gate call_table_remove(call); 14920Sstevel@tonic-gate p->cku_flags &= ~CKU_ONQUEUE; 14930Sstevel@tonic-gate } 14940Sstevel@tonic-gate 14950Sstevel@tonic-gate RPCLOG(64, "clnt_cots_kcallit: xid 0x%x taken off dispatch list", 14960Sstevel@tonic-gate p->cku_xid); 14970Sstevel@tonic-gate RPCLOG(64, " status is %s\n", clnt_sperrno(p->cku_err.re_status)); 14980Sstevel@tonic-gate cots_done: 14990Sstevel@tonic-gate if (cm_entry) 15000Sstevel@tonic-gate connmgr_release(cm_entry); 15010Sstevel@tonic-gate 15020Sstevel@tonic-gate if (mp != NULL) 15030Sstevel@tonic-gate freemsg(mp); 15040Sstevel@tonic-gate if ((p->cku_flags & CKU_ONQUEUE) == 0 && call->call_reply) { 15050Sstevel@tonic-gate freemsg(call->call_reply); 15060Sstevel@tonic-gate call->call_reply = NULL; 15070Sstevel@tonic-gate } 15080Sstevel@tonic-gate if (p->cku_err.re_status != RPC_SUCCESS) { 15090Sstevel@tonic-gate RPCLOG0(1, "clnt_cots_kcallit: tail-end failure\n"); 15100Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rcbadcalls); 15110Sstevel@tonic-gate } 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate /* 15140Sstevel@tonic-gate * No point in delaying if the zone is going away. 15150Sstevel@tonic-gate */ 15160Sstevel@tonic-gate if (delay_first == TRUE && 15170Sstevel@tonic-gate !(zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)) { 15180Sstevel@tonic-gate if (clnt_delay(ticks, h->cl_nosignal) == EINTR) { 15190Sstevel@tonic-gate p->cku_err.re_errno = EINTR; 15200Sstevel@tonic-gate p->cku_err.re_status = RPC_INTR; 15210Sstevel@tonic-gate } 15220Sstevel@tonic-gate } 15230Sstevel@tonic-gate return (p->cku_err.re_status); 15240Sstevel@tonic-gate } 15250Sstevel@tonic-gate 15260Sstevel@tonic-gate /* 15270Sstevel@tonic-gate * Kinit routine for cots. This sets up the correct operations in 15280Sstevel@tonic-gate * the client handle, as the handle may have previously been a clts 15290Sstevel@tonic-gate * handle, and clears the xid field so there is no way a new call 15300Sstevel@tonic-gate * could be mistaken for a retry. It also sets in the handle the 15310Sstevel@tonic-gate * information that is passed at create/kinit time but needed at 15320Sstevel@tonic-gate * call time, as cots creates the transport at call time - device, 15330Sstevel@tonic-gate * address of the server, protocol family. 15340Sstevel@tonic-gate */ 15350Sstevel@tonic-gate void 15360Sstevel@tonic-gate clnt_cots_kinit(CLIENT *h, dev_t dev, int family, struct netbuf *addr, 15370Sstevel@tonic-gate int max_msgsize, cred_t *cred) 15380Sstevel@tonic-gate { 15390Sstevel@tonic-gate /* LINTED pointer alignment */ 15400Sstevel@tonic-gate cku_private_t *p = htop(h); 15410Sstevel@tonic-gate calllist_t *call = &p->cku_call; 15420Sstevel@tonic-gate 15430Sstevel@tonic-gate h->cl_ops = &tcp_ops; 15440Sstevel@tonic-gate if (p->cku_flags & CKU_ONQUEUE) { 15450Sstevel@tonic-gate call_table_remove(call); 15460Sstevel@tonic-gate p->cku_flags &= ~CKU_ONQUEUE; 15470Sstevel@tonic-gate RPCLOG(64, "clnt_cots_kinit: removing call for xid 0x%x from" 15480Sstevel@tonic-gate " dispatch list\n", p->cku_xid); 15490Sstevel@tonic-gate } 15500Sstevel@tonic-gate 15510Sstevel@tonic-gate if (call->call_reply != NULL) { 15520Sstevel@tonic-gate freemsg(call->call_reply); 15530Sstevel@tonic-gate call->call_reply = NULL; 15540Sstevel@tonic-gate } 15550Sstevel@tonic-gate 15560Sstevel@tonic-gate call->call_bucket = NULL; 15570Sstevel@tonic-gate call->call_hash = 0; 15580Sstevel@tonic-gate 15590Sstevel@tonic-gate /* 15600Sstevel@tonic-gate * We don't clear cku_flags here, because clnt_cots_kcallit() 15610Sstevel@tonic-gate * takes care of handling the cku_flags reset. 15620Sstevel@tonic-gate */ 15630Sstevel@tonic-gate p->cku_xid = 0; 15640Sstevel@tonic-gate p->cku_device = dev; 15650Sstevel@tonic-gate p->cku_addrfmly = family; 15660Sstevel@tonic-gate p->cku_cred = cred; 15670Sstevel@tonic-gate 15680Sstevel@tonic-gate if (p->cku_addr.maxlen < addr->len) { 15690Sstevel@tonic-gate if (p->cku_addr.maxlen != 0 && p->cku_addr.buf != NULL) 15700Sstevel@tonic-gate kmem_free(p->cku_addr.buf, p->cku_addr.maxlen); 15710Sstevel@tonic-gate p->cku_addr.buf = kmem_zalloc(addr->maxlen, KM_SLEEP); 15720Sstevel@tonic-gate p->cku_addr.maxlen = addr->maxlen; 15730Sstevel@tonic-gate } 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate p->cku_addr.len = addr->len; 15760Sstevel@tonic-gate bcopy(addr->buf, p->cku_addr.buf, addr->len); 15770Sstevel@tonic-gate 15780Sstevel@tonic-gate /* 15790Sstevel@tonic-gate * If the current sanity check size in rpcmod is smaller 15800Sstevel@tonic-gate * than the size needed, then increase the sanity check. 15810Sstevel@tonic-gate */ 15820Sstevel@tonic-gate if (max_msgsize != 0 && clnt_max_msg_sizep != NULL && 15830Sstevel@tonic-gate max_msgsize > *clnt_max_msg_sizep) { 15840Sstevel@tonic-gate mutex_enter(&clnt_max_msg_lock); 15850Sstevel@tonic-gate if (max_msgsize > *clnt_max_msg_sizep) 15860Sstevel@tonic-gate *clnt_max_msg_sizep = max_msgsize; 15870Sstevel@tonic-gate mutex_exit(&clnt_max_msg_lock); 15880Sstevel@tonic-gate } 15890Sstevel@tonic-gate } 15900Sstevel@tonic-gate 15910Sstevel@tonic-gate /* 15920Sstevel@tonic-gate * ksettimers is a no-op for cots, with the exception of setting the xid. 15930Sstevel@tonic-gate */ 15940Sstevel@tonic-gate /* ARGSUSED */ 15950Sstevel@tonic-gate static int 15960Sstevel@tonic-gate clnt_cots_ksettimers(CLIENT *h, struct rpc_timers *t, struct rpc_timers *all, 15970Sstevel@tonic-gate int minimum, void (*feedback)(int, int, caddr_t), caddr_t arg, 15980Sstevel@tonic-gate uint32_t xid) 15990Sstevel@tonic-gate { 16000Sstevel@tonic-gate /* LINTED pointer alignment */ 16010Sstevel@tonic-gate cku_private_t *p = htop(h); 16020Sstevel@tonic-gate 16030Sstevel@tonic-gate if (xid) 16040Sstevel@tonic-gate p->cku_xid = xid; 16050Sstevel@tonic-gate COTSRCSTAT_INCR(p->cku_stats, rctimers); 16060Sstevel@tonic-gate return (0); 16070Sstevel@tonic-gate } 16080Sstevel@tonic-gate 16090Sstevel@tonic-gate extern void rpc_poptimod(struct vnode *); 16100Sstevel@tonic-gate extern int kstr_push(struct vnode *, char *); 16110Sstevel@tonic-gate 16120Sstevel@tonic-gate int 16130Sstevel@tonic-gate conn_kstat_update(kstat_t *ksp, int rw) 16140Sstevel@tonic-gate { 16150Sstevel@tonic-gate struct cm_xprt *cm_entry; 16160Sstevel@tonic-gate struct cm_kstat_xprt *cm_ksp_data; 16170Sstevel@tonic-gate uchar_t *b; 16180Sstevel@tonic-gate char *fbuf; 16190Sstevel@tonic-gate 16200Sstevel@tonic-gate if (rw == KSTAT_WRITE) 16210Sstevel@tonic-gate return (EACCES); 16220Sstevel@tonic-gate if (ksp == NULL || ksp->ks_private == NULL) 16230Sstevel@tonic-gate return (EIO); 16240Sstevel@tonic-gate cm_entry = (struct cm_xprt *)ksp->ks_private; 16250Sstevel@tonic-gate cm_ksp_data = (struct cm_kstat_xprt *)ksp->ks_data; 16260Sstevel@tonic-gate 16270Sstevel@tonic-gate cm_ksp_data->x_wq.value.ui32 = (uint32_t)(uintptr_t)cm_entry->x_wq; 16280Sstevel@tonic-gate cm_ksp_data->x_family.value.ui32 = cm_entry->x_family; 16290Sstevel@tonic-gate cm_ksp_data->x_rdev.value.ui32 = (uint32_t)cm_entry->x_rdev; 16300Sstevel@tonic-gate cm_ksp_data->x_time.value.ui32 = cm_entry->x_time; 16310Sstevel@tonic-gate cm_ksp_data->x_ref.value.ui32 = cm_entry->x_ref; 16320Sstevel@tonic-gate cm_ksp_data->x_state.value.ui32 = cm_entry->x_state_flags; 16330Sstevel@tonic-gate 16340Sstevel@tonic-gate if (cm_entry->x_server.buf) { 1635457Sbmc fbuf = cm_ksp_data->x_server.value.str.addr.ptr; 16360Sstevel@tonic-gate if (cm_entry->x_family == AF_INET && 16370Sstevel@tonic-gate cm_entry->x_server.len == 16380Sstevel@tonic-gate sizeof (struct sockaddr_in)) { 16390Sstevel@tonic-gate struct sockaddr_in *sa; 16400Sstevel@tonic-gate sa = (struct sockaddr_in *) 16410Sstevel@tonic-gate cm_entry->x_server.buf; 16420Sstevel@tonic-gate b = (uchar_t *)&sa->sin_addr; 16430Sstevel@tonic-gate (void) sprintf(fbuf, 16440Sstevel@tonic-gate "%03d.%03d.%03d.%03d", b[0] & 0xFF, b[1] & 0xFF, 16450Sstevel@tonic-gate b[2] & 0xFF, b[3] & 0xFF); 16460Sstevel@tonic-gate cm_ksp_data->x_port.value.ui32 = 16470Sstevel@tonic-gate (uint32_t)sa->sin_port; 16480Sstevel@tonic-gate } else if (cm_entry->x_family == AF_INET6 && 16490Sstevel@tonic-gate cm_entry->x_server.len >= 16500Sstevel@tonic-gate sizeof (struct sockaddr_in6)) { 16510Sstevel@tonic-gate /* extract server IP address & port */ 16520Sstevel@tonic-gate struct sockaddr_in6 *sin6; 16530Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)cm_entry->x_server.buf; 16540Sstevel@tonic-gate (void) kinet_ntop6((uchar_t *)&sin6->sin6_addr, fbuf, 16550Sstevel@tonic-gate INET6_ADDRSTRLEN); 16560Sstevel@tonic-gate cm_ksp_data->x_port.value.ui32 = sin6->sin6_port; 16570Sstevel@tonic-gate } else { 16580Sstevel@tonic-gate struct sockaddr_in *sa; 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate sa = (struct sockaddr_in *)cm_entry->x_server.buf; 16610Sstevel@tonic-gate b = (uchar_t *)&sa->sin_addr; 16620Sstevel@tonic-gate (void) sprintf(fbuf, 16630Sstevel@tonic-gate "%03d.%03d.%03d.%03d", b[0] & 0xFF, b[1] & 0xFF, 16640Sstevel@tonic-gate b[2] & 0xFF, b[3] & 0xFF); 16650Sstevel@tonic-gate } 16660Sstevel@tonic-gate KSTAT_NAMED_STR_BUFLEN(&cm_ksp_data->x_server) = 16676403Sgt29601 strlen(fbuf) + 1; 16680Sstevel@tonic-gate } 16690Sstevel@tonic-gate 16700Sstevel@tonic-gate return (0); 16710Sstevel@tonic-gate } 16720Sstevel@tonic-gate 16730Sstevel@tonic-gate 16740Sstevel@tonic-gate /* 16750Sstevel@tonic-gate * We want a version of delay which is interruptible by a UNIX signal 16760Sstevel@tonic-gate * Return EINTR if an interrupt occured. 16770Sstevel@tonic-gate */ 16780Sstevel@tonic-gate static int 16790Sstevel@tonic-gate clnt_delay(clock_t ticks, bool_t nosignal) 16800Sstevel@tonic-gate { 16810Sstevel@tonic-gate if (nosignal == TRUE) { 16820Sstevel@tonic-gate delay(ticks); 16830Sstevel@tonic-gate return (0); 16840Sstevel@tonic-gate } 16850Sstevel@tonic-gate return (delay_sig(ticks)); 16860Sstevel@tonic-gate } 16870Sstevel@tonic-gate 16880Sstevel@tonic-gate /* 16890Sstevel@tonic-gate * Wait for a connection until a timeout, or until we are 16900Sstevel@tonic-gate * signalled that there has been a connection state change. 16910Sstevel@tonic-gate */ 16920Sstevel@tonic-gate static enum clnt_stat 16930Sstevel@tonic-gate connmgr_cwait(struct cm_xprt *cm_entry, const struct timeval *waitp, 16940Sstevel@tonic-gate bool_t nosignal) 16950Sstevel@tonic-gate { 16960Sstevel@tonic-gate bool_t interrupted; 16970Sstevel@tonic-gate clock_t timout, cv_stat; 16980Sstevel@tonic-gate enum clnt_stat clstat; 16990Sstevel@tonic-gate unsigned int old_state; 17000Sstevel@tonic-gate 17010Sstevel@tonic-gate ASSERT(MUTEX_HELD(&connmgr_lock)); 17020Sstevel@tonic-gate /* 17030Sstevel@tonic-gate * We wait for the transport connection to be made, or an 17040Sstevel@tonic-gate * indication that it could not be made. 17050Sstevel@tonic-gate */ 17060Sstevel@tonic-gate clstat = RPC_TIMEDOUT; 17070Sstevel@tonic-gate interrupted = FALSE; 17080Sstevel@tonic-gate 17090Sstevel@tonic-gate old_state = cm_entry->x_state_flags; 17100Sstevel@tonic-gate /* 17110Sstevel@tonic-gate * Now loop until cv_timedwait{_sig} returns because of 17120Sstevel@tonic-gate * a signal(0) or timeout(-1) or cv_signal(>0). But it may be 17130Sstevel@tonic-gate * cv_signalled for various other reasons too. So loop 17140Sstevel@tonic-gate * until there is a state change on the connection. 17150Sstevel@tonic-gate */ 17160Sstevel@tonic-gate 17170Sstevel@tonic-gate timout = waitp->tv_sec * drv_usectohz(1000000) + 17180Sstevel@tonic-gate drv_usectohz(waitp->tv_usec) + lbolt; 17190Sstevel@tonic-gate 17200Sstevel@tonic-gate if (nosignal) { 17210Sstevel@tonic-gate while ((cv_stat = cv_timedwait(&cm_entry->x_conn_cv, 17220Sstevel@tonic-gate &connmgr_lock, timout)) > 0 && 17230Sstevel@tonic-gate cm_entry->x_state_flags == old_state) 17240Sstevel@tonic-gate ; 17250Sstevel@tonic-gate } else { 17260Sstevel@tonic-gate while ((cv_stat = cv_timedwait_sig(&cm_entry->x_conn_cv, 17270Sstevel@tonic-gate &connmgr_lock, timout)) > 0 && 17280Sstevel@tonic-gate cm_entry->x_state_flags == old_state) 17290Sstevel@tonic-gate ; 17300Sstevel@tonic-gate 17310Sstevel@tonic-gate if (cv_stat == 0) /* got intr signal? */ 17320Sstevel@tonic-gate interrupted = TRUE; 17330Sstevel@tonic-gate } 17340Sstevel@tonic-gate 17350Sstevel@tonic-gate if ((cm_entry->x_state_flags & (X_BADSTATES|X_CONNECTED)) == 17360Sstevel@tonic-gate X_CONNECTED) { 17370Sstevel@tonic-gate clstat = RPC_SUCCESS; 17380Sstevel@tonic-gate } else { 17390Sstevel@tonic-gate if (interrupted == TRUE) 17400Sstevel@tonic-gate clstat = RPC_INTR; 17410Sstevel@tonic-gate RPCLOG(1, "connmgr_cwait: can't connect, error: %s\n", 17420Sstevel@tonic-gate clnt_sperrno(clstat)); 17430Sstevel@tonic-gate } 17440Sstevel@tonic-gate 17450Sstevel@tonic-gate return (clstat); 17460Sstevel@tonic-gate } 17470Sstevel@tonic-gate 17480Sstevel@tonic-gate /* 17490Sstevel@tonic-gate * Primary interface for how RPC grabs a connection. 17500Sstevel@tonic-gate */ 17510Sstevel@tonic-gate static struct cm_xprt * 17520Sstevel@tonic-gate connmgr_wrapget( 17530Sstevel@tonic-gate struct netbuf *retryaddr, 17540Sstevel@tonic-gate const struct timeval *waitp, 17550Sstevel@tonic-gate cku_private_t *p) 17560Sstevel@tonic-gate { 17570Sstevel@tonic-gate struct cm_xprt *cm_entry; 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate cm_entry = connmgr_get(retryaddr, waitp, &p->cku_addr, p->cku_addrfmly, 17600Sstevel@tonic-gate &p->cku_srcaddr, &p->cku_err, p->cku_device, 17618778SErik.Nordmark@Sun.COM p->cku_client.cl_nosignal, p->cku_useresvport, p->cku_cred); 17620Sstevel@tonic-gate 17630Sstevel@tonic-gate if (cm_entry == NULL) { 17640Sstevel@tonic-gate /* 17650Sstevel@tonic-gate * Re-map the call status to RPC_INTR if the err code is 17660Sstevel@tonic-gate * EINTR. This can happen if calls status is RPC_TLIERROR. 17670Sstevel@tonic-gate * However, don't re-map if signalling has been turned off. 17680Sstevel@tonic-gate * XXX Really need to create a separate thread whenever 17690Sstevel@tonic-gate * there isn't an existing connection. 17700Sstevel@tonic-gate */ 17710Sstevel@tonic-gate if (p->cku_err.re_errno == EINTR) { 17720Sstevel@tonic-gate if (p->cku_client.cl_nosignal == TRUE) 17730Sstevel@tonic-gate p->cku_err.re_errno = EIO; 17740Sstevel@tonic-gate else 17750Sstevel@tonic-gate p->cku_err.re_status = RPC_INTR; 17760Sstevel@tonic-gate } 17770Sstevel@tonic-gate } 17780Sstevel@tonic-gate 17790Sstevel@tonic-gate return (cm_entry); 17800Sstevel@tonic-gate } 17810Sstevel@tonic-gate 17820Sstevel@tonic-gate /* 17830Sstevel@tonic-gate * Obtains a transport to the server specified in addr. If a suitable transport 17840Sstevel@tonic-gate * does not already exist in the list of cached transports, a new connection 17850Sstevel@tonic-gate * is created, connected, and added to the list. The connection is for sending 17860Sstevel@tonic-gate * only - the reply message may come back on another transport connection. 17879806Sdai.ngo@sun.com * 17889806Sdai.ngo@sun.com * To implement round-robin load balancing with multiple client connections, 17899806Sdai.ngo@sun.com * the last entry on the list is always selected. Once the entry is selected 17909806Sdai.ngo@sun.com * it's re-inserted to the head of the list. 17910Sstevel@tonic-gate */ 17920Sstevel@tonic-gate static struct cm_xprt * 17930Sstevel@tonic-gate connmgr_get( 17940Sstevel@tonic-gate struct netbuf *retryaddr, 17950Sstevel@tonic-gate const struct timeval *waitp, /* changed to a ptr to converse stack */ 17960Sstevel@tonic-gate struct netbuf *destaddr, 17970Sstevel@tonic-gate int addrfmly, 17980Sstevel@tonic-gate struct netbuf *srcaddr, 17990Sstevel@tonic-gate struct rpc_err *rpcerr, 18000Sstevel@tonic-gate dev_t device, 18010Sstevel@tonic-gate bool_t nosignal, 18028778SErik.Nordmark@Sun.COM int useresvport, 18038778SErik.Nordmark@Sun.COM cred_t *cr) 18040Sstevel@tonic-gate { 18050Sstevel@tonic-gate struct cm_xprt *cm_entry; 18060Sstevel@tonic-gate struct cm_xprt *lru_entry; 18079806Sdai.ngo@sun.com struct cm_xprt **cmp, **prev; 18080Sstevel@tonic-gate queue_t *wq; 18090Sstevel@tonic-gate TIUSER *tiptr; 18100Sstevel@tonic-gate int i; 18110Sstevel@tonic-gate int retval; 18120Sstevel@tonic-gate int tidu_size; 18130Sstevel@tonic-gate bool_t connected; 1814766Scarlsonj zoneid_t zoneid = rpc_zoneid(); 18150Sstevel@tonic-gate 18160Sstevel@tonic-gate /* 18170Sstevel@tonic-gate * If the call is not a retry, look for a transport entry that 18180Sstevel@tonic-gate * goes to the server of interest. 18190Sstevel@tonic-gate */ 18200Sstevel@tonic-gate mutex_enter(&connmgr_lock); 18210Sstevel@tonic-gate 18220Sstevel@tonic-gate if (retryaddr == NULL) { 18230Sstevel@tonic-gate use_new_conn: 18240Sstevel@tonic-gate i = 0; 18250Sstevel@tonic-gate cm_entry = lru_entry = NULL; 18269806Sdai.ngo@sun.com 18279806Sdai.ngo@sun.com prev = cmp = &cm_hd; 18280Sstevel@tonic-gate while ((cm_entry = *cmp) != NULL) { 18290Sstevel@tonic-gate ASSERT(cm_entry != cm_entry->x_next); 18300Sstevel@tonic-gate /* 18310Sstevel@tonic-gate * Garbage collect conections that are marked 18320Sstevel@tonic-gate * for needs disconnect. 18330Sstevel@tonic-gate */ 18340Sstevel@tonic-gate if (cm_entry->x_needdis) { 1835154Sshepler CONN_HOLD(cm_entry); 18360Sstevel@tonic-gate connmgr_dis_and_wait(cm_entry); 1837154Sshepler connmgr_release(cm_entry); 18380Sstevel@tonic-gate /* 18390Sstevel@tonic-gate * connmgr_lock could have been 18400Sstevel@tonic-gate * dropped for the disconnect 18410Sstevel@tonic-gate * processing so start over. 18420Sstevel@tonic-gate */ 18430Sstevel@tonic-gate goto use_new_conn; 18440Sstevel@tonic-gate } 18450Sstevel@tonic-gate 18460Sstevel@tonic-gate /* 18470Sstevel@tonic-gate * Garbage collect the dead connections that have 18480Sstevel@tonic-gate * no threads working on them. 18490Sstevel@tonic-gate */ 18500Sstevel@tonic-gate if ((cm_entry->x_state_flags & (X_DEAD|X_THREAD)) == 18510Sstevel@tonic-gate X_DEAD) { 18523017Smaheshvs mutex_enter(&cm_entry->x_lock); 18533017Smaheshvs if (cm_entry->x_ref != 0) { 18543017Smaheshvs /* 18553017Smaheshvs * Currently in use. 18563017Smaheshvs * Cleanup later. 18573017Smaheshvs */ 18583017Smaheshvs cmp = &cm_entry->x_next; 18593017Smaheshvs mutex_exit(&cm_entry->x_lock); 18603017Smaheshvs continue; 18613017Smaheshvs } 18623017Smaheshvs mutex_exit(&cm_entry->x_lock); 18630Sstevel@tonic-gate *cmp = cm_entry->x_next; 18640Sstevel@tonic-gate mutex_exit(&connmgr_lock); 18650Sstevel@tonic-gate connmgr_close(cm_entry); 18660Sstevel@tonic-gate mutex_enter(&connmgr_lock); 18670Sstevel@tonic-gate goto use_new_conn; 18680Sstevel@tonic-gate } 18690Sstevel@tonic-gate 18700Sstevel@tonic-gate 18710Sstevel@tonic-gate if ((cm_entry->x_state_flags & X_BADSTATES) == 0 && 18720Sstevel@tonic-gate cm_entry->x_zoneid == zoneid && 18730Sstevel@tonic-gate cm_entry->x_rdev == device && 18740Sstevel@tonic-gate destaddr->len == cm_entry->x_server.len && 18750Sstevel@tonic-gate bcmp(destaddr->buf, cm_entry->x_server.buf, 18760Sstevel@tonic-gate destaddr->len) == 0) { 18770Sstevel@tonic-gate /* 18780Sstevel@tonic-gate * If the matching entry isn't connected, 18790Sstevel@tonic-gate * attempt to reconnect it. 18800Sstevel@tonic-gate */ 18810Sstevel@tonic-gate if (cm_entry->x_connected == FALSE) { 18820Sstevel@tonic-gate /* 18830Sstevel@tonic-gate * We don't go through trying 18840Sstevel@tonic-gate * to find the least recently 18850Sstevel@tonic-gate * used connected because 18860Sstevel@tonic-gate * connmgr_reconnect() briefly 18870Sstevel@tonic-gate * dropped the connmgr_lock, 18880Sstevel@tonic-gate * allowing a window for our 18890Sstevel@tonic-gate * accounting to be messed up. 18900Sstevel@tonic-gate * In any case, a re-connected 18910Sstevel@tonic-gate * connection is as good as 18920Sstevel@tonic-gate * a LRU connection. 18930Sstevel@tonic-gate */ 18940Sstevel@tonic-gate return (connmgr_wrapconnect(cm_entry, 18950Sstevel@tonic-gate waitp, destaddr, addrfmly, srcaddr, 18968778SErik.Nordmark@Sun.COM rpcerr, TRUE, nosignal, cr)); 18970Sstevel@tonic-gate } 18980Sstevel@tonic-gate i++; 18999806Sdai.ngo@sun.com 19009806Sdai.ngo@sun.com /* keep track of the last entry */ 19019806Sdai.ngo@sun.com lru_entry = cm_entry; 19029806Sdai.ngo@sun.com prev = cmp; 19030Sstevel@tonic-gate } 19040Sstevel@tonic-gate cmp = &cm_entry->x_next; 19050Sstevel@tonic-gate } 19060Sstevel@tonic-gate 19070Sstevel@tonic-gate if (i > clnt_max_conns) { 19080Sstevel@tonic-gate RPCLOG(8, "connmgr_get: too many conns, dooming entry" 19090Sstevel@tonic-gate " %p\n", (void *)lru_entry->x_tiptr); 19100Sstevel@tonic-gate lru_entry->x_doomed = TRUE; 19110Sstevel@tonic-gate goto use_new_conn; 19120Sstevel@tonic-gate } 19130Sstevel@tonic-gate 19140Sstevel@tonic-gate /* 19150Sstevel@tonic-gate * If we are at the maximum number of connections to 19160Sstevel@tonic-gate * the server, hand back the least recently used one. 19170Sstevel@tonic-gate */ 19180Sstevel@tonic-gate if (i == clnt_max_conns) { 19190Sstevel@tonic-gate /* 19200Sstevel@tonic-gate * Copy into the handle the source address of 19210Sstevel@tonic-gate * the connection, which we will use in case of 19220Sstevel@tonic-gate * a later retry. 19230Sstevel@tonic-gate */ 19240Sstevel@tonic-gate if (srcaddr->len != lru_entry->x_src.len) { 19250Sstevel@tonic-gate if (srcaddr->len > 0) 19260Sstevel@tonic-gate kmem_free(srcaddr->buf, 19270Sstevel@tonic-gate srcaddr->maxlen); 19280Sstevel@tonic-gate srcaddr->buf = kmem_zalloc( 19290Sstevel@tonic-gate lru_entry->x_src.len, KM_SLEEP); 19300Sstevel@tonic-gate srcaddr->maxlen = srcaddr->len = 19310Sstevel@tonic-gate lru_entry->x_src.len; 19320Sstevel@tonic-gate } 19330Sstevel@tonic-gate bcopy(lru_entry->x_src.buf, srcaddr->buf, srcaddr->len); 19340Sstevel@tonic-gate RPCLOG(2, "connmgr_get: call going out on %p\n", 19350Sstevel@tonic-gate (void *)lru_entry); 19360Sstevel@tonic-gate lru_entry->x_time = lbolt; 19370Sstevel@tonic-gate CONN_HOLD(lru_entry); 19389806Sdai.ngo@sun.com 19399806Sdai.ngo@sun.com if ((i > 1) && (prev != &cm_hd)) { 19409806Sdai.ngo@sun.com /* 19419806Sdai.ngo@sun.com * remove and re-insert entry at head of list. 19429806Sdai.ngo@sun.com */ 19439806Sdai.ngo@sun.com *prev = lru_entry->x_next; 19449806Sdai.ngo@sun.com lru_entry->x_next = cm_hd; 19459806Sdai.ngo@sun.com cm_hd = lru_entry; 19469806Sdai.ngo@sun.com } 19479806Sdai.ngo@sun.com 19480Sstevel@tonic-gate mutex_exit(&connmgr_lock); 19490Sstevel@tonic-gate return (lru_entry); 19500Sstevel@tonic-gate } 19510Sstevel@tonic-gate 19520Sstevel@tonic-gate } else { 19530Sstevel@tonic-gate /* 19540Sstevel@tonic-gate * This is the retry case (retryaddr != NULL). Retries must 19550Sstevel@tonic-gate * be sent on the same source port as the original call. 19560Sstevel@tonic-gate */ 19570Sstevel@tonic-gate 19580Sstevel@tonic-gate /* 19590Sstevel@tonic-gate * Walk the list looking for a connection with a source address 19600Sstevel@tonic-gate * that matches the retry address. 19610Sstevel@tonic-gate */ 19628806SGerald.Thornbrugh@Sun.COM start_retry_loop: 19630Sstevel@tonic-gate cmp = &cm_hd; 19640Sstevel@tonic-gate while ((cm_entry = *cmp) != NULL) { 19650Sstevel@tonic-gate ASSERT(cm_entry != cm_entry->x_next); 19668806SGerald.Thornbrugh@Sun.COM 19678806SGerald.Thornbrugh@Sun.COM /* 19688806SGerald.Thornbrugh@Sun.COM * determine if this connection matches the passed 19698806SGerald.Thornbrugh@Sun.COM * in retry address. If it does not match, advance 19708806SGerald.Thornbrugh@Sun.COM * to the next element on the list. 19718806SGerald.Thornbrugh@Sun.COM */ 19720Sstevel@tonic-gate if (zoneid != cm_entry->x_zoneid || 19730Sstevel@tonic-gate device != cm_entry->x_rdev || 19740Sstevel@tonic-gate retryaddr->len != cm_entry->x_src.len || 19750Sstevel@tonic-gate bcmp(retryaddr->buf, cm_entry->x_src.buf, 19766403Sgt29601 retryaddr->len) != 0) { 19770Sstevel@tonic-gate cmp = &cm_entry->x_next; 19780Sstevel@tonic-gate continue; 19790Sstevel@tonic-gate } 19808806SGerald.Thornbrugh@Sun.COM /* 19818806SGerald.Thornbrugh@Sun.COM * Garbage collect conections that are marked 19828806SGerald.Thornbrugh@Sun.COM * for needs disconnect. 19838806SGerald.Thornbrugh@Sun.COM */ 19848806SGerald.Thornbrugh@Sun.COM if (cm_entry->x_needdis) { 19858806SGerald.Thornbrugh@Sun.COM CONN_HOLD(cm_entry); 19868806SGerald.Thornbrugh@Sun.COM connmgr_dis_and_wait(cm_entry); 19878806SGerald.Thornbrugh@Sun.COM connmgr_release(cm_entry); 19888806SGerald.Thornbrugh@Sun.COM /* 19898806SGerald.Thornbrugh@Sun.COM * connmgr_lock could have been 19908806SGerald.Thornbrugh@Sun.COM * dropped for the disconnect 19918806SGerald.Thornbrugh@Sun.COM * processing so start over. 19928806SGerald.Thornbrugh@Sun.COM */ 19938806SGerald.Thornbrugh@Sun.COM goto start_retry_loop; 19948806SGerald.Thornbrugh@Sun.COM } 19958806SGerald.Thornbrugh@Sun.COM /* 19968806SGerald.Thornbrugh@Sun.COM * Garbage collect the dead connections that have 19978806SGerald.Thornbrugh@Sun.COM * no threads working on them. 19988806SGerald.Thornbrugh@Sun.COM */ 19998806SGerald.Thornbrugh@Sun.COM if ((cm_entry->x_state_flags & (X_DEAD|X_THREAD)) == 20008806SGerald.Thornbrugh@Sun.COM X_DEAD) { 20018806SGerald.Thornbrugh@Sun.COM mutex_enter(&cm_entry->x_lock); 20028806SGerald.Thornbrugh@Sun.COM if (cm_entry->x_ref != 0) { 20038806SGerald.Thornbrugh@Sun.COM /* 20048806SGerald.Thornbrugh@Sun.COM * Currently in use. 20058806SGerald.Thornbrugh@Sun.COM * Cleanup later. 20068806SGerald.Thornbrugh@Sun.COM */ 20078806SGerald.Thornbrugh@Sun.COM cmp = &cm_entry->x_next; 20088806SGerald.Thornbrugh@Sun.COM mutex_exit(&cm_entry->x_lock); 20098806SGerald.Thornbrugh@Sun.COM continue; 20108806SGerald.Thornbrugh@Sun.COM } 20118806SGerald.Thornbrugh@Sun.COM mutex_exit(&cm_entry->x_lock); 20128806SGerald.Thornbrugh@Sun.COM *cmp = cm_entry->x_next; 20138806SGerald.Thornbrugh@Sun.COM mutex_exit(&connmgr_lock); 20148806SGerald.Thornbrugh@Sun.COM connmgr_close(cm_entry); 20158806SGerald.Thornbrugh@Sun.COM mutex_enter(&connmgr_lock); 20168806SGerald.Thornbrugh@Sun.COM goto start_retry_loop; 20178806SGerald.Thornbrugh@Sun.COM } 20180Sstevel@tonic-gate 20190Sstevel@tonic-gate /* 20200Sstevel@tonic-gate * Sanity check: if the connection with our source 20210Sstevel@tonic-gate * port is going to some other server, something went 20220Sstevel@tonic-gate * wrong, as we never delete connections (i.e. release 20230Sstevel@tonic-gate * ports) unless they have been idle. In this case, 20240Sstevel@tonic-gate * it is probably better to send the call out using 20250Sstevel@tonic-gate * a new source address than to fail it altogether, 20260Sstevel@tonic-gate * since that port may never be released. 20270Sstevel@tonic-gate */ 20280Sstevel@tonic-gate if (destaddr->len != cm_entry->x_server.len || 20296403Sgt29601 bcmp(destaddr->buf, cm_entry->x_server.buf, 20306403Sgt29601 destaddr->len) != 0) { 20310Sstevel@tonic-gate RPCLOG(1, "connmgr_get: tiptr %p" 20320Sstevel@tonic-gate " is going to a different server" 20330Sstevel@tonic-gate " with the port that belongs" 20340Sstevel@tonic-gate " to us!\n", (void *)cm_entry->x_tiptr); 20350Sstevel@tonic-gate retryaddr = NULL; 20360Sstevel@tonic-gate goto use_new_conn; 20370Sstevel@tonic-gate } 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate /* 20400Sstevel@tonic-gate * If the connection of interest is not connected and we 20410Sstevel@tonic-gate * can't reconnect it, then the server is probably 20420Sstevel@tonic-gate * still down. Return NULL to the caller and let it 20430Sstevel@tonic-gate * retry later if it wants to. We have a delay so the 20440Sstevel@tonic-gate * machine doesn't go into a tight retry loop. If the 20450Sstevel@tonic-gate * entry was already connected, or the reconnected was 20460Sstevel@tonic-gate * successful, return this entry. 20470Sstevel@tonic-gate */ 20480Sstevel@tonic-gate if (cm_entry->x_connected == FALSE) { 20490Sstevel@tonic-gate return (connmgr_wrapconnect(cm_entry, 20500Sstevel@tonic-gate waitp, destaddr, addrfmly, NULL, 20518778SErik.Nordmark@Sun.COM rpcerr, TRUE, nosignal, cr)); 20520Sstevel@tonic-gate } else { 20530Sstevel@tonic-gate CONN_HOLD(cm_entry); 20540Sstevel@tonic-gate 20550Sstevel@tonic-gate cm_entry->x_time = lbolt; 20560Sstevel@tonic-gate mutex_exit(&connmgr_lock); 20570Sstevel@tonic-gate RPCLOG(2, "connmgr_get: found old " 20580Sstevel@tonic-gate "transport %p for retry\n", 20590Sstevel@tonic-gate (void *)cm_entry); 20600Sstevel@tonic-gate return (cm_entry); 20610Sstevel@tonic-gate } 20620Sstevel@tonic-gate } 20630Sstevel@tonic-gate 20640Sstevel@tonic-gate /* 20650Sstevel@tonic-gate * We cannot find an entry in the list for this retry. 20660Sstevel@tonic-gate * Either the entry has been removed temporarily to be 20670Sstevel@tonic-gate * reconnected by another thread, or the original call 20680Sstevel@tonic-gate * got a port but never got connected, 20690Sstevel@tonic-gate * and hence the transport never got put in the 20700Sstevel@tonic-gate * list. Fall through to the "create new connection" code - 20710Sstevel@tonic-gate * the former case will fail there trying to rebind the port, 20720Sstevel@tonic-gate * and the later case (and any other pathological cases) will 20730Sstevel@tonic-gate * rebind and reconnect and not hang the client machine. 20740Sstevel@tonic-gate */ 20750Sstevel@tonic-gate RPCLOG0(8, "connmgr_get: no entry in list for retry\n"); 20760Sstevel@tonic-gate } 20770Sstevel@tonic-gate /* 20780Sstevel@tonic-gate * Set up a transport entry in the connection manager's list. 20790Sstevel@tonic-gate */ 20800Sstevel@tonic-gate cm_entry = (struct cm_xprt *) 20810Sstevel@tonic-gate kmem_zalloc(sizeof (struct cm_xprt), KM_SLEEP); 20820Sstevel@tonic-gate 20830Sstevel@tonic-gate cm_entry->x_server.buf = kmem_zalloc(destaddr->len, KM_SLEEP); 20840Sstevel@tonic-gate bcopy(destaddr->buf, cm_entry->x_server.buf, destaddr->len); 20850Sstevel@tonic-gate cm_entry->x_server.len = cm_entry->x_server.maxlen = destaddr->len; 20860Sstevel@tonic-gate 20870Sstevel@tonic-gate cm_entry->x_state_flags = X_THREAD; 20880Sstevel@tonic-gate cm_entry->x_ref = 1; 20890Sstevel@tonic-gate cm_entry->x_family = addrfmly; 20900Sstevel@tonic-gate cm_entry->x_rdev = device; 20910Sstevel@tonic-gate cm_entry->x_zoneid = zoneid; 20920Sstevel@tonic-gate mutex_init(&cm_entry->x_lock, NULL, MUTEX_DEFAULT, NULL); 20930Sstevel@tonic-gate cv_init(&cm_entry->x_cv, NULL, CV_DEFAULT, NULL); 20940Sstevel@tonic-gate cv_init(&cm_entry->x_conn_cv, NULL, CV_DEFAULT, NULL); 20950Sstevel@tonic-gate cv_init(&cm_entry->x_dis_cv, NULL, CV_DEFAULT, NULL); 20960Sstevel@tonic-gate 20970Sstevel@tonic-gate /* 20980Sstevel@tonic-gate * Note that we add this partially initialized entry to the 20990Sstevel@tonic-gate * connection list. This is so that we don't have connections to 21000Sstevel@tonic-gate * the same server. 21010Sstevel@tonic-gate * 21020Sstevel@tonic-gate * Note that x_src is not initialized at this point. This is because 21030Sstevel@tonic-gate * retryaddr might be NULL in which case x_src is whatever 21040Sstevel@tonic-gate * t_kbind/bindresvport gives us. If another thread wants a 21050Sstevel@tonic-gate * connection to the same server, seemingly we have an issue, but we 21060Sstevel@tonic-gate * don't. If the other thread comes in with retryaddr == NULL, then it 21070Sstevel@tonic-gate * will never look at x_src, and it will end up waiting in 21080Sstevel@tonic-gate * connmgr_cwait() for the first thread to finish the connection 21090Sstevel@tonic-gate * attempt. If the other thread comes in with retryaddr != NULL, then 21100Sstevel@tonic-gate * that means there was a request sent on a connection, in which case 21110Sstevel@tonic-gate * the the connection should already exist. Thus the first thread 21120Sstevel@tonic-gate * never gets here ... it finds the connection it its server in the 21130Sstevel@tonic-gate * connection list. 21140Sstevel@tonic-gate * 21150Sstevel@tonic-gate * But even if theory is wrong, in the retryaddr != NULL case, the 2nd 21160Sstevel@tonic-gate * thread will skip us because x_src.len == 0. 21170Sstevel@tonic-gate */ 21180Sstevel@tonic-gate cm_entry->x_next = cm_hd; 21190Sstevel@tonic-gate cm_hd = cm_entry; 21200Sstevel@tonic-gate mutex_exit(&connmgr_lock); 21210Sstevel@tonic-gate 21220Sstevel@tonic-gate /* 21230Sstevel@tonic-gate * Either we didn't find an entry to the server of interest, or we 21240Sstevel@tonic-gate * don't have the maximum number of connections to that server - 21250Sstevel@tonic-gate * create a new connection. 21260Sstevel@tonic-gate */ 21270Sstevel@tonic-gate RPCLOG0(8, "connmgr_get: creating new connection\n"); 21280Sstevel@tonic-gate rpcerr->re_status = RPC_TLIERROR; 21290Sstevel@tonic-gate 21301676Sjpk i = t_kopen(NULL, device, FREAD|FWRITE|FNDELAY, &tiptr, zone_kcred()); 21310Sstevel@tonic-gate if (i) { 21320Sstevel@tonic-gate RPCLOG(1, "connmgr_get: can't open cots device, error %d\n", i); 21330Sstevel@tonic-gate rpcerr->re_errno = i; 21340Sstevel@tonic-gate connmgr_cancelconn(cm_entry); 21350Sstevel@tonic-gate return (NULL); 21360Sstevel@tonic-gate } 21370Sstevel@tonic-gate rpc_poptimod(tiptr->fp->f_vnode); 21380Sstevel@tonic-gate 21390Sstevel@tonic-gate if (i = strioctl(tiptr->fp->f_vnode, I_PUSH, (intptr_t)"rpcmod", 0, 21406403Sgt29601 K_TO_K, kcred, &retval)) { 21410Sstevel@tonic-gate RPCLOG(1, "connmgr_get: can't push cots module, %d\n", i); 21420Sstevel@tonic-gate (void) t_kclose(tiptr, 1); 21430Sstevel@tonic-gate rpcerr->re_errno = i; 21440Sstevel@tonic-gate connmgr_cancelconn(cm_entry); 21450Sstevel@tonic-gate return (NULL); 21460Sstevel@tonic-gate } 21470Sstevel@tonic-gate 21480Sstevel@tonic-gate if (i = strioctl(tiptr->fp->f_vnode, RPC_CLIENT, 0, 0, K_TO_K, 21496403Sgt29601 kcred, &retval)) { 21500Sstevel@tonic-gate RPCLOG(1, "connmgr_get: can't set client status with cots " 21510Sstevel@tonic-gate "module, %d\n", i); 21520Sstevel@tonic-gate (void) t_kclose(tiptr, 1); 21530Sstevel@tonic-gate rpcerr->re_errno = i; 21540Sstevel@tonic-gate connmgr_cancelconn(cm_entry); 21550Sstevel@tonic-gate return (NULL); 21560Sstevel@tonic-gate } 21570Sstevel@tonic-gate 21580Sstevel@tonic-gate mutex_enter(&connmgr_lock); 21590Sstevel@tonic-gate 21600Sstevel@tonic-gate wq = tiptr->fp->f_vnode->v_stream->sd_wrq->q_next; 21610Sstevel@tonic-gate cm_entry->x_wq = wq; 21620Sstevel@tonic-gate 21630Sstevel@tonic-gate mutex_exit(&connmgr_lock); 21640Sstevel@tonic-gate 21650Sstevel@tonic-gate if (i = strioctl(tiptr->fp->f_vnode, I_PUSH, (intptr_t)"timod", 0, 21666403Sgt29601 K_TO_K, kcred, &retval)) { 21670Sstevel@tonic-gate RPCLOG(1, "connmgr_get: can't push timod, %d\n", i); 21680Sstevel@tonic-gate (void) t_kclose(tiptr, 1); 21690Sstevel@tonic-gate rpcerr->re_errno = i; 21700Sstevel@tonic-gate connmgr_cancelconn(cm_entry); 21710Sstevel@tonic-gate return (NULL); 21720Sstevel@tonic-gate } 21730Sstevel@tonic-gate 21740Sstevel@tonic-gate /* 21750Sstevel@tonic-gate * If the caller has not specified reserved port usage then 21760Sstevel@tonic-gate * take the system default. 21770Sstevel@tonic-gate */ 21780Sstevel@tonic-gate if (useresvport == -1) 21790Sstevel@tonic-gate useresvport = clnt_cots_do_bindresvport; 21800Sstevel@tonic-gate 21810Sstevel@tonic-gate if ((useresvport || retryaddr != NULL) && 21820Sstevel@tonic-gate (addrfmly == AF_INET || addrfmly == AF_INET6)) { 21830Sstevel@tonic-gate bool_t alloc_src = FALSE; 21840Sstevel@tonic-gate 21850Sstevel@tonic-gate if (srcaddr->len != destaddr->len) { 21860Sstevel@tonic-gate kmem_free(srcaddr->buf, srcaddr->maxlen); 21870Sstevel@tonic-gate srcaddr->buf = kmem_zalloc(destaddr->len, KM_SLEEP); 21880Sstevel@tonic-gate srcaddr->maxlen = destaddr->len; 21890Sstevel@tonic-gate srcaddr->len = destaddr->len; 21900Sstevel@tonic-gate alloc_src = TRUE; 21910Sstevel@tonic-gate } 21920Sstevel@tonic-gate 21930Sstevel@tonic-gate if ((i = bindresvport(tiptr, retryaddr, srcaddr, TRUE)) != 0) { 21940Sstevel@tonic-gate (void) t_kclose(tiptr, 1); 21950Sstevel@tonic-gate RPCLOG(1, "connmgr_get: couldn't bind, retryaddr: " 21966403Sgt29601 "%p\n", (void *)retryaddr); 21970Sstevel@tonic-gate 21980Sstevel@tonic-gate /* 21990Sstevel@tonic-gate * 1225408: If we allocated a source address, then it 22000Sstevel@tonic-gate * is either garbage or all zeroes. In that case 22010Sstevel@tonic-gate * we need to clear srcaddr. 22020Sstevel@tonic-gate */ 22030Sstevel@tonic-gate if (alloc_src == TRUE) { 22040Sstevel@tonic-gate kmem_free(srcaddr->buf, srcaddr->maxlen); 22050Sstevel@tonic-gate srcaddr->maxlen = srcaddr->len = 0; 22060Sstevel@tonic-gate srcaddr->buf = NULL; 22070Sstevel@tonic-gate } 22080Sstevel@tonic-gate rpcerr->re_errno = i; 22090Sstevel@tonic-gate connmgr_cancelconn(cm_entry); 22100Sstevel@tonic-gate return (NULL); 22110Sstevel@tonic-gate } 22120Sstevel@tonic-gate } else { 22130Sstevel@tonic-gate if ((i = t_kbind(tiptr, NULL, NULL)) != 0) { 22140Sstevel@tonic-gate RPCLOG(1, "clnt_cots_kcreate: t_kbind: %d\n", i); 22150Sstevel@tonic-gate (void) t_kclose(tiptr, 1); 22160Sstevel@tonic-gate rpcerr->re_errno = i; 22170Sstevel@tonic-gate connmgr_cancelconn(cm_entry); 22180Sstevel@tonic-gate return (NULL); 22190Sstevel@tonic-gate } 22200Sstevel@tonic-gate } 22210Sstevel@tonic-gate 22220Sstevel@tonic-gate { 22230Sstevel@tonic-gate /* 22240Sstevel@tonic-gate * Keep the kernel stack lean. Don't move this call 22250Sstevel@tonic-gate * declaration to the top of this function because a 22260Sstevel@tonic-gate * call is declared in connmgr_wrapconnect() 22270Sstevel@tonic-gate */ 22280Sstevel@tonic-gate calllist_t call; 22290Sstevel@tonic-gate 22300Sstevel@tonic-gate bzero(&call, sizeof (call)); 22310Sstevel@tonic-gate cv_init(&call.call_cv, NULL, CV_DEFAULT, NULL); 22320Sstevel@tonic-gate 22330Sstevel@tonic-gate /* 22340Sstevel@tonic-gate * This is a bound end-point so don't close it's stream. 22350Sstevel@tonic-gate */ 22360Sstevel@tonic-gate connected = connmgr_connect(cm_entry, wq, destaddr, addrfmly, 22378778SErik.Nordmark@Sun.COM &call, &tidu_size, FALSE, waitp, nosignal, cr); 22380Sstevel@tonic-gate *rpcerr = call.call_err; 22390Sstevel@tonic-gate cv_destroy(&call.call_cv); 22400Sstevel@tonic-gate 22410Sstevel@tonic-gate } 22420Sstevel@tonic-gate 22430Sstevel@tonic-gate mutex_enter(&connmgr_lock); 22440Sstevel@tonic-gate 22450Sstevel@tonic-gate /* 22460Sstevel@tonic-gate * Set up a transport entry in the connection manager's list. 22470Sstevel@tonic-gate */ 22480Sstevel@tonic-gate cm_entry->x_src.buf = kmem_zalloc(srcaddr->len, KM_SLEEP); 22490Sstevel@tonic-gate bcopy(srcaddr->buf, cm_entry->x_src.buf, srcaddr->len); 22500Sstevel@tonic-gate cm_entry->x_src.len = cm_entry->x_src.maxlen = srcaddr->len; 22510Sstevel@tonic-gate 22520Sstevel@tonic-gate cm_entry->x_tiptr = tiptr; 22530Sstevel@tonic-gate cm_entry->x_time = lbolt; 22540Sstevel@tonic-gate 22550Sstevel@tonic-gate if (tiptr->tp_info.servtype == T_COTS_ORD) 22560Sstevel@tonic-gate cm_entry->x_ordrel = TRUE; 22570Sstevel@tonic-gate else 22580Sstevel@tonic-gate cm_entry->x_ordrel = FALSE; 22590Sstevel@tonic-gate 22600Sstevel@tonic-gate cm_entry->x_tidu_size = tidu_size; 22610Sstevel@tonic-gate 22624457Svv149972 if (cm_entry->x_early_disc) { 22634457Svv149972 /* 22644457Svv149972 * We need to check if a disconnect request has come 22654457Svv149972 * while we are connected, if so, then we need to 22664457Svv149972 * set rpcerr->re_status appropriately before returning 22674457Svv149972 * NULL to caller. 22684457Svv149972 */ 22694457Svv149972 if (rpcerr->re_status == RPC_SUCCESS) 22704457Svv149972 rpcerr->re_status = RPC_XPRTFAILED; 22710Sstevel@tonic-gate cm_entry->x_connected = FALSE; 22724457Svv149972 } else 22730Sstevel@tonic-gate cm_entry->x_connected = connected; 22740Sstevel@tonic-gate 22750Sstevel@tonic-gate /* 22760Sstevel@tonic-gate * There could be a discrepancy here such that 22770Sstevel@tonic-gate * x_early_disc is TRUE yet connected is TRUE as well 22780Sstevel@tonic-gate * and the connection is actually connected. In that case 22790Sstevel@tonic-gate * lets be conservative and declare the connection as not 22800Sstevel@tonic-gate * connected. 22810Sstevel@tonic-gate */ 22820Sstevel@tonic-gate cm_entry->x_early_disc = FALSE; 22830Sstevel@tonic-gate cm_entry->x_needdis = (cm_entry->x_connected == FALSE); 22840Sstevel@tonic-gate cm_entry->x_ctime = lbolt; 22850Sstevel@tonic-gate 22860Sstevel@tonic-gate /* 22870Sstevel@tonic-gate * Notify any threads waiting that the connection attempt is done. 22880Sstevel@tonic-gate */ 22890Sstevel@tonic-gate cm_entry->x_thread = FALSE; 22900Sstevel@tonic-gate cv_broadcast(&cm_entry->x_conn_cv); 22910Sstevel@tonic-gate 22920Sstevel@tonic-gate if (cm_entry->x_connected == FALSE) { 22934457Svv149972 mutex_exit(&connmgr_lock); 22940Sstevel@tonic-gate connmgr_release(cm_entry); 22950Sstevel@tonic-gate return (NULL); 22960Sstevel@tonic-gate } 22974457Svv149972 22984457Svv149972 mutex_exit(&connmgr_lock); 22994457Svv149972 23000Sstevel@tonic-gate return (cm_entry); 23010Sstevel@tonic-gate } 23020Sstevel@tonic-gate 23030Sstevel@tonic-gate /* 23040Sstevel@tonic-gate * Keep the cm_xprt entry on the connecton list when making a connection. This 23050Sstevel@tonic-gate * is to prevent multiple connections to a slow server from appearing. 23060Sstevel@tonic-gate * We use the bit field x_thread to tell if a thread is doing a connection 23070Sstevel@tonic-gate * which keeps other interested threads from messing with connection. 23080Sstevel@tonic-gate * Those other threads just wait if x_thread is set. 23090Sstevel@tonic-gate * 23100Sstevel@tonic-gate * If x_thread is not set, then we do the actual work of connecting via 23110Sstevel@tonic-gate * connmgr_connect(). 23120Sstevel@tonic-gate * 23130Sstevel@tonic-gate * mutex convention: called with connmgr_lock held, returns with it released. 23140Sstevel@tonic-gate */ 23150Sstevel@tonic-gate static struct cm_xprt * 23160Sstevel@tonic-gate connmgr_wrapconnect( 23170Sstevel@tonic-gate struct cm_xprt *cm_entry, 23180Sstevel@tonic-gate const struct timeval *waitp, 23190Sstevel@tonic-gate struct netbuf *destaddr, 23200Sstevel@tonic-gate int addrfmly, 23210Sstevel@tonic-gate struct netbuf *srcaddr, 23220Sstevel@tonic-gate struct rpc_err *rpcerr, 23230Sstevel@tonic-gate bool_t reconnect, 23248778SErik.Nordmark@Sun.COM bool_t nosignal, 23258778SErik.Nordmark@Sun.COM cred_t *cr) 23260Sstevel@tonic-gate { 23270Sstevel@tonic-gate ASSERT(MUTEX_HELD(&connmgr_lock)); 23280Sstevel@tonic-gate /* 23290Sstevel@tonic-gate * Hold this entry as we are about to drop connmgr_lock. 23300Sstevel@tonic-gate */ 23310Sstevel@tonic-gate CONN_HOLD(cm_entry); 23320Sstevel@tonic-gate 23330Sstevel@tonic-gate /* 23340Sstevel@tonic-gate * If there is a thread already making a connection for us, then 23350Sstevel@tonic-gate * wait for it to complete the connection. 23360Sstevel@tonic-gate */ 23370Sstevel@tonic-gate if (cm_entry->x_thread == TRUE) { 23380Sstevel@tonic-gate rpcerr->re_status = connmgr_cwait(cm_entry, waitp, nosignal); 23390Sstevel@tonic-gate 23400Sstevel@tonic-gate if (rpcerr->re_status != RPC_SUCCESS) { 23410Sstevel@tonic-gate mutex_exit(&connmgr_lock); 23420Sstevel@tonic-gate connmgr_release(cm_entry); 23430Sstevel@tonic-gate return (NULL); 23440Sstevel@tonic-gate } 23450Sstevel@tonic-gate } else { 23460Sstevel@tonic-gate bool_t connected; 23470Sstevel@tonic-gate calllist_t call; 23480Sstevel@tonic-gate 23490Sstevel@tonic-gate cm_entry->x_thread = TRUE; 23500Sstevel@tonic-gate 23510Sstevel@tonic-gate while (cm_entry->x_needrel == TRUE) { 23520Sstevel@tonic-gate cm_entry->x_needrel = FALSE; 23530Sstevel@tonic-gate 23540Sstevel@tonic-gate connmgr_sndrel(cm_entry); 23550Sstevel@tonic-gate delay(drv_usectohz(1000000)); 23560Sstevel@tonic-gate 23570Sstevel@tonic-gate mutex_enter(&connmgr_lock); 23580Sstevel@tonic-gate } 23590Sstevel@tonic-gate 23600Sstevel@tonic-gate /* 23610Sstevel@tonic-gate * If we need to send a T_DISCON_REQ, send one. 23620Sstevel@tonic-gate */ 23630Sstevel@tonic-gate connmgr_dis_and_wait(cm_entry); 23640Sstevel@tonic-gate 23650Sstevel@tonic-gate mutex_exit(&connmgr_lock); 23660Sstevel@tonic-gate 23670Sstevel@tonic-gate bzero(&call, sizeof (call)); 23680Sstevel@tonic-gate cv_init(&call.call_cv, NULL, CV_DEFAULT, NULL); 23690Sstevel@tonic-gate 23700Sstevel@tonic-gate connected = connmgr_connect(cm_entry, cm_entry->x_wq, 23716403Sgt29601 destaddr, addrfmly, &call, &cm_entry->x_tidu_size, 23728778SErik.Nordmark@Sun.COM reconnect, waitp, nosignal, cr); 23730Sstevel@tonic-gate 23740Sstevel@tonic-gate *rpcerr = call.call_err; 23750Sstevel@tonic-gate cv_destroy(&call.call_cv); 23760Sstevel@tonic-gate 23770Sstevel@tonic-gate mutex_enter(&connmgr_lock); 23780Sstevel@tonic-gate 23790Sstevel@tonic-gate 23804457Svv149972 if (cm_entry->x_early_disc) { 23814457Svv149972 /* 23824457Svv149972 * We need to check if a disconnect request has come 23834457Svv149972 * while we are connected, if so, then we need to 23844457Svv149972 * set rpcerr->re_status appropriately before returning 23854457Svv149972 * NULL to caller. 23864457Svv149972 */ 23874457Svv149972 if (rpcerr->re_status == RPC_SUCCESS) 23884457Svv149972 rpcerr->re_status = RPC_XPRTFAILED; 23890Sstevel@tonic-gate cm_entry->x_connected = FALSE; 23904457Svv149972 } else 23910Sstevel@tonic-gate cm_entry->x_connected = connected; 23920Sstevel@tonic-gate 23930Sstevel@tonic-gate /* 23940Sstevel@tonic-gate * There could be a discrepancy here such that 23950Sstevel@tonic-gate * x_early_disc is TRUE yet connected is TRUE as well 23960Sstevel@tonic-gate * and the connection is actually connected. In that case 23970Sstevel@tonic-gate * lets be conservative and declare the connection as not 23980Sstevel@tonic-gate * connected. 23990Sstevel@tonic-gate */ 24000Sstevel@tonic-gate 24010Sstevel@tonic-gate cm_entry->x_early_disc = FALSE; 24020Sstevel@tonic-gate cm_entry->x_needdis = (cm_entry->x_connected == FALSE); 24030Sstevel@tonic-gate 24040Sstevel@tonic-gate 24050Sstevel@tonic-gate /* 24060Sstevel@tonic-gate * connmgr_connect() may have given up before the connection 24070Sstevel@tonic-gate * actually timed out. So ensure that before the next 24080Sstevel@tonic-gate * connection attempt we do a disconnect. 24090Sstevel@tonic-gate */ 24100Sstevel@tonic-gate cm_entry->x_ctime = lbolt; 24110Sstevel@tonic-gate cm_entry->x_thread = FALSE; 24120Sstevel@tonic-gate 24130Sstevel@tonic-gate cv_broadcast(&cm_entry->x_conn_cv); 24140Sstevel@tonic-gate 24150Sstevel@tonic-gate if (cm_entry->x_connected == FALSE) { 24160Sstevel@tonic-gate mutex_exit(&connmgr_lock); 24170Sstevel@tonic-gate connmgr_release(cm_entry); 24180Sstevel@tonic-gate return (NULL); 24190Sstevel@tonic-gate } 24200Sstevel@tonic-gate } 24210Sstevel@tonic-gate 24220Sstevel@tonic-gate if (srcaddr != NULL) { 24230Sstevel@tonic-gate /* 24240Sstevel@tonic-gate * Copy into the handle the 24250Sstevel@tonic-gate * source address of the 24260Sstevel@tonic-gate * connection, which we will use 24270Sstevel@tonic-gate * in case of a later retry. 24280Sstevel@tonic-gate */ 24290Sstevel@tonic-gate if (srcaddr->len != cm_entry->x_src.len) { 24300Sstevel@tonic-gate if (srcaddr->maxlen > 0) 24310Sstevel@tonic-gate kmem_free(srcaddr->buf, srcaddr->maxlen); 24320Sstevel@tonic-gate srcaddr->buf = kmem_zalloc(cm_entry->x_src.len, 24330Sstevel@tonic-gate KM_SLEEP); 24340Sstevel@tonic-gate srcaddr->maxlen = srcaddr->len = 24350Sstevel@tonic-gate cm_entry->x_src.len; 24360Sstevel@tonic-gate } 24370Sstevel@tonic-gate bcopy(cm_entry->x_src.buf, srcaddr->buf, srcaddr->len); 24380Sstevel@tonic-gate } 24390Sstevel@tonic-gate cm_entry->x_time = lbolt; 24400Sstevel@tonic-gate mutex_exit(&connmgr_lock); 24410Sstevel@tonic-gate return (cm_entry); 24420Sstevel@tonic-gate } 24430Sstevel@tonic-gate 24440Sstevel@tonic-gate /* 24450Sstevel@tonic-gate * If we need to send a T_DISCON_REQ, send one. 24460Sstevel@tonic-gate */ 24470Sstevel@tonic-gate static void 24480Sstevel@tonic-gate connmgr_dis_and_wait(struct cm_xprt *cm_entry) 24490Sstevel@tonic-gate { 24500Sstevel@tonic-gate ASSERT(MUTEX_HELD(&connmgr_lock)); 24510Sstevel@tonic-gate for (;;) { 24520Sstevel@tonic-gate while (cm_entry->x_needdis == TRUE) { 24530Sstevel@tonic-gate RPCLOG(8, "connmgr_dis_and_wait: need " 24546403Sgt29601 "T_DISCON_REQ for connection 0x%p\n", 24556403Sgt29601 (void *)cm_entry); 24560Sstevel@tonic-gate cm_entry->x_needdis = FALSE; 24570Sstevel@tonic-gate cm_entry->x_waitdis = TRUE; 24580Sstevel@tonic-gate 24590Sstevel@tonic-gate connmgr_snddis(cm_entry); 24600Sstevel@tonic-gate 24610Sstevel@tonic-gate mutex_enter(&connmgr_lock); 24620Sstevel@tonic-gate } 24630Sstevel@tonic-gate 24640Sstevel@tonic-gate if (cm_entry->x_waitdis == TRUE) { 24650Sstevel@tonic-gate clock_t curlbolt; 24660Sstevel@tonic-gate clock_t timout; 24670Sstevel@tonic-gate 24680Sstevel@tonic-gate RPCLOG(8, "connmgr_dis_and_wait waiting for " 24696403Sgt29601 "T_DISCON_REQ's ACK for connection %p\n", 24706403Sgt29601 (void *)cm_entry); 24710Sstevel@tonic-gate curlbolt = ddi_get_lbolt(); 24720Sstevel@tonic-gate 24730Sstevel@tonic-gate timout = clnt_cots_min_conntout * 24746403Sgt29601 drv_usectohz(1000000) + curlbolt; 24750Sstevel@tonic-gate 24760Sstevel@tonic-gate /* 24770Sstevel@tonic-gate * The TPI spec says that the T_DISCON_REQ 24780Sstevel@tonic-gate * will get acknowledged, but in practice 24790Sstevel@tonic-gate * the ACK may never get sent. So don't 24800Sstevel@tonic-gate * block forever. 24810Sstevel@tonic-gate */ 24820Sstevel@tonic-gate (void) cv_timedwait(&cm_entry->x_dis_cv, 24836403Sgt29601 &connmgr_lock, timout); 24840Sstevel@tonic-gate } 24850Sstevel@tonic-gate /* 24860Sstevel@tonic-gate * If we got the ACK, break. If we didn't, 24870Sstevel@tonic-gate * then send another T_DISCON_REQ. 24880Sstevel@tonic-gate */ 24890Sstevel@tonic-gate if (cm_entry->x_waitdis == FALSE) { 24900Sstevel@tonic-gate break; 24910Sstevel@tonic-gate } else { 24920Sstevel@tonic-gate RPCLOG(8, "connmgr_dis_and_wait: did" 24936403Sgt29601 "not get T_DISCON_REQ's ACK for " 24946403Sgt29601 "connection %p\n", (void *)cm_entry); 24950Sstevel@tonic-gate cm_entry->x_needdis = TRUE; 24960Sstevel@tonic-gate } 24970Sstevel@tonic-gate } 24980Sstevel@tonic-gate } 24990Sstevel@tonic-gate 25000Sstevel@tonic-gate static void 25010Sstevel@tonic-gate connmgr_cancelconn(struct cm_xprt *cm_entry) 25020Sstevel@tonic-gate { 25030Sstevel@tonic-gate /* 25040Sstevel@tonic-gate * Mark the connection table entry as dead; the next thread that 25050Sstevel@tonic-gate * goes through connmgr_release() will notice this and deal with it. 25060Sstevel@tonic-gate */ 25070Sstevel@tonic-gate mutex_enter(&connmgr_lock); 25080Sstevel@tonic-gate cm_entry->x_dead = TRUE; 25090Sstevel@tonic-gate 25100Sstevel@tonic-gate /* 25110Sstevel@tonic-gate * Notify any threads waiting for the connection that it isn't 25120Sstevel@tonic-gate * going to happen. 25130Sstevel@tonic-gate */ 25140Sstevel@tonic-gate cm_entry->x_thread = FALSE; 25150Sstevel@tonic-gate cv_broadcast(&cm_entry->x_conn_cv); 25160Sstevel@tonic-gate mutex_exit(&connmgr_lock); 25170Sstevel@tonic-gate 25180Sstevel@tonic-gate connmgr_release(cm_entry); 25190Sstevel@tonic-gate } 25200Sstevel@tonic-gate 25210Sstevel@tonic-gate static void 25220Sstevel@tonic-gate connmgr_close(struct cm_xprt *cm_entry) 25230Sstevel@tonic-gate { 25240Sstevel@tonic-gate mutex_enter(&cm_entry->x_lock); 25250Sstevel@tonic-gate while (cm_entry->x_ref != 0) { 25260Sstevel@tonic-gate /* 25270Sstevel@tonic-gate * Must be a noninterruptible wait. 25280Sstevel@tonic-gate */ 25290Sstevel@tonic-gate cv_wait(&cm_entry->x_cv, &cm_entry->x_lock); 25300Sstevel@tonic-gate } 25310Sstevel@tonic-gate 25320Sstevel@tonic-gate if (cm_entry->x_tiptr != NULL) 25330Sstevel@tonic-gate (void) t_kclose(cm_entry->x_tiptr, 1); 25340Sstevel@tonic-gate 25350Sstevel@tonic-gate mutex_exit(&cm_entry->x_lock); 25360Sstevel@tonic-gate if (cm_entry->x_ksp != NULL) { 25370Sstevel@tonic-gate mutex_enter(&connmgr_lock); 25380Sstevel@tonic-gate cm_entry->x_ksp->ks_private = NULL; 25390Sstevel@tonic-gate mutex_exit(&connmgr_lock); 25400Sstevel@tonic-gate 25410Sstevel@tonic-gate /* 25420Sstevel@tonic-gate * Must free the buffer we allocated for the 25430Sstevel@tonic-gate * server address in the update function 25440Sstevel@tonic-gate */ 25450Sstevel@tonic-gate if (((struct cm_kstat_xprt *)(cm_entry->x_ksp->ks_data))-> 2546457Sbmc x_server.value.str.addr.ptr != NULL) 25470Sstevel@tonic-gate kmem_free(((struct cm_kstat_xprt *)(cm_entry->x_ksp-> 2548457Sbmc ks_data))->x_server.value.str.addr.ptr, 25496403Sgt29601 INET6_ADDRSTRLEN); 25500Sstevel@tonic-gate kmem_free(cm_entry->x_ksp->ks_data, 25516403Sgt29601 cm_entry->x_ksp->ks_data_size); 25520Sstevel@tonic-gate kstat_delete(cm_entry->x_ksp); 25530Sstevel@tonic-gate } 25540Sstevel@tonic-gate 25550Sstevel@tonic-gate mutex_destroy(&cm_entry->x_lock); 25560Sstevel@tonic-gate cv_destroy(&cm_entry->x_cv); 25570Sstevel@tonic-gate cv_destroy(&cm_entry->x_conn_cv); 25580Sstevel@tonic-gate cv_destroy(&cm_entry->x_dis_cv); 25590Sstevel@tonic-gate 25600Sstevel@tonic-gate if (cm_entry->x_server.buf != NULL) 25610Sstevel@tonic-gate kmem_free(cm_entry->x_server.buf, cm_entry->x_server.maxlen); 25620Sstevel@tonic-gate if (cm_entry->x_src.buf != NULL) 25630Sstevel@tonic-gate kmem_free(cm_entry->x_src.buf, cm_entry->x_src.maxlen); 25640Sstevel@tonic-gate kmem_free(cm_entry, sizeof (struct cm_xprt)); 25650Sstevel@tonic-gate } 25660Sstevel@tonic-gate 25670Sstevel@tonic-gate /* 25680Sstevel@tonic-gate * Called by KRPC after sending the call message to release the connection 25690Sstevel@tonic-gate * it was using. 25700Sstevel@tonic-gate */ 25710Sstevel@tonic-gate static void 25720Sstevel@tonic-gate connmgr_release(struct cm_xprt *cm_entry) 25730Sstevel@tonic-gate { 25740Sstevel@tonic-gate mutex_enter(&cm_entry->x_lock); 25750Sstevel@tonic-gate cm_entry->x_ref--; 25760Sstevel@tonic-gate if (cm_entry->x_ref == 0) 25770Sstevel@tonic-gate cv_signal(&cm_entry->x_cv); 25780Sstevel@tonic-gate mutex_exit(&cm_entry->x_lock); 25790Sstevel@tonic-gate } 25800Sstevel@tonic-gate 25810Sstevel@tonic-gate /* 2582*10004Sdai.ngo@sun.com * Set TCP receive and xmit buffer size for RPC connections. 2583*10004Sdai.ngo@sun.com */ 2584*10004Sdai.ngo@sun.com static bool_t 2585*10004Sdai.ngo@sun.com connmgr_setbufsz(calllist_t *e, queue_t *wq, cred_t *cr) 2586*10004Sdai.ngo@sun.com { 2587*10004Sdai.ngo@sun.com int ok = FALSE; 2588*10004Sdai.ngo@sun.com int val; 2589*10004Sdai.ngo@sun.com 2590*10004Sdai.ngo@sun.com if (rpc_default_tcp_bufsz) 2591*10004Sdai.ngo@sun.com return (FALSE); 2592*10004Sdai.ngo@sun.com 2593*10004Sdai.ngo@sun.com /* 2594*10004Sdai.ngo@sun.com * Only set new buffer size if it's larger than the system 2595*10004Sdai.ngo@sun.com * default buffer size. If smaller buffer size is needed 2596*10004Sdai.ngo@sun.com * then use /etc/system to set rpc_default_tcp_bufsz to 1. 2597*10004Sdai.ngo@sun.com */ 2598*10004Sdai.ngo@sun.com ok = connmgr_getopt_int(wq, SOL_SOCKET, SO_RCVBUF, &val, e, cr); 2599*10004Sdai.ngo@sun.com if ((ok == TRUE) && (val < rpc_send_bufsz)) { 2600*10004Sdai.ngo@sun.com ok = connmgr_setopt_int(wq, SOL_SOCKET, SO_RCVBUF, 2601*10004Sdai.ngo@sun.com rpc_send_bufsz, e, cr); 2602*10004Sdai.ngo@sun.com DTRACE_PROBE2(krpc__i__connmgr_rcvbufsz, 2603*10004Sdai.ngo@sun.com int, ok, calllist_t *, e); 2604*10004Sdai.ngo@sun.com } 2605*10004Sdai.ngo@sun.com 2606*10004Sdai.ngo@sun.com ok = connmgr_getopt_int(wq, SOL_SOCKET, SO_SNDBUF, &val, e, cr); 2607*10004Sdai.ngo@sun.com if ((ok == TRUE) && (val < rpc_recv_bufsz)) { 2608*10004Sdai.ngo@sun.com ok = connmgr_setopt_int(wq, SOL_SOCKET, SO_SNDBUF, 2609*10004Sdai.ngo@sun.com rpc_recv_bufsz, e, cr); 2610*10004Sdai.ngo@sun.com DTRACE_PROBE2(krpc__i__connmgr_sndbufsz, 2611*10004Sdai.ngo@sun.com int, ok, calllist_t *, e); 2612*10004Sdai.ngo@sun.com } 2613*10004Sdai.ngo@sun.com return (TRUE); 2614*10004Sdai.ngo@sun.com } 2615*10004Sdai.ngo@sun.com 2616*10004Sdai.ngo@sun.com /* 26170Sstevel@tonic-gate * Given an open stream, connect to the remote. Returns true if connected, 26180Sstevel@tonic-gate * false otherwise. 26190Sstevel@tonic-gate */ 26200Sstevel@tonic-gate static bool_t 26210Sstevel@tonic-gate connmgr_connect( 26220Sstevel@tonic-gate struct cm_xprt *cm_entry, 26230Sstevel@tonic-gate queue_t *wq, 26240Sstevel@tonic-gate struct netbuf *addr, 26250Sstevel@tonic-gate int addrfmly, 26260Sstevel@tonic-gate calllist_t *e, 26270Sstevel@tonic-gate int *tidu_ptr, 26280Sstevel@tonic-gate bool_t reconnect, 26290Sstevel@tonic-gate const struct timeval *waitp, 26308778SErik.Nordmark@Sun.COM bool_t nosignal, 26318778SErik.Nordmark@Sun.COM cred_t *cr) 26320Sstevel@tonic-gate { 26330Sstevel@tonic-gate mblk_t *mp; 26340Sstevel@tonic-gate struct T_conn_req *tcr; 26350Sstevel@tonic-gate struct T_info_ack *tinfo; 26360Sstevel@tonic-gate int interrupted, error; 26370Sstevel@tonic-gate int tidu_size, kstat_instance; 26380Sstevel@tonic-gate 26390Sstevel@tonic-gate /* if it's a reconnect, flush any lingering data messages */ 26400Sstevel@tonic-gate if (reconnect) 26410Sstevel@tonic-gate (void) putctl1(wq, M_FLUSH, FLUSHRW); 26420Sstevel@tonic-gate 26438778SErik.Nordmark@Sun.COM /* 26448778SErik.Nordmark@Sun.COM * Note: if the receiver uses SCM_UCRED/getpeerucred the pid will 26458778SErik.Nordmark@Sun.COM * appear as -1. 26468778SErik.Nordmark@Sun.COM */ 26478778SErik.Nordmark@Sun.COM mp = allocb_cred(sizeof (*tcr) + addr->len, cr, NOPID); 26480Sstevel@tonic-gate if (mp == NULL) { 26490Sstevel@tonic-gate /* 26500Sstevel@tonic-gate * This is unfortunate, but we need to look up the stats for 26510Sstevel@tonic-gate * this zone to increment the "memory allocation failed" 26520Sstevel@tonic-gate * counter. curproc->p_zone is safe since we're initiating a 26530Sstevel@tonic-gate * connection and not in some strange streams context. 26540Sstevel@tonic-gate */ 26550Sstevel@tonic-gate struct rpcstat *rpcstat; 26560Sstevel@tonic-gate 2657766Scarlsonj rpcstat = zone_getspecific(rpcstat_zone_key, rpc_zone()); 26580Sstevel@tonic-gate ASSERT(rpcstat != NULL); 26590Sstevel@tonic-gate 26600Sstevel@tonic-gate RPCLOG0(1, "connmgr_connect: cannot alloc mp for " 26610Sstevel@tonic-gate "sending conn request\n"); 26620Sstevel@tonic-gate COTSRCSTAT_INCR(rpcstat->rpc_cots_client, rcnomem); 26630Sstevel@tonic-gate e->call_status = RPC_SYSTEMERROR; 26640Sstevel@tonic-gate e->call_reason = ENOSR; 26650Sstevel@tonic-gate return (FALSE); 26660Sstevel@tonic-gate } 26670Sstevel@tonic-gate 2668*10004Sdai.ngo@sun.com /* Set TCP buffer size for RPC connections if needed */ 2669*10004Sdai.ngo@sun.com if (addrfmly == AF_INET || addrfmly == AF_INET6) 2670*10004Sdai.ngo@sun.com (void) connmgr_setbufsz(e, wq, cr); 2671*10004Sdai.ngo@sun.com 26720Sstevel@tonic-gate mp->b_datap->db_type = M_PROTO; 26730Sstevel@tonic-gate tcr = (struct T_conn_req *)mp->b_rptr; 26740Sstevel@tonic-gate bzero(tcr, sizeof (*tcr)); 26750Sstevel@tonic-gate tcr->PRIM_type = T_CONN_REQ; 26760Sstevel@tonic-gate tcr->DEST_length = addr->len; 26770Sstevel@tonic-gate tcr->DEST_offset = sizeof (struct T_conn_req); 26780Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (*tcr); 26790Sstevel@tonic-gate 26800Sstevel@tonic-gate bcopy(addr->buf, mp->b_wptr, tcr->DEST_length); 26810Sstevel@tonic-gate mp->b_wptr += tcr->DEST_length; 26820Sstevel@tonic-gate 26830Sstevel@tonic-gate RPCLOG(8, "connmgr_connect: sending conn request on queue " 26840Sstevel@tonic-gate "%p", (void *)wq); 26850Sstevel@tonic-gate RPCLOG(8, " call %p\n", (void *)wq); 26860Sstevel@tonic-gate /* 26870Sstevel@tonic-gate * We use the entry in the handle that is normally used for 26880Sstevel@tonic-gate * waiting for RPC replies to wait for the connection accept. 26890Sstevel@tonic-gate */ 26908205SSiddheshwar.Mahesh@Sun.COM if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) { 26918205SSiddheshwar.Mahesh@Sun.COM DTRACE_PROBE(krpc__e__connmgr__connect__cantsend); 26928205SSiddheshwar.Mahesh@Sun.COM freemsg(mp); 26938205SSiddheshwar.Mahesh@Sun.COM return (FALSE); 26948205SSiddheshwar.Mahesh@Sun.COM } 26950Sstevel@tonic-gate 26960Sstevel@tonic-gate mutex_enter(&clnt_pending_lock); 26970Sstevel@tonic-gate 26980Sstevel@tonic-gate /* 26990Sstevel@tonic-gate * We wait for the transport connection to be made, or an 27000Sstevel@tonic-gate * indication that it could not be made. 27010Sstevel@tonic-gate */ 27020Sstevel@tonic-gate interrupted = 0; 27030Sstevel@tonic-gate 27040Sstevel@tonic-gate /* 27050Sstevel@tonic-gate * waitforack should have been called with T_OK_ACK, but the 27060Sstevel@tonic-gate * present implementation needs to be passed T_INFO_ACK to 27070Sstevel@tonic-gate * work correctly. 27080Sstevel@tonic-gate */ 27090Sstevel@tonic-gate error = waitforack(e, T_INFO_ACK, waitp, nosignal); 27100Sstevel@tonic-gate if (error == EINTR) 27110Sstevel@tonic-gate interrupted = 1; 27120Sstevel@tonic-gate if (zone_status_get(curproc->p_zone) >= ZONE_IS_EMPTY) { 27130Sstevel@tonic-gate /* 27140Sstevel@tonic-gate * No time to lose; we essentially have been signaled to 27150Sstevel@tonic-gate * quit. 27160Sstevel@tonic-gate */ 27170Sstevel@tonic-gate interrupted = 1; 27180Sstevel@tonic-gate } 27190Sstevel@tonic-gate #ifdef RPCDEBUG 27200Sstevel@tonic-gate if (error == ETIME) 27210Sstevel@tonic-gate RPCLOG0(8, "connmgr_connect: giving up " 27220Sstevel@tonic-gate "on connection attempt; " 27230Sstevel@tonic-gate "clnt_dispatch notifyconn " 27240Sstevel@tonic-gate "diagnostic 'no one waiting for " 27250Sstevel@tonic-gate "connection' should not be " 27260Sstevel@tonic-gate "unexpected\n"); 27270Sstevel@tonic-gate #endif 27280Sstevel@tonic-gate if (e->call_prev) 27290Sstevel@tonic-gate e->call_prev->call_next = e->call_next; 27300Sstevel@tonic-gate else 27310Sstevel@tonic-gate clnt_pending = e->call_next; 27320Sstevel@tonic-gate if (e->call_next) 27330Sstevel@tonic-gate e->call_next->call_prev = e->call_prev; 27340Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 27350Sstevel@tonic-gate 27360Sstevel@tonic-gate if (e->call_status != RPC_SUCCESS || error != 0) { 27370Sstevel@tonic-gate if (interrupted) 27380Sstevel@tonic-gate e->call_status = RPC_INTR; 27390Sstevel@tonic-gate else if (error == ETIME) 27400Sstevel@tonic-gate e->call_status = RPC_TIMEDOUT; 27419804SGerald.Thornbrugh@Sun.COM else if (error == EPROTO) { 27420Sstevel@tonic-gate e->call_status = RPC_SYSTEMERROR; 27439804SGerald.Thornbrugh@Sun.COM e->call_reason = EPROTO; 27449804SGerald.Thornbrugh@Sun.COM } 27450Sstevel@tonic-gate 27460Sstevel@tonic-gate RPCLOG(8, "connmgr_connect: can't connect, status: " 27470Sstevel@tonic-gate "%s\n", clnt_sperrno(e->call_status)); 27480Sstevel@tonic-gate 27490Sstevel@tonic-gate if (e->call_reply) { 27500Sstevel@tonic-gate freemsg(e->call_reply); 27510Sstevel@tonic-gate e->call_reply = NULL; 27520Sstevel@tonic-gate } 27530Sstevel@tonic-gate 27540Sstevel@tonic-gate return (FALSE); 27550Sstevel@tonic-gate } 27560Sstevel@tonic-gate /* 27570Sstevel@tonic-gate * The result of the "connection accept" is a T_info_ack 27580Sstevel@tonic-gate * in the call_reply field. 27590Sstevel@tonic-gate */ 27600Sstevel@tonic-gate ASSERT(e->call_reply != NULL); 27610Sstevel@tonic-gate mp = e->call_reply; 27620Sstevel@tonic-gate e->call_reply = NULL; 27630Sstevel@tonic-gate tinfo = (struct T_info_ack *)mp->b_rptr; 27640Sstevel@tonic-gate 27650Sstevel@tonic-gate tidu_size = tinfo->TIDU_size; 27660Sstevel@tonic-gate tidu_size -= (tidu_size % BYTES_PER_XDR_UNIT); 27670Sstevel@tonic-gate if (tidu_size > COTS_DEFAULT_ALLOCSIZE || (tidu_size <= 0)) 27680Sstevel@tonic-gate tidu_size = COTS_DEFAULT_ALLOCSIZE; 27690Sstevel@tonic-gate *tidu_ptr = tidu_size; 27700Sstevel@tonic-gate 27710Sstevel@tonic-gate freemsg(mp); 27720Sstevel@tonic-gate 27730Sstevel@tonic-gate /* 27740Sstevel@tonic-gate * Set up the pertinent options. NODELAY is so the transport doesn't 27750Sstevel@tonic-gate * buffer up RPC messages on either end. This may not be valid for 27760Sstevel@tonic-gate * all transports. Failure to set this option is not cause to 27770Sstevel@tonic-gate * bail out so we return success anyway. Note that lack of NODELAY 27780Sstevel@tonic-gate * or some other way to flush the message on both ends will cause 27790Sstevel@tonic-gate * lots of retries and terrible performance. 27800Sstevel@tonic-gate */ 27810Sstevel@tonic-gate if (addrfmly == AF_INET || addrfmly == AF_INET6) { 27828778SErik.Nordmark@Sun.COM (void) connmgr_setopt(wq, IPPROTO_TCP, TCP_NODELAY, e, cr); 27830Sstevel@tonic-gate if (e->call_status == RPC_XPRTFAILED) 27840Sstevel@tonic-gate return (FALSE); 27850Sstevel@tonic-gate } 27860Sstevel@tonic-gate 27870Sstevel@tonic-gate /* 27880Sstevel@tonic-gate * Since we have a connection, we now need to figure out if 27890Sstevel@tonic-gate * we need to create a kstat. If x_ksp is not NULL then we 27900Sstevel@tonic-gate * are reusing a connection and so we do not need to create 27910Sstevel@tonic-gate * another kstat -- lets just return. 27920Sstevel@tonic-gate */ 27930Sstevel@tonic-gate if (cm_entry->x_ksp != NULL) 27940Sstevel@tonic-gate return (TRUE); 27950Sstevel@tonic-gate 27960Sstevel@tonic-gate /* 27970Sstevel@tonic-gate * We need to increment rpc_kstat_instance atomically to prevent 27980Sstevel@tonic-gate * two kstats being created with the same instance. 27990Sstevel@tonic-gate */ 28000Sstevel@tonic-gate kstat_instance = atomic_add_32_nv((uint32_t *)&rpc_kstat_instance, 1); 28010Sstevel@tonic-gate 28020Sstevel@tonic-gate if ((cm_entry->x_ksp = kstat_create_zone("unix", kstat_instance, 28030Sstevel@tonic-gate "rpc_cots_connections", "rpc", KSTAT_TYPE_NAMED, 28040Sstevel@tonic-gate (uint_t)(sizeof (cm_kstat_xprt_t) / sizeof (kstat_named_t)), 28050Sstevel@tonic-gate KSTAT_FLAG_VIRTUAL, cm_entry->x_zoneid)) == NULL) { 28060Sstevel@tonic-gate return (TRUE); 28076403Sgt29601 } 28080Sstevel@tonic-gate 28090Sstevel@tonic-gate cm_entry->x_ksp->ks_lock = &connmgr_lock; 28100Sstevel@tonic-gate cm_entry->x_ksp->ks_private = cm_entry; 28110Sstevel@tonic-gate cm_entry->x_ksp->ks_data_size = ((INET6_ADDRSTRLEN * sizeof (char)) 28126403Sgt29601 + sizeof (cm_kstat_template)); 28130Sstevel@tonic-gate cm_entry->x_ksp->ks_data = kmem_alloc(cm_entry->x_ksp->ks_data_size, 28146403Sgt29601 KM_SLEEP); 28150Sstevel@tonic-gate bcopy(&cm_kstat_template, cm_entry->x_ksp->ks_data, 28160Sstevel@tonic-gate cm_entry->x_ksp->ks_data_size); 28170Sstevel@tonic-gate ((struct cm_kstat_xprt *)(cm_entry->x_ksp->ks_data))-> 28186403Sgt29601 x_server.value.str.addr.ptr = 28196403Sgt29601 kmem_alloc(INET6_ADDRSTRLEN, KM_SLEEP); 28200Sstevel@tonic-gate 28210Sstevel@tonic-gate cm_entry->x_ksp->ks_update = conn_kstat_update; 28220Sstevel@tonic-gate kstat_install(cm_entry->x_ksp); 28230Sstevel@tonic-gate return (TRUE); 28240Sstevel@tonic-gate } 28250Sstevel@tonic-gate 28260Sstevel@tonic-gate /* 2827*10004Sdai.ngo@sun.com * Verify that the specified offset falls within the mblk and 2828*10004Sdai.ngo@sun.com * that the resulting pointer is aligned. 2829*10004Sdai.ngo@sun.com * Returns NULL if not. 2830*10004Sdai.ngo@sun.com * 2831*10004Sdai.ngo@sun.com * code from fs/sockfs/socksubr.c 2832*10004Sdai.ngo@sun.com */ 2833*10004Sdai.ngo@sun.com static void * 2834*10004Sdai.ngo@sun.com connmgr_opt_getoff(mblk_t *mp, t_uscalar_t offset, 2835*10004Sdai.ngo@sun.com t_uscalar_t length, uint_t align_size) 2836*10004Sdai.ngo@sun.com { 2837*10004Sdai.ngo@sun.com uintptr_t ptr1, ptr2; 2838*10004Sdai.ngo@sun.com 2839*10004Sdai.ngo@sun.com ASSERT(mp && mp->b_wptr >= mp->b_rptr); 2840*10004Sdai.ngo@sun.com ptr1 = (uintptr_t)mp->b_rptr + offset; 2841*10004Sdai.ngo@sun.com ptr2 = (uintptr_t)ptr1 + length; 2842*10004Sdai.ngo@sun.com if (ptr1 < (uintptr_t)mp->b_rptr || ptr2 > (uintptr_t)mp->b_wptr) { 2843*10004Sdai.ngo@sun.com return (NULL); 2844*10004Sdai.ngo@sun.com } 2845*10004Sdai.ngo@sun.com if ((ptr1 & (align_size - 1)) != 0) { 2846*10004Sdai.ngo@sun.com return (NULL); 2847*10004Sdai.ngo@sun.com } 2848*10004Sdai.ngo@sun.com return ((void *)ptr1); 2849*10004Sdai.ngo@sun.com } 2850*10004Sdai.ngo@sun.com 2851*10004Sdai.ngo@sun.com static bool_t 2852*10004Sdai.ngo@sun.com connmgr_getopt_int(queue_t *wq, int level, int name, int *val, 2853*10004Sdai.ngo@sun.com calllist_t *e, cred_t *cr) 2854*10004Sdai.ngo@sun.com { 2855*10004Sdai.ngo@sun.com mblk_t *mp; 2856*10004Sdai.ngo@sun.com struct opthdr *opt, *opt_res; 2857*10004Sdai.ngo@sun.com struct T_optmgmt_req *tor; 2858*10004Sdai.ngo@sun.com struct T_optmgmt_ack *opt_ack; 2859*10004Sdai.ngo@sun.com struct timeval waitp; 2860*10004Sdai.ngo@sun.com int error; 2861*10004Sdai.ngo@sun.com 2862*10004Sdai.ngo@sun.com mp = allocb_cred(sizeof (struct T_optmgmt_req) + 2863*10004Sdai.ngo@sun.com sizeof (struct opthdr) + sizeof (int), cr, NOPID); 2864*10004Sdai.ngo@sun.com if (mp == NULL) 2865*10004Sdai.ngo@sun.com return (FALSE); 2866*10004Sdai.ngo@sun.com 2867*10004Sdai.ngo@sun.com mp->b_datap->db_type = M_PROTO; 2868*10004Sdai.ngo@sun.com tor = (struct T_optmgmt_req *)(mp->b_rptr); 2869*10004Sdai.ngo@sun.com tor->PRIM_type = T_SVR4_OPTMGMT_REQ; 2870*10004Sdai.ngo@sun.com tor->MGMT_flags = T_CURRENT; 2871*10004Sdai.ngo@sun.com tor->OPT_length = sizeof (struct opthdr) + sizeof (int); 2872*10004Sdai.ngo@sun.com tor->OPT_offset = sizeof (struct T_optmgmt_req); 2873*10004Sdai.ngo@sun.com 2874*10004Sdai.ngo@sun.com opt = (struct opthdr *)(mp->b_rptr + sizeof (struct T_optmgmt_req)); 2875*10004Sdai.ngo@sun.com opt->level = level; 2876*10004Sdai.ngo@sun.com opt->name = name; 2877*10004Sdai.ngo@sun.com opt->len = sizeof (int); 2878*10004Sdai.ngo@sun.com mp->b_wptr += sizeof (struct T_optmgmt_req) + sizeof (struct opthdr) + 2879*10004Sdai.ngo@sun.com sizeof (int); 2880*10004Sdai.ngo@sun.com 2881*10004Sdai.ngo@sun.com /* 2882*10004Sdai.ngo@sun.com * We will use this connection regardless 2883*10004Sdai.ngo@sun.com * of whether or not the option is readable. 2884*10004Sdai.ngo@sun.com */ 2885*10004Sdai.ngo@sun.com if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) { 2886*10004Sdai.ngo@sun.com DTRACE_PROBE(krpc__e__connmgr__getopt__cantsend); 2887*10004Sdai.ngo@sun.com freemsg(mp); 2888*10004Sdai.ngo@sun.com return (FALSE); 2889*10004Sdai.ngo@sun.com } 2890*10004Sdai.ngo@sun.com 2891*10004Sdai.ngo@sun.com mutex_enter(&clnt_pending_lock); 2892*10004Sdai.ngo@sun.com 2893*10004Sdai.ngo@sun.com waitp.tv_sec = clnt_cots_min_conntout; 2894*10004Sdai.ngo@sun.com waitp.tv_usec = 0; 2895*10004Sdai.ngo@sun.com error = waitforack(e, T_OPTMGMT_ACK, &waitp, 1); 2896*10004Sdai.ngo@sun.com 2897*10004Sdai.ngo@sun.com if (e->call_prev) 2898*10004Sdai.ngo@sun.com e->call_prev->call_next = e->call_next; 2899*10004Sdai.ngo@sun.com else 2900*10004Sdai.ngo@sun.com clnt_pending = e->call_next; 2901*10004Sdai.ngo@sun.com if (e->call_next) 2902*10004Sdai.ngo@sun.com e->call_next->call_prev = e->call_prev; 2903*10004Sdai.ngo@sun.com mutex_exit(&clnt_pending_lock); 2904*10004Sdai.ngo@sun.com 2905*10004Sdai.ngo@sun.com /* get reply message */ 2906*10004Sdai.ngo@sun.com mp = e->call_reply; 2907*10004Sdai.ngo@sun.com e->call_reply = NULL; 2908*10004Sdai.ngo@sun.com 2909*10004Sdai.ngo@sun.com if ((!mp) || (e->call_status != RPC_SUCCESS) || (error != 0)) { 2910*10004Sdai.ngo@sun.com 2911*10004Sdai.ngo@sun.com DTRACE_PROBE4(krpc__e__connmgr_getopt, int, name, 2912*10004Sdai.ngo@sun.com int, e->call_status, int, error, mblk_t *, mp); 2913*10004Sdai.ngo@sun.com 2914*10004Sdai.ngo@sun.com if (mp) 2915*10004Sdai.ngo@sun.com freemsg(mp); 2916*10004Sdai.ngo@sun.com return (FALSE); 2917*10004Sdai.ngo@sun.com } 2918*10004Sdai.ngo@sun.com 2919*10004Sdai.ngo@sun.com opt_ack = (struct T_optmgmt_ack *)mp->b_rptr; 2920*10004Sdai.ngo@sun.com opt_res = (struct opthdr *)connmgr_opt_getoff(mp, opt_ack->OPT_offset, 2921*10004Sdai.ngo@sun.com opt_ack->OPT_length, __TPI_ALIGN_SIZE); 2922*10004Sdai.ngo@sun.com 2923*10004Sdai.ngo@sun.com if (!opt_res) { 2924*10004Sdai.ngo@sun.com DTRACE_PROBE4(krpc__e__connmgr_optres, mblk_t *, mp, int, name, 2925*10004Sdai.ngo@sun.com int, opt_ack->OPT_offset, int, opt_ack->OPT_length); 2926*10004Sdai.ngo@sun.com freemsg(mp); 2927*10004Sdai.ngo@sun.com return (FALSE); 2928*10004Sdai.ngo@sun.com } 2929*10004Sdai.ngo@sun.com *val = *(int *)&opt_res[1]; 2930*10004Sdai.ngo@sun.com 2931*10004Sdai.ngo@sun.com DTRACE_PROBE2(connmgr_getopt__ok, int, name, int, *val); 2932*10004Sdai.ngo@sun.com 2933*10004Sdai.ngo@sun.com freemsg(mp); 2934*10004Sdai.ngo@sun.com return (TRUE); 2935*10004Sdai.ngo@sun.com } 2936*10004Sdai.ngo@sun.com 2937*10004Sdai.ngo@sun.com /* 29380Sstevel@tonic-gate * Called by connmgr_connect to set an option on the new stream. 29390Sstevel@tonic-gate */ 29400Sstevel@tonic-gate static bool_t 2941*10004Sdai.ngo@sun.com connmgr_setopt_int(queue_t *wq, int level, int name, int val, 2942*10004Sdai.ngo@sun.com calllist_t *e, cred_t *cr) 29430Sstevel@tonic-gate { 29440Sstevel@tonic-gate mblk_t *mp; 29450Sstevel@tonic-gate struct opthdr *opt; 29460Sstevel@tonic-gate struct T_optmgmt_req *tor; 29470Sstevel@tonic-gate struct timeval waitp; 29480Sstevel@tonic-gate int error; 29490Sstevel@tonic-gate 29508778SErik.Nordmark@Sun.COM mp = allocb_cred(sizeof (struct T_optmgmt_req) + 29518778SErik.Nordmark@Sun.COM sizeof (struct opthdr) + sizeof (int), cr, NOPID); 29520Sstevel@tonic-gate if (mp == NULL) { 29530Sstevel@tonic-gate RPCLOG0(1, "connmgr_setopt: cannot alloc mp for option " 29540Sstevel@tonic-gate "request\n"); 29550Sstevel@tonic-gate return (FALSE); 29560Sstevel@tonic-gate } 29570Sstevel@tonic-gate 29580Sstevel@tonic-gate mp->b_datap->db_type = M_PROTO; 29590Sstevel@tonic-gate tor = (struct T_optmgmt_req *)(mp->b_rptr); 29600Sstevel@tonic-gate tor->PRIM_type = T_SVR4_OPTMGMT_REQ; 29610Sstevel@tonic-gate tor->MGMT_flags = T_NEGOTIATE; 29620Sstevel@tonic-gate tor->OPT_length = sizeof (struct opthdr) + sizeof (int); 29630Sstevel@tonic-gate tor->OPT_offset = sizeof (struct T_optmgmt_req); 29640Sstevel@tonic-gate 29650Sstevel@tonic-gate opt = (struct opthdr *)(mp->b_rptr + sizeof (struct T_optmgmt_req)); 29660Sstevel@tonic-gate opt->level = level; 29670Sstevel@tonic-gate opt->name = name; 29680Sstevel@tonic-gate opt->len = sizeof (int); 2969*10004Sdai.ngo@sun.com *(int *)((char *)opt + sizeof (*opt)) = val; 29700Sstevel@tonic-gate mp->b_wptr += sizeof (struct T_optmgmt_req) + sizeof (struct opthdr) + 29710Sstevel@tonic-gate sizeof (int); 29720Sstevel@tonic-gate 29730Sstevel@tonic-gate /* 29740Sstevel@tonic-gate * We will use this connection regardless 29750Sstevel@tonic-gate * of whether or not the option is settable. 29760Sstevel@tonic-gate */ 29778205SSiddheshwar.Mahesh@Sun.COM if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) { 29788205SSiddheshwar.Mahesh@Sun.COM DTRACE_PROBE(krpc__e__connmgr__setopt__cantsend); 29798205SSiddheshwar.Mahesh@Sun.COM freemsg(mp); 29808205SSiddheshwar.Mahesh@Sun.COM return (FALSE); 29818205SSiddheshwar.Mahesh@Sun.COM } 29828205SSiddheshwar.Mahesh@Sun.COM 29830Sstevel@tonic-gate mutex_enter(&clnt_pending_lock); 29840Sstevel@tonic-gate 29850Sstevel@tonic-gate waitp.tv_sec = clnt_cots_min_conntout; 29860Sstevel@tonic-gate waitp.tv_usec = 0; 29870Sstevel@tonic-gate error = waitforack(e, T_OPTMGMT_ACK, &waitp, 1); 29880Sstevel@tonic-gate 29890Sstevel@tonic-gate if (e->call_prev) 29900Sstevel@tonic-gate e->call_prev->call_next = e->call_next; 29910Sstevel@tonic-gate else 29920Sstevel@tonic-gate clnt_pending = e->call_next; 29930Sstevel@tonic-gate if (e->call_next) 29940Sstevel@tonic-gate e->call_next->call_prev = e->call_prev; 29950Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 29960Sstevel@tonic-gate 29970Sstevel@tonic-gate if (e->call_reply != NULL) { 29980Sstevel@tonic-gate freemsg(e->call_reply); 29990Sstevel@tonic-gate e->call_reply = NULL; 30000Sstevel@tonic-gate } 30010Sstevel@tonic-gate 30020Sstevel@tonic-gate if (e->call_status != RPC_SUCCESS || error != 0) { 30030Sstevel@tonic-gate RPCLOG(1, "connmgr_setopt: can't set option: %d\n", name); 30040Sstevel@tonic-gate return (FALSE); 30050Sstevel@tonic-gate } 30060Sstevel@tonic-gate RPCLOG(8, "connmgr_setopt: successfully set option: %d\n", name); 30070Sstevel@tonic-gate return (TRUE); 30080Sstevel@tonic-gate } 30090Sstevel@tonic-gate 3010*10004Sdai.ngo@sun.com static bool_t 3011*10004Sdai.ngo@sun.com connmgr_setopt(queue_t *wq, int level, int name, calllist_t *e, cred_t *cr) 3012*10004Sdai.ngo@sun.com { 3013*10004Sdai.ngo@sun.com return (connmgr_setopt_int(wq, level, name, 1, e, cr)); 3014*10004Sdai.ngo@sun.com } 3015*10004Sdai.ngo@sun.com 30160Sstevel@tonic-gate #ifdef DEBUG 30170Sstevel@tonic-gate 30180Sstevel@tonic-gate /* 30190Sstevel@tonic-gate * This is a knob to let us force code coverage in allocation failure 30200Sstevel@tonic-gate * case. 30210Sstevel@tonic-gate */ 30220Sstevel@tonic-gate static int connmgr_failsnd; 30230Sstevel@tonic-gate #define CONN_SND_ALLOC(Size, Pri) \ 30240Sstevel@tonic-gate ((connmgr_failsnd-- > 0) ? NULL : allocb(Size, Pri)) 30250Sstevel@tonic-gate 30260Sstevel@tonic-gate #else 30270Sstevel@tonic-gate 30280Sstevel@tonic-gate #define CONN_SND_ALLOC(Size, Pri) allocb(Size, Pri) 30290Sstevel@tonic-gate 30300Sstevel@tonic-gate #endif 30310Sstevel@tonic-gate 30320Sstevel@tonic-gate /* 30330Sstevel@tonic-gate * Sends an orderly release on the specified queue. 30340Sstevel@tonic-gate * Entered with connmgr_lock. Exited without connmgr_lock 30350Sstevel@tonic-gate */ 30360Sstevel@tonic-gate static void 30370Sstevel@tonic-gate connmgr_sndrel(struct cm_xprt *cm_entry) 30380Sstevel@tonic-gate { 30390Sstevel@tonic-gate struct T_ordrel_req *torr; 30400Sstevel@tonic-gate mblk_t *mp; 30410Sstevel@tonic-gate queue_t *q = cm_entry->x_wq; 30420Sstevel@tonic-gate ASSERT(MUTEX_HELD(&connmgr_lock)); 30430Sstevel@tonic-gate mp = CONN_SND_ALLOC(sizeof (struct T_ordrel_req), BPRI_LO); 30440Sstevel@tonic-gate if (mp == NULL) { 30450Sstevel@tonic-gate cm_entry->x_needrel = TRUE; 30460Sstevel@tonic-gate mutex_exit(&connmgr_lock); 30470Sstevel@tonic-gate RPCLOG(1, "connmgr_sndrel: cannot alloc mp for sending ordrel " 30486403Sgt29601 "to queue %p\n", (void *)q); 30490Sstevel@tonic-gate return; 30500Sstevel@tonic-gate } 30510Sstevel@tonic-gate mutex_exit(&connmgr_lock); 30520Sstevel@tonic-gate 30530Sstevel@tonic-gate mp->b_datap->db_type = M_PROTO; 30540Sstevel@tonic-gate torr = (struct T_ordrel_req *)(mp->b_rptr); 30550Sstevel@tonic-gate torr->PRIM_type = T_ORDREL_REQ; 30560Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (struct T_ordrel_req); 30570Sstevel@tonic-gate 30580Sstevel@tonic-gate RPCLOG(8, "connmgr_sndrel: sending ordrel to queue %p\n", (void *)q); 30590Sstevel@tonic-gate put(q, mp); 30600Sstevel@tonic-gate } 30610Sstevel@tonic-gate 30620Sstevel@tonic-gate /* 30630Sstevel@tonic-gate * Sends an disconnect on the specified queue. 30640Sstevel@tonic-gate * Entered with connmgr_lock. Exited without connmgr_lock 30650Sstevel@tonic-gate */ 30660Sstevel@tonic-gate static void 30670Sstevel@tonic-gate connmgr_snddis(struct cm_xprt *cm_entry) 30680Sstevel@tonic-gate { 30690Sstevel@tonic-gate struct T_discon_req *tdis; 30700Sstevel@tonic-gate mblk_t *mp; 30710Sstevel@tonic-gate queue_t *q = cm_entry->x_wq; 30720Sstevel@tonic-gate 30730Sstevel@tonic-gate ASSERT(MUTEX_HELD(&connmgr_lock)); 30740Sstevel@tonic-gate mp = CONN_SND_ALLOC(sizeof (*tdis), BPRI_LO); 30750Sstevel@tonic-gate if (mp == NULL) { 30760Sstevel@tonic-gate cm_entry->x_needdis = TRUE; 30770Sstevel@tonic-gate mutex_exit(&connmgr_lock); 30780Sstevel@tonic-gate RPCLOG(1, "connmgr_snddis: cannot alloc mp for sending discon " 30790Sstevel@tonic-gate "to queue %p\n", (void *)q); 30800Sstevel@tonic-gate return; 30810Sstevel@tonic-gate } 30820Sstevel@tonic-gate mutex_exit(&connmgr_lock); 30830Sstevel@tonic-gate 30840Sstevel@tonic-gate mp->b_datap->db_type = M_PROTO; 30850Sstevel@tonic-gate tdis = (struct T_discon_req *)mp->b_rptr; 30860Sstevel@tonic-gate tdis->PRIM_type = T_DISCON_REQ; 30870Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (*tdis); 30880Sstevel@tonic-gate 30890Sstevel@tonic-gate RPCLOG(8, "connmgr_snddis: sending discon to queue %p\n", (void *)q); 30900Sstevel@tonic-gate put(q, mp); 30910Sstevel@tonic-gate } 30920Sstevel@tonic-gate 30930Sstevel@tonic-gate /* 30940Sstevel@tonic-gate * Sets up the entry for receiving replies, and calls rpcmod's write put proc 30950Sstevel@tonic-gate * (through put) to send the call. 30960Sstevel@tonic-gate */ 30978205SSiddheshwar.Mahesh@Sun.COM static int 30980Sstevel@tonic-gate clnt_dispatch_send(queue_t *q, mblk_t *mp, calllist_t *e, uint_t xid, 30990Sstevel@tonic-gate uint_t queue_flag) 31000Sstevel@tonic-gate { 31010Sstevel@tonic-gate ASSERT(e != NULL); 31020Sstevel@tonic-gate 31030Sstevel@tonic-gate e->call_status = RPC_TIMEDOUT; /* optimistic, eh? */ 31040Sstevel@tonic-gate e->call_reason = 0; 31050Sstevel@tonic-gate e->call_wq = q; 31060Sstevel@tonic-gate e->call_xid = xid; 31070Sstevel@tonic-gate e->call_notified = FALSE; 31080Sstevel@tonic-gate 31098205SSiddheshwar.Mahesh@Sun.COM if (!canput(q)) { 31108205SSiddheshwar.Mahesh@Sun.COM e->call_status = RPC_CANTSEND; 31119675Sdai.ngo@sun.com e->call_reason = ENOBUFS; 31128205SSiddheshwar.Mahesh@Sun.COM return (RPC_CANTSEND); 31138205SSiddheshwar.Mahesh@Sun.COM } 31148205SSiddheshwar.Mahesh@Sun.COM 31150Sstevel@tonic-gate /* 31160Sstevel@tonic-gate * If queue_flag is set then the calllist_t is already on the hash 31170Sstevel@tonic-gate * queue. In this case just send the message and return. 31180Sstevel@tonic-gate */ 31190Sstevel@tonic-gate if (queue_flag) { 31200Sstevel@tonic-gate put(q, mp); 31218205SSiddheshwar.Mahesh@Sun.COM return (RPC_SUCCESS); 31228205SSiddheshwar.Mahesh@Sun.COM 31230Sstevel@tonic-gate } 31240Sstevel@tonic-gate 31250Sstevel@tonic-gate /* 31260Sstevel@tonic-gate * Set up calls for RPC requests (with XID != 0) on the hash 31270Sstevel@tonic-gate * queue for fast lookups and place other calls (i.e. 31280Sstevel@tonic-gate * connection management) on the linked list. 31290Sstevel@tonic-gate */ 31300Sstevel@tonic-gate if (xid != 0) { 31310Sstevel@tonic-gate RPCLOG(64, "clnt_dispatch_send: putting xid 0x%x on " 31326403Sgt29601 "dispatch list\n", xid); 31330Sstevel@tonic-gate e->call_hash = call_hash(xid, clnt_cots_hash_size); 31340Sstevel@tonic-gate e->call_bucket = &cots_call_ht[e->call_hash]; 31350Sstevel@tonic-gate call_table_enter(e); 31360Sstevel@tonic-gate } else { 31370Sstevel@tonic-gate mutex_enter(&clnt_pending_lock); 31380Sstevel@tonic-gate if (clnt_pending) 31390Sstevel@tonic-gate clnt_pending->call_prev = e; 31400Sstevel@tonic-gate e->call_next = clnt_pending; 31410Sstevel@tonic-gate e->call_prev = NULL; 31420Sstevel@tonic-gate clnt_pending = e; 31430Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 31440Sstevel@tonic-gate } 31450Sstevel@tonic-gate 31460Sstevel@tonic-gate put(q, mp); 31478205SSiddheshwar.Mahesh@Sun.COM return (RPC_SUCCESS); 31480Sstevel@tonic-gate } 31490Sstevel@tonic-gate 31500Sstevel@tonic-gate /* 31510Sstevel@tonic-gate * Called by rpcmod to notify a client with a clnt_pending call that its reply 31520Sstevel@tonic-gate * has arrived. If we can't find a client waiting for this reply, we log 31530Sstevel@tonic-gate * the error and return. 31540Sstevel@tonic-gate */ 31550Sstevel@tonic-gate bool_t 31560Sstevel@tonic-gate clnt_dispatch_notify(mblk_t *mp, zoneid_t zoneid) 31570Sstevel@tonic-gate { 31580Sstevel@tonic-gate calllist_t *e = NULL; 31590Sstevel@tonic-gate call_table_t *chtp; 31600Sstevel@tonic-gate uint32_t xid; 31610Sstevel@tonic-gate uint_t hash; 31620Sstevel@tonic-gate 31630Sstevel@tonic-gate if ((IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) && 31640Sstevel@tonic-gate (mp->b_wptr - mp->b_rptr) >= sizeof (xid)) 31650Sstevel@tonic-gate xid = *((uint32_t *)mp->b_rptr); 31660Sstevel@tonic-gate else { 31670Sstevel@tonic-gate int i = 0; 31680Sstevel@tonic-gate unsigned char *p = (unsigned char *)&xid; 31690Sstevel@tonic-gate unsigned char *rptr; 31700Sstevel@tonic-gate mblk_t *tmp = mp; 31710Sstevel@tonic-gate 31720Sstevel@tonic-gate /* 31730Sstevel@tonic-gate * Copy the xid, byte-by-byte into xid. 31740Sstevel@tonic-gate */ 31750Sstevel@tonic-gate while (tmp) { 31760Sstevel@tonic-gate rptr = tmp->b_rptr; 31770Sstevel@tonic-gate while (rptr < tmp->b_wptr) { 31780Sstevel@tonic-gate *p++ = *rptr++; 31790Sstevel@tonic-gate if (++i >= sizeof (xid)) 31800Sstevel@tonic-gate goto done_xid_copy; 31810Sstevel@tonic-gate } 31820Sstevel@tonic-gate tmp = tmp->b_cont; 31830Sstevel@tonic-gate } 31840Sstevel@tonic-gate 31850Sstevel@tonic-gate /* 31860Sstevel@tonic-gate * If we got here, we ran out of mblk space before the 31870Sstevel@tonic-gate * xid could be copied. 31880Sstevel@tonic-gate */ 31890Sstevel@tonic-gate ASSERT(tmp == NULL && i < sizeof (xid)); 31900Sstevel@tonic-gate 31910Sstevel@tonic-gate RPCLOG0(1, 31920Sstevel@tonic-gate "clnt_dispatch_notify: message less than size of xid\n"); 31930Sstevel@tonic-gate return (FALSE); 31940Sstevel@tonic-gate 31950Sstevel@tonic-gate } 31960Sstevel@tonic-gate done_xid_copy: 31970Sstevel@tonic-gate 31980Sstevel@tonic-gate hash = call_hash(xid, clnt_cots_hash_size); 31990Sstevel@tonic-gate chtp = &cots_call_ht[hash]; 32000Sstevel@tonic-gate /* call_table_find returns with the hash bucket locked */ 32010Sstevel@tonic-gate call_table_find(chtp, xid, e); 32020Sstevel@tonic-gate 32030Sstevel@tonic-gate if (e != NULL) { 32040Sstevel@tonic-gate /* 32050Sstevel@tonic-gate * Found thread waiting for this reply 32060Sstevel@tonic-gate */ 32070Sstevel@tonic-gate mutex_enter(&e->call_lock); 32086403Sgt29601 32096403Sgt29601 /* 32106403Sgt29601 * verify that the reply is coming in on 32116403Sgt29601 * the same zone that it was sent from. 32126403Sgt29601 */ 32136403Sgt29601 if (e->call_zoneid != zoneid) { 32146403Sgt29601 mutex_exit(&e->call_lock); 32156403Sgt29601 mutex_exit(&chtp->ct_lock); 32169804SGerald.Thornbrugh@Sun.COM RPCLOG0(1, "clnt_dispatch_notify: incorrect zoneid\n"); 32176403Sgt29601 return (FALSE); 32186403Sgt29601 } 32196403Sgt29601 32200Sstevel@tonic-gate if (e->call_reply) 32210Sstevel@tonic-gate /* 32220Sstevel@tonic-gate * This can happen under the following scenario: 32230Sstevel@tonic-gate * clnt_cots_kcallit() times out on the response, 32240Sstevel@tonic-gate * rfscall() repeats the CLNT_CALL() with 32250Sstevel@tonic-gate * the same xid, clnt_cots_kcallit() sends the retry, 32260Sstevel@tonic-gate * thereby putting the clnt handle on the pending list, 32270Sstevel@tonic-gate * the first response arrives, signalling the thread 32280Sstevel@tonic-gate * in clnt_cots_kcallit(). Before that thread is 32290Sstevel@tonic-gate * dispatched, the second response arrives as well, 32300Sstevel@tonic-gate * and clnt_dispatch_notify still finds the handle on 32310Sstevel@tonic-gate * the pending list, with call_reply set. So free the 32320Sstevel@tonic-gate * old reply now. 32330Sstevel@tonic-gate * 32340Sstevel@tonic-gate * It is also possible for a response intended for 32350Sstevel@tonic-gate * an RPC call with a different xid to reside here. 32360Sstevel@tonic-gate * This can happen if the thread that owned this 32370Sstevel@tonic-gate * client handle prior to the current owner bailed 32380Sstevel@tonic-gate * out and left its call record on the dispatch 32390Sstevel@tonic-gate * queue. A window exists where the response can 32400Sstevel@tonic-gate * arrive before the current owner dispatches its 32410Sstevel@tonic-gate * RPC call. 32420Sstevel@tonic-gate * 32430Sstevel@tonic-gate * In any case, this is the very last point where we 32440Sstevel@tonic-gate * can safely check the call_reply field before 32450Sstevel@tonic-gate * placing the new response there. 32460Sstevel@tonic-gate */ 32470Sstevel@tonic-gate freemsg(e->call_reply); 32480Sstevel@tonic-gate e->call_reply = mp; 32490Sstevel@tonic-gate e->call_status = RPC_SUCCESS; 32500Sstevel@tonic-gate e->call_notified = TRUE; 32510Sstevel@tonic-gate cv_signal(&e->call_cv); 32520Sstevel@tonic-gate mutex_exit(&e->call_lock); 32530Sstevel@tonic-gate mutex_exit(&chtp->ct_lock); 32540Sstevel@tonic-gate return (TRUE); 32550Sstevel@tonic-gate } else { 32560Sstevel@tonic-gate zone_t *zone; 32570Sstevel@tonic-gate struct rpcstat *rpcstat; 32580Sstevel@tonic-gate 32590Sstevel@tonic-gate mutex_exit(&chtp->ct_lock); 32600Sstevel@tonic-gate RPCLOG(65, "clnt_dispatch_notify: no caller for reply 0x%x\n", 32610Sstevel@tonic-gate xid); 32620Sstevel@tonic-gate /* 32630Sstevel@tonic-gate * This is unfortunate, but we need to lookup the zone so we 32640Sstevel@tonic-gate * can increment its "rcbadxids" counter. 32650Sstevel@tonic-gate */ 32660Sstevel@tonic-gate zone = zone_find_by_id(zoneid); 32670Sstevel@tonic-gate if (zone == NULL) { 32680Sstevel@tonic-gate /* 32690Sstevel@tonic-gate * The zone went away... 32700Sstevel@tonic-gate */ 32710Sstevel@tonic-gate return (FALSE); 32720Sstevel@tonic-gate } 32730Sstevel@tonic-gate rpcstat = zone_getspecific(rpcstat_zone_key, zone); 32740Sstevel@tonic-gate if (zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN) { 32750Sstevel@tonic-gate /* 32760Sstevel@tonic-gate * Not interested 32770Sstevel@tonic-gate */ 32780Sstevel@tonic-gate zone_rele(zone); 32790Sstevel@tonic-gate return (FALSE); 32800Sstevel@tonic-gate } 32810Sstevel@tonic-gate COTSRCSTAT_INCR(rpcstat->rpc_cots_client, rcbadxids); 32820Sstevel@tonic-gate zone_rele(zone); 32830Sstevel@tonic-gate } 32840Sstevel@tonic-gate return (FALSE); 32850Sstevel@tonic-gate } 32860Sstevel@tonic-gate 32870Sstevel@tonic-gate /* 32880Sstevel@tonic-gate * Called by rpcmod when a non-data indication arrives. The ones in which we 32890Sstevel@tonic-gate * are interested are connection indications and options acks. We dispatch 32900Sstevel@tonic-gate * based on the queue the indication came in on. If we are not interested in 32910Sstevel@tonic-gate * what came in, we return false to rpcmod, who will then pass it upstream. 32920Sstevel@tonic-gate */ 32930Sstevel@tonic-gate bool_t 32940Sstevel@tonic-gate clnt_dispatch_notifyconn(queue_t *q, mblk_t *mp) 32950Sstevel@tonic-gate { 32960Sstevel@tonic-gate calllist_t *e; 32970Sstevel@tonic-gate int type; 32980Sstevel@tonic-gate 32990Sstevel@tonic-gate ASSERT((q->q_flag & QREADR) == 0); 33000Sstevel@tonic-gate 33010Sstevel@tonic-gate type = ((union T_primitives *)mp->b_rptr)->type; 33020Sstevel@tonic-gate RPCLOG(8, "clnt_dispatch_notifyconn: prim type: [%s]\n", 33030Sstevel@tonic-gate rpc_tpiprim2name(type)); 33040Sstevel@tonic-gate mutex_enter(&clnt_pending_lock); 33050Sstevel@tonic-gate for (e = clnt_pending; /* NO CONDITION */; e = e->call_next) { 33060Sstevel@tonic-gate if (e == NULL) { 33070Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 33080Sstevel@tonic-gate RPCLOG(1, "clnt_dispatch_notifyconn: no one waiting " 33090Sstevel@tonic-gate "for connection on queue 0x%p\n", (void *)q); 33100Sstevel@tonic-gate return (FALSE); 33110Sstevel@tonic-gate } 33120Sstevel@tonic-gate if (e->call_wq == q) 33130Sstevel@tonic-gate break; 33140Sstevel@tonic-gate } 33150Sstevel@tonic-gate 33160Sstevel@tonic-gate switch (type) { 33170Sstevel@tonic-gate case T_CONN_CON: 33180Sstevel@tonic-gate /* 33190Sstevel@tonic-gate * The transport is now connected, send a T_INFO_REQ to get 33200Sstevel@tonic-gate * the tidu size. 33210Sstevel@tonic-gate */ 33220Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 33230Sstevel@tonic-gate ASSERT(mp->b_datap->db_lim - mp->b_datap->db_base >= 33246403Sgt29601 sizeof (struct T_info_req)); 33250Sstevel@tonic-gate mp->b_rptr = mp->b_datap->db_base; 33260Sstevel@tonic-gate ((union T_primitives *)mp->b_rptr)->type = T_INFO_REQ; 33270Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (struct T_info_req); 33280Sstevel@tonic-gate mp->b_datap->db_type = M_PCPROTO; 33290Sstevel@tonic-gate put(q, mp); 33300Sstevel@tonic-gate return (TRUE); 33310Sstevel@tonic-gate case T_INFO_ACK: 33320Sstevel@tonic-gate case T_OPTMGMT_ACK: 33330Sstevel@tonic-gate e->call_status = RPC_SUCCESS; 33340Sstevel@tonic-gate e->call_reply = mp; 33350Sstevel@tonic-gate e->call_notified = TRUE; 33360Sstevel@tonic-gate cv_signal(&e->call_cv); 33370Sstevel@tonic-gate break; 33380Sstevel@tonic-gate case T_ERROR_ACK: 33390Sstevel@tonic-gate e->call_status = RPC_CANTCONNECT; 33400Sstevel@tonic-gate e->call_reply = mp; 33410Sstevel@tonic-gate e->call_notified = TRUE; 33420Sstevel@tonic-gate cv_signal(&e->call_cv); 33430Sstevel@tonic-gate break; 33440Sstevel@tonic-gate case T_OK_ACK: 33450Sstevel@tonic-gate /* 33460Sstevel@tonic-gate * Great, but we are really waiting for a T_CONN_CON 33470Sstevel@tonic-gate */ 33480Sstevel@tonic-gate freemsg(mp); 33490Sstevel@tonic-gate break; 33500Sstevel@tonic-gate default: 33510Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 33520Sstevel@tonic-gate RPCLOG(1, "clnt_dispatch_notifyconn: bad type %d\n", type); 33530Sstevel@tonic-gate return (FALSE); 33540Sstevel@tonic-gate } 33550Sstevel@tonic-gate 33560Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 33570Sstevel@tonic-gate return (TRUE); 33580Sstevel@tonic-gate } 33590Sstevel@tonic-gate 33600Sstevel@tonic-gate /* 33610Sstevel@tonic-gate * Called by rpcmod when the transport is (or should be) going away. Informs 33620Sstevel@tonic-gate * all callers waiting for replies and marks the entry in the connection 33630Sstevel@tonic-gate * manager's list as unconnected, and either closing (close handshake in 33640Sstevel@tonic-gate * progress) or dead. 33650Sstevel@tonic-gate */ 33660Sstevel@tonic-gate void 33670Sstevel@tonic-gate clnt_dispatch_notifyall(queue_t *q, int32_t msg_type, int32_t reason) 33680Sstevel@tonic-gate { 33690Sstevel@tonic-gate calllist_t *e; 33700Sstevel@tonic-gate call_table_t *ctp; 33710Sstevel@tonic-gate struct cm_xprt *cm_entry; 33720Sstevel@tonic-gate int have_connmgr_lock; 33730Sstevel@tonic-gate int i; 33740Sstevel@tonic-gate 33750Sstevel@tonic-gate ASSERT((q->q_flag & QREADR) == 0); 33760Sstevel@tonic-gate 33770Sstevel@tonic-gate RPCLOG(1, "clnt_dispatch_notifyall on queue %p", (void *)q); 33780Sstevel@tonic-gate RPCLOG(1, " received a notifcation prim type [%s]", 33790Sstevel@tonic-gate rpc_tpiprim2name(msg_type)); 33800Sstevel@tonic-gate RPCLOG(1, " and reason %d\n", reason); 33810Sstevel@tonic-gate 33820Sstevel@tonic-gate /* 33830Sstevel@tonic-gate * Find the transport entry in the connection manager's list, close 33840Sstevel@tonic-gate * the transport and delete the entry. In the case where rpcmod's 33850Sstevel@tonic-gate * idle timer goes off, it sends us a T_ORDREL_REQ, indicating we 33860Sstevel@tonic-gate * should gracefully close the connection. 33870Sstevel@tonic-gate */ 33880Sstevel@tonic-gate have_connmgr_lock = 1; 33890Sstevel@tonic-gate mutex_enter(&connmgr_lock); 33900Sstevel@tonic-gate for (cm_entry = cm_hd; cm_entry; cm_entry = cm_entry->x_next) { 33910Sstevel@tonic-gate ASSERT(cm_entry != cm_entry->x_next); 33920Sstevel@tonic-gate if (cm_entry->x_wq == q) { 33930Sstevel@tonic-gate ASSERT(MUTEX_HELD(&connmgr_lock)); 33940Sstevel@tonic-gate ASSERT(have_connmgr_lock == 1); 33950Sstevel@tonic-gate switch (msg_type) { 33960Sstevel@tonic-gate case T_ORDREL_REQ: 33970Sstevel@tonic-gate 33980Sstevel@tonic-gate if (cm_entry->x_dead) { 33990Sstevel@tonic-gate RPCLOG(1, "idle timeout on dead " 34000Sstevel@tonic-gate "connection: %p\n", 34010Sstevel@tonic-gate (void *)cm_entry); 34020Sstevel@tonic-gate if (clnt_stop_idle != NULL) 34030Sstevel@tonic-gate (*clnt_stop_idle)(q); 34040Sstevel@tonic-gate break; 34050Sstevel@tonic-gate } 34060Sstevel@tonic-gate 34070Sstevel@tonic-gate /* 34080Sstevel@tonic-gate * Only mark the connection as dead if it is 34090Sstevel@tonic-gate * connected and idle. 34100Sstevel@tonic-gate * An unconnected connection has probably 34110Sstevel@tonic-gate * gone idle because the server is down, 34120Sstevel@tonic-gate * and when it comes back up there will be 34130Sstevel@tonic-gate * retries that need to use that connection. 34140Sstevel@tonic-gate */ 34150Sstevel@tonic-gate if (cm_entry->x_connected || 34160Sstevel@tonic-gate cm_entry->x_doomed) { 34176403Sgt29601 if (cm_entry->x_ordrel) { 34186403Sgt29601 if (cm_entry->x_closing == 34196403Sgt29601 TRUE) { 34206403Sgt29601 /* 34216403Sgt29601 * The connection is 34226403Sgt29601 * obviously wedged due 34236403Sgt29601 * to a bug or problem 34246403Sgt29601 * with the transport. 34256403Sgt29601 * Mark it as dead. 34266403Sgt29601 * Otherwise we can 34276403Sgt29601 * leak connections. 34286403Sgt29601 */ 34296403Sgt29601 cm_entry->x_dead = TRUE; 34306403Sgt29601 mutex_exit( 34316403Sgt29601 &connmgr_lock); 34326403Sgt29601 have_connmgr_lock = 0; 34336403Sgt29601 if (clnt_stop_idle != 34346403Sgt29601 NULL) 34356403Sgt29601 (*clnt_stop_idle)(q); 34366403Sgt29601 break; 34376403Sgt29601 } 34386403Sgt29601 cm_entry->x_closing = TRUE; 34396403Sgt29601 connmgr_sndrel(cm_entry); 34406403Sgt29601 have_connmgr_lock = 0; 34416403Sgt29601 } else { 34426403Sgt29601 cm_entry->x_dead = TRUE; 34436403Sgt29601 mutex_exit(&connmgr_lock); 34446403Sgt29601 have_connmgr_lock = 0; 34456403Sgt29601 if (clnt_stop_idle != NULL) 34466403Sgt29601 (*clnt_stop_idle)(q); 34470Sstevel@tonic-gate } 34480Sstevel@tonic-gate } else { 34490Sstevel@tonic-gate /* 34500Sstevel@tonic-gate * We don't mark the connection 34510Sstevel@tonic-gate * as dead, but we turn off the 34520Sstevel@tonic-gate * idle timer. 34530Sstevel@tonic-gate */ 34540Sstevel@tonic-gate mutex_exit(&connmgr_lock); 34550Sstevel@tonic-gate have_connmgr_lock = 0; 34560Sstevel@tonic-gate if (clnt_stop_idle != NULL) 34570Sstevel@tonic-gate (*clnt_stop_idle)(q); 34580Sstevel@tonic-gate RPCLOG(1, "clnt_dispatch_notifyall:" 34590Sstevel@tonic-gate " ignoring timeout from rpcmod" 34600Sstevel@tonic-gate " (q %p) because we are not " 34610Sstevel@tonic-gate " connected\n", (void *)q); 34620Sstevel@tonic-gate } 34630Sstevel@tonic-gate break; 34640Sstevel@tonic-gate case T_ORDREL_IND: 34650Sstevel@tonic-gate /* 34660Sstevel@tonic-gate * If this entry is marked closing, then we are 34670Sstevel@tonic-gate * completing a close handshake, and the 34680Sstevel@tonic-gate * connection is dead. Otherwise, the server is 34690Sstevel@tonic-gate * trying to close. Since the server will not 34700Sstevel@tonic-gate * be sending any more RPC replies, we abort 34710Sstevel@tonic-gate * the connection, including flushing 34720Sstevel@tonic-gate * any RPC requests that are in-transit. 34738956Sdai.ngo@sun.com * In either case, mark the entry as dead so 34748956Sdai.ngo@sun.com * that it can be closed by the connection 34758956Sdai.ngo@sun.com * manager's garbage collector. 34760Sstevel@tonic-gate */ 34778956Sdai.ngo@sun.com cm_entry->x_dead = TRUE; 34780Sstevel@tonic-gate if (cm_entry->x_closing) { 34790Sstevel@tonic-gate mutex_exit(&connmgr_lock); 34800Sstevel@tonic-gate have_connmgr_lock = 0; 34810Sstevel@tonic-gate if (clnt_stop_idle != NULL) 34820Sstevel@tonic-gate (*clnt_stop_idle)(q); 34830Sstevel@tonic-gate } else { 34840Sstevel@tonic-gate /* 34850Sstevel@tonic-gate * if we're getting a disconnect 34860Sstevel@tonic-gate * before we've finished our 34870Sstevel@tonic-gate * connect attempt, mark it for 34880Sstevel@tonic-gate * later processing 34890Sstevel@tonic-gate */ 34900Sstevel@tonic-gate if (cm_entry->x_thread) 34910Sstevel@tonic-gate cm_entry->x_early_disc = TRUE; 34920Sstevel@tonic-gate else 34930Sstevel@tonic-gate cm_entry->x_connected = FALSE; 34940Sstevel@tonic-gate cm_entry->x_waitdis = TRUE; 34950Sstevel@tonic-gate connmgr_snddis(cm_entry); 34960Sstevel@tonic-gate have_connmgr_lock = 0; 34970Sstevel@tonic-gate } 34980Sstevel@tonic-gate break; 34990Sstevel@tonic-gate 35000Sstevel@tonic-gate case T_ERROR_ACK: 35010Sstevel@tonic-gate case T_OK_ACK: 35020Sstevel@tonic-gate cm_entry->x_waitdis = FALSE; 35030Sstevel@tonic-gate cv_signal(&cm_entry->x_dis_cv); 35040Sstevel@tonic-gate mutex_exit(&connmgr_lock); 35050Sstevel@tonic-gate return; 35060Sstevel@tonic-gate 35070Sstevel@tonic-gate case T_DISCON_REQ: 35080Sstevel@tonic-gate if (cm_entry->x_thread) 35090Sstevel@tonic-gate cm_entry->x_early_disc = TRUE; 35100Sstevel@tonic-gate else 35110Sstevel@tonic-gate cm_entry->x_connected = FALSE; 35120Sstevel@tonic-gate cm_entry->x_waitdis = TRUE; 35130Sstevel@tonic-gate 35140Sstevel@tonic-gate connmgr_snddis(cm_entry); 35150Sstevel@tonic-gate have_connmgr_lock = 0; 35160Sstevel@tonic-gate break; 35170Sstevel@tonic-gate 35180Sstevel@tonic-gate case T_DISCON_IND: 35190Sstevel@tonic-gate default: 35200Sstevel@tonic-gate /* 35210Sstevel@tonic-gate * if we're getting a disconnect before 35220Sstevel@tonic-gate * we've finished our connect attempt, 35230Sstevel@tonic-gate * mark it for later processing 35240Sstevel@tonic-gate */ 35250Sstevel@tonic-gate if (cm_entry->x_closing) { 35260Sstevel@tonic-gate cm_entry->x_dead = TRUE; 35270Sstevel@tonic-gate mutex_exit(&connmgr_lock); 35280Sstevel@tonic-gate have_connmgr_lock = 0; 35290Sstevel@tonic-gate if (clnt_stop_idle != NULL) 35300Sstevel@tonic-gate (*clnt_stop_idle)(q); 35310Sstevel@tonic-gate } else { 35320Sstevel@tonic-gate if (cm_entry->x_thread) { 35330Sstevel@tonic-gate cm_entry->x_early_disc = TRUE; 35340Sstevel@tonic-gate } else { 35350Sstevel@tonic-gate cm_entry->x_dead = TRUE; 35360Sstevel@tonic-gate cm_entry->x_connected = FALSE; 35370Sstevel@tonic-gate } 35380Sstevel@tonic-gate } 35390Sstevel@tonic-gate break; 35400Sstevel@tonic-gate } 35410Sstevel@tonic-gate break; 35420Sstevel@tonic-gate } 35430Sstevel@tonic-gate } 35440Sstevel@tonic-gate 35450Sstevel@tonic-gate if (have_connmgr_lock) 35460Sstevel@tonic-gate mutex_exit(&connmgr_lock); 35470Sstevel@tonic-gate 35480Sstevel@tonic-gate if (msg_type == T_ERROR_ACK || msg_type == T_OK_ACK) { 35490Sstevel@tonic-gate RPCLOG(1, "clnt_dispatch_notifyall: (wq %p) could not find " 35500Sstevel@tonic-gate "connmgr entry for discon ack\n", (void *)q); 35510Sstevel@tonic-gate return; 35520Sstevel@tonic-gate } 35530Sstevel@tonic-gate 35540Sstevel@tonic-gate /* 35550Sstevel@tonic-gate * Then kick all the clnt_pending calls out of their wait. There 35560Sstevel@tonic-gate * should be no clnt_pending calls in the case of rpcmod's idle 35570Sstevel@tonic-gate * timer firing. 35580Sstevel@tonic-gate */ 35590Sstevel@tonic-gate for (i = 0; i < clnt_cots_hash_size; i++) { 35600Sstevel@tonic-gate ctp = &cots_call_ht[i]; 35610Sstevel@tonic-gate mutex_enter(&ctp->ct_lock); 35620Sstevel@tonic-gate for (e = ctp->ct_call_next; 35636403Sgt29601 e != (calllist_t *)ctp; 35646403Sgt29601 e = e->call_next) { 35650Sstevel@tonic-gate if (e->call_wq == q && e->call_notified == FALSE) { 35660Sstevel@tonic-gate RPCLOG(1, 35676403Sgt29601 "clnt_dispatch_notifyall for queue %p ", 35686403Sgt29601 (void *)q); 35690Sstevel@tonic-gate RPCLOG(1, "aborting clnt_pending call %p\n", 35706403Sgt29601 (void *)e); 35710Sstevel@tonic-gate 35720Sstevel@tonic-gate if (msg_type == T_DISCON_IND) 35730Sstevel@tonic-gate e->call_reason = reason; 35740Sstevel@tonic-gate e->call_notified = TRUE; 35750Sstevel@tonic-gate e->call_status = RPC_XPRTFAILED; 35760Sstevel@tonic-gate cv_signal(&e->call_cv); 35770Sstevel@tonic-gate } 35780Sstevel@tonic-gate } 35790Sstevel@tonic-gate mutex_exit(&ctp->ct_lock); 35800Sstevel@tonic-gate } 35810Sstevel@tonic-gate 35820Sstevel@tonic-gate mutex_enter(&clnt_pending_lock); 35830Sstevel@tonic-gate for (e = clnt_pending; e; e = e->call_next) { 35840Sstevel@tonic-gate /* 35850Sstevel@tonic-gate * Only signal those RPC handles that haven't been 35860Sstevel@tonic-gate * signalled yet. Otherwise we can get a bogus call_reason. 35870Sstevel@tonic-gate * This can happen if thread A is making a call over a 35880Sstevel@tonic-gate * connection. If the server is killed, it will cause 35890Sstevel@tonic-gate * reset, and reason will default to EIO as a result of 35900Sstevel@tonic-gate * a T_ORDREL_IND. Thread B then attempts to recreate 35910Sstevel@tonic-gate * the connection but gets a T_DISCON_IND. If we set the 35920Sstevel@tonic-gate * call_reason code for all threads, then if thread A 35930Sstevel@tonic-gate * hasn't been dispatched yet, it will get the wrong 35940Sstevel@tonic-gate * reason. The bogus call_reason can make it harder to 35950Sstevel@tonic-gate * discriminate between calls that fail because the 35960Sstevel@tonic-gate * connection attempt failed versus those where the call 35970Sstevel@tonic-gate * may have been executed on the server. 35980Sstevel@tonic-gate */ 35990Sstevel@tonic-gate if (e->call_wq == q && e->call_notified == FALSE) { 36000Sstevel@tonic-gate RPCLOG(1, "clnt_dispatch_notifyall for queue %p ", 36010Sstevel@tonic-gate (void *)q); 36020Sstevel@tonic-gate RPCLOG(1, " aborting clnt_pending call %p\n", 36030Sstevel@tonic-gate (void *)e); 36040Sstevel@tonic-gate 36050Sstevel@tonic-gate if (msg_type == T_DISCON_IND) 36060Sstevel@tonic-gate e->call_reason = reason; 36070Sstevel@tonic-gate e->call_notified = TRUE; 36080Sstevel@tonic-gate /* 36090Sstevel@tonic-gate * Let the caller timeout, else he will retry 36100Sstevel@tonic-gate * immediately. 36110Sstevel@tonic-gate */ 36120Sstevel@tonic-gate e->call_status = RPC_XPRTFAILED; 36130Sstevel@tonic-gate 36140Sstevel@tonic-gate /* 36150Sstevel@tonic-gate * We used to just signal those threads 36160Sstevel@tonic-gate * waiting for a connection, (call_xid = 0). 36170Sstevel@tonic-gate * That meant that threads waiting for a response 36180Sstevel@tonic-gate * waited till their timeout expired. This 36190Sstevel@tonic-gate * could be a long time if they've specified a 36200Sstevel@tonic-gate * maximum timeout. (2^31 - 1). So we 36210Sstevel@tonic-gate * Signal all threads now. 36220Sstevel@tonic-gate */ 36230Sstevel@tonic-gate cv_signal(&e->call_cv); 36240Sstevel@tonic-gate } 36250Sstevel@tonic-gate } 36260Sstevel@tonic-gate mutex_exit(&clnt_pending_lock); 36270Sstevel@tonic-gate } 36280Sstevel@tonic-gate 36290Sstevel@tonic-gate 36300Sstevel@tonic-gate /*ARGSUSED*/ 36310Sstevel@tonic-gate /* 36320Sstevel@tonic-gate * after resuming a system that's been suspended for longer than the 36330Sstevel@tonic-gate * NFS server's idle timeout (svc_idle_timeout for Solaris 2), rfscall() 36340Sstevel@tonic-gate * generates "NFS server X not responding" and "NFS server X ok" messages; 36350Sstevel@tonic-gate * here we reset inet connections to cause a re-connect and avoid those 36360Sstevel@tonic-gate * NFS messages. see 4045054 36370Sstevel@tonic-gate */ 36380Sstevel@tonic-gate boolean_t 36390Sstevel@tonic-gate connmgr_cpr_reset(void *arg, int code) 36400Sstevel@tonic-gate { 36410Sstevel@tonic-gate struct cm_xprt *cxp; 36420Sstevel@tonic-gate 36430Sstevel@tonic-gate if (code == CB_CODE_CPR_CHKPT) 36440Sstevel@tonic-gate return (B_TRUE); 36450Sstevel@tonic-gate 36460Sstevel@tonic-gate if (mutex_tryenter(&connmgr_lock) == 0) 36470Sstevel@tonic-gate return (B_FALSE); 36480Sstevel@tonic-gate for (cxp = cm_hd; cxp; cxp = cxp->x_next) { 36490Sstevel@tonic-gate if ((cxp->x_family == AF_INET || cxp->x_family == AF_INET6) && 36506403Sgt29601 cxp->x_connected == TRUE) { 36510Sstevel@tonic-gate if (cxp->x_thread) 36520Sstevel@tonic-gate cxp->x_early_disc = TRUE; 36530Sstevel@tonic-gate else 36540Sstevel@tonic-gate cxp->x_connected = FALSE; 36550Sstevel@tonic-gate cxp->x_needdis = TRUE; 36560Sstevel@tonic-gate } 36570Sstevel@tonic-gate } 36580Sstevel@tonic-gate mutex_exit(&connmgr_lock); 36590Sstevel@tonic-gate return (B_TRUE); 36600Sstevel@tonic-gate } 36610Sstevel@tonic-gate 36620Sstevel@tonic-gate void 36630Sstevel@tonic-gate clnt_cots_stats_init(zoneid_t zoneid, struct rpc_cots_client **statsp) 36640Sstevel@tonic-gate { 36650Sstevel@tonic-gate 36660Sstevel@tonic-gate *statsp = (struct rpc_cots_client *)rpcstat_zone_init_common(zoneid, 36670Sstevel@tonic-gate "unix", "rpc_cots_client", (const kstat_named_t *)&cots_rcstat_tmpl, 36680Sstevel@tonic-gate sizeof (cots_rcstat_tmpl)); 36690Sstevel@tonic-gate } 36700Sstevel@tonic-gate 36710Sstevel@tonic-gate void 36720Sstevel@tonic-gate clnt_cots_stats_fini(zoneid_t zoneid, struct rpc_cots_client **statsp) 36730Sstevel@tonic-gate { 36740Sstevel@tonic-gate rpcstat_zone_fini_common(zoneid, "unix", "rpc_cots_client"); 36750Sstevel@tonic-gate kmem_free(*statsp, sizeof (cots_rcstat_tmpl)); 36760Sstevel@tonic-gate } 36770Sstevel@tonic-gate 36780Sstevel@tonic-gate void 36790Sstevel@tonic-gate clnt_cots_init(void) 36800Sstevel@tonic-gate { 36810Sstevel@tonic-gate mutex_init(&connmgr_lock, NULL, MUTEX_DEFAULT, NULL); 36820Sstevel@tonic-gate mutex_init(&clnt_pending_lock, NULL, MUTEX_DEFAULT, NULL); 36830Sstevel@tonic-gate 36840Sstevel@tonic-gate if (clnt_cots_hash_size < DEFAULT_MIN_HASH_SIZE) 36850Sstevel@tonic-gate clnt_cots_hash_size = DEFAULT_MIN_HASH_SIZE; 36860Sstevel@tonic-gate 36870Sstevel@tonic-gate cots_call_ht = call_table_init(clnt_cots_hash_size); 36880Sstevel@tonic-gate zone_key_create(&zone_cots_key, NULL, NULL, clnt_zone_destroy); 36890Sstevel@tonic-gate } 36900Sstevel@tonic-gate 36910Sstevel@tonic-gate void 36920Sstevel@tonic-gate clnt_cots_fini(void) 36930Sstevel@tonic-gate { 36940Sstevel@tonic-gate (void) zone_key_delete(zone_cots_key); 36950Sstevel@tonic-gate } 36960Sstevel@tonic-gate 36970Sstevel@tonic-gate /* 36980Sstevel@tonic-gate * Wait for TPI ack, returns success only if expected ack is received 36990Sstevel@tonic-gate * within timeout period. 37000Sstevel@tonic-gate */ 37010Sstevel@tonic-gate 37020Sstevel@tonic-gate static int 37030Sstevel@tonic-gate waitforack(calllist_t *e, t_scalar_t ack_prim, const struct timeval *waitp, 37040Sstevel@tonic-gate bool_t nosignal) 37050Sstevel@tonic-gate { 37060Sstevel@tonic-gate union T_primitives *tpr; 37070Sstevel@tonic-gate clock_t timout; 37080Sstevel@tonic-gate int cv_stat = 1; 37090Sstevel@tonic-gate 37100Sstevel@tonic-gate ASSERT(MUTEX_HELD(&clnt_pending_lock)); 37110Sstevel@tonic-gate while (e->call_reply == NULL) { 37120Sstevel@tonic-gate if (waitp != NULL) { 37130Sstevel@tonic-gate timout = waitp->tv_sec * drv_usectohz(MICROSEC) + 37140Sstevel@tonic-gate drv_usectohz(waitp->tv_usec) + lbolt; 37150Sstevel@tonic-gate if (nosignal) 37160Sstevel@tonic-gate cv_stat = cv_timedwait(&e->call_cv, 37170Sstevel@tonic-gate &clnt_pending_lock, timout); 37180Sstevel@tonic-gate else 37190Sstevel@tonic-gate cv_stat = cv_timedwait_sig(&e->call_cv, 37200Sstevel@tonic-gate &clnt_pending_lock, timout); 37210Sstevel@tonic-gate } else { 37220Sstevel@tonic-gate if (nosignal) 37230Sstevel@tonic-gate cv_wait(&e->call_cv, &clnt_pending_lock); 37240Sstevel@tonic-gate else 37250Sstevel@tonic-gate cv_stat = cv_wait_sig(&e->call_cv, 37260Sstevel@tonic-gate &clnt_pending_lock); 37270Sstevel@tonic-gate } 37280Sstevel@tonic-gate if (cv_stat == -1) 37290Sstevel@tonic-gate return (ETIME); 37300Sstevel@tonic-gate if (cv_stat == 0) 37310Sstevel@tonic-gate return (EINTR); 37326632Sgt29601 /* 37336632Sgt29601 * if we received an error from the server and we know a reply 37346632Sgt29601 * is not going to be sent, do not wait for the full timeout, 37356632Sgt29601 * return now. 37366632Sgt29601 */ 37376632Sgt29601 if (e->call_status == RPC_XPRTFAILED) 37386632Sgt29601 return (e->call_reason); 37390Sstevel@tonic-gate } 37400Sstevel@tonic-gate tpr = (union T_primitives *)e->call_reply->b_rptr; 37410Sstevel@tonic-gate if (tpr->type == ack_prim) 37420Sstevel@tonic-gate return (0); /* Success */ 37430Sstevel@tonic-gate 37440Sstevel@tonic-gate if (tpr->type == T_ERROR_ACK) { 37450Sstevel@tonic-gate if (tpr->error_ack.TLI_error == TSYSERR) 37460Sstevel@tonic-gate return (tpr->error_ack.UNIX_error); 37470Sstevel@tonic-gate else 37480Sstevel@tonic-gate return (t_tlitosyserr(tpr->error_ack.TLI_error)); 37490Sstevel@tonic-gate } 37500Sstevel@tonic-gate 37510Sstevel@tonic-gate return (EPROTO); /* unknown or unexpected primitive */ 37520Sstevel@tonic-gate } 3753