xref: /titanic_44/usr/src/uts/common/rpc/clnt_cots.c (revision 1a5e258f5471356ca102c7176637cdce45bac147)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22de8c4a14SErik Nordmark  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
287c478bd9Sstevel@tonic-gate  *		All Rights Reserved
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate /*
327c478bd9Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley 4.3 BSD
337c478bd9Sstevel@tonic-gate  * under license from the Regents of the University of California.
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate /*
387c478bd9Sstevel@tonic-gate  * Implements a kernel based, client side RPC over Connection Oriented
397c478bd9Sstevel@tonic-gate  * Transports (COTS).
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate  * Much of this file has been re-written to let NFS work better over slow
447c478bd9Sstevel@tonic-gate  * transports. A description follows.
457c478bd9Sstevel@tonic-gate  *
467c478bd9Sstevel@tonic-gate  * One of the annoying things about kRPC/COTS is that it will temporarily
477c478bd9Sstevel@tonic-gate  * create more than one connection between a client and server. This
487c478bd9Sstevel@tonic-gate  * happens because when a connection is made, the end-points entry in the
497c478bd9Sstevel@tonic-gate  * linked list of connections (headed by cm_hd), is removed so that other
507c478bd9Sstevel@tonic-gate  * threads don't mess with it. Went ahead and bit the bullet by keeping
517c478bd9Sstevel@tonic-gate  * the endpoint on the connection list and introducing state bits,
527c478bd9Sstevel@tonic-gate  * condition variables etc. to the connection entry data structure (struct
537c478bd9Sstevel@tonic-gate  * cm_xprt).
547c478bd9Sstevel@tonic-gate  *
557c478bd9Sstevel@tonic-gate  * Here is a summary of the changes to cm-xprt:
567c478bd9Sstevel@tonic-gate  *
577c478bd9Sstevel@tonic-gate  *	x_ctime is the timestamp of when the endpoint was last
587c478bd9Sstevel@tonic-gate  *	connected or disconnected. If an end-point is ever disconnected
597c478bd9Sstevel@tonic-gate  *	or re-connected, then any outstanding RPC request is presumed
607c478bd9Sstevel@tonic-gate  *	lost, telling clnt_cots_kcallit that it needs to re-send the
617c478bd9Sstevel@tonic-gate  *	request, not just wait for the original request's reply to
627c478bd9Sstevel@tonic-gate  *	arrive.
637c478bd9Sstevel@tonic-gate  *
647c478bd9Sstevel@tonic-gate  *	x_thread flag which tells us if a thread is doing a connection attempt.
657c478bd9Sstevel@tonic-gate  *
667c478bd9Sstevel@tonic-gate  *	x_waitdis flag which tells us we are waiting a disconnect ACK.
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  *	x_needdis flag which tells us we need to send a T_DISCONN_REQ
697c478bd9Sstevel@tonic-gate  *	to kill the connection.
707c478bd9Sstevel@tonic-gate  *
717c478bd9Sstevel@tonic-gate  *	x_needrel flag which tells us we need to send a T_ORDREL_REQ to
727c478bd9Sstevel@tonic-gate  *	gracefully close the connection.
737c478bd9Sstevel@tonic-gate  *
747c478bd9Sstevel@tonic-gate  *	#defined bitmasks for the all the b_* bits so that more
757c478bd9Sstevel@tonic-gate  *	efficient (and at times less clumsy) masks can be used to
767c478bd9Sstevel@tonic-gate  *	manipulated state in cases where multiple bits have to
777c478bd9Sstevel@tonic-gate  *	set/cleared/checked in the same critical section.
787c478bd9Sstevel@tonic-gate  *
797c478bd9Sstevel@tonic-gate  *	x_conn_cv and x_dis-_cv are new condition variables to let
807c478bd9Sstevel@tonic-gate  *	threads knows when the connection attempt is done, and to let
817c478bd9Sstevel@tonic-gate  *	the connecting thread know when the disconnect handshake is
827c478bd9Sstevel@tonic-gate  *	done.
837c478bd9Sstevel@tonic-gate  *
847c478bd9Sstevel@tonic-gate  * Added the CONN_HOLD() macro so that all reference holds have the same
857c478bd9Sstevel@tonic-gate  * look and feel.
867c478bd9Sstevel@tonic-gate  *
877c478bd9Sstevel@tonic-gate  * In the private (cku_private) portion of the client handle,
887c478bd9Sstevel@tonic-gate  *
897c478bd9Sstevel@tonic-gate  *	cku_flags replaces the cku_sent a boolean. cku_flags keeps
907c478bd9Sstevel@tonic-gate  *	track of whether a request as been sent, and whether the
917c478bd9Sstevel@tonic-gate  *	client's handles call record is on the dispatch list (so that
927c478bd9Sstevel@tonic-gate  *	the reply can be matched by XID to the right client handle).
937c478bd9Sstevel@tonic-gate  *	The idea of CKU_ONQUEUE is that we can exit clnt_cots_kcallit()
947c478bd9Sstevel@tonic-gate  *	and still have the response find the right client handle so
957c478bd9Sstevel@tonic-gate  *	that the retry of CLNT_CALL() gets the result. Testing, found
967c478bd9Sstevel@tonic-gate  *	situations where if the timeout was increased, performance
977c478bd9Sstevel@tonic-gate  *	degraded. This was due to us hitting a window where the thread
987c478bd9Sstevel@tonic-gate  *	was back in rfscall() (probably printing server not responding)
997c478bd9Sstevel@tonic-gate  *	while the response came back but no place to put it.
1007c478bd9Sstevel@tonic-gate  *
1017c478bd9Sstevel@tonic-gate  *	cku_ctime is just a cache of x_ctime. If they match,
1027c478bd9Sstevel@tonic-gate  *	clnt_cots_kcallit() won't to send a retry (unless the maximum
1037c478bd9Sstevel@tonic-gate  *	receive count limit as been reached). If the don't match, then
1047c478bd9Sstevel@tonic-gate  *	we assume the request has been lost, and a retry of the request
1057c478bd9Sstevel@tonic-gate  *	is needed.
1067c478bd9Sstevel@tonic-gate  *
1077c478bd9Sstevel@tonic-gate  *	cku_recv_attempts counts the number of receive count attempts
1087c478bd9Sstevel@tonic-gate  *	after one try is sent on the wire.
1097c478bd9Sstevel@tonic-gate  *
1107c478bd9Sstevel@tonic-gate  * Added the clnt_delay() routine so that interruptible and
1117c478bd9Sstevel@tonic-gate  * noninterruptible delays are possible.
1127c478bd9Sstevel@tonic-gate  *
1137c478bd9Sstevel@tonic-gate  * CLNT_MIN_TIMEOUT has been bumped to 10 seconds from 3. This is used to
1147c478bd9Sstevel@tonic-gate  * control how long the client delays before returned after getting
1157c478bd9Sstevel@tonic-gate  * ECONNREFUSED. At 3 seconds, 8 client threads per mount really does bash
1167c478bd9Sstevel@tonic-gate  * a server that may be booting and not yet started nfsd.
1177c478bd9Sstevel@tonic-gate  *
1187c478bd9Sstevel@tonic-gate  * CLNT_MAXRECV_WITHOUT_RETRY is a new macro (value of 3) (with a tunable)
1197c478bd9Sstevel@tonic-gate  * Why don't we just wait forever (receive an infinite # of times)?
1207c478bd9Sstevel@tonic-gate  * Because the server may have rebooted. More insidious is that some
1217c478bd9Sstevel@tonic-gate  * servers (ours) will drop NFS/TCP requests in some cases. This is bad,
1227c478bd9Sstevel@tonic-gate  * but it is a reality.
1237c478bd9Sstevel@tonic-gate  *
1247c478bd9Sstevel@tonic-gate  * The case of a server doing orderly release really messes up the
1257c478bd9Sstevel@tonic-gate  * client's recovery, especially if the server's TCP implementation is
1267c478bd9Sstevel@tonic-gate  * buggy.  It was found was that the kRPC/COTS client was breaking some
1277c478bd9Sstevel@tonic-gate  * TPI rules, such as not waiting for the acknowledgement of a
1287c478bd9Sstevel@tonic-gate  * T_DISCON_REQ (hence the added case statements T_ERROR_ACK, T_OK_ACK and
1297c478bd9Sstevel@tonic-gate  * T_DISCON_REQ in clnt_dispatch_notifyall()).
1307c478bd9Sstevel@tonic-gate  *
1317c478bd9Sstevel@tonic-gate  * One of things that we've seen is that a kRPC TCP endpoint goes into
1327c478bd9Sstevel@tonic-gate  * TIMEWAIT and a thus a reconnect takes a long time to satisfy because
1337c478bd9Sstevel@tonic-gate  * that the TIMEWAIT state takes a while to finish.  If a server sends a
1347c478bd9Sstevel@tonic-gate  * T_ORDREL_IND, there is little point in an RPC client doing a
1357c478bd9Sstevel@tonic-gate  * T_ORDREL_REQ, because the RPC request isn't going to make it (the
1367c478bd9Sstevel@tonic-gate  * server is saying that it won't accept any more data). So kRPC was
1377c478bd9Sstevel@tonic-gate  * changed to send a T_DISCON_REQ when we get a T_ORDREL_IND. So now the
1387c478bd9Sstevel@tonic-gate  * connection skips the TIMEWAIT state and goes straight to a bound state
1397c478bd9Sstevel@tonic-gate  * that kRPC can quickly switch to connected.
1407c478bd9Sstevel@tonic-gate  *
1417c478bd9Sstevel@tonic-gate  * Code that issues TPI request must use waitforack() to wait for the
1427c478bd9Sstevel@tonic-gate  * corresponding ack (assuming there is one) in any future modifications.
1437c478bd9Sstevel@tonic-gate  * This works around problems that may be introduced by breaking TPI rules
1447c478bd9Sstevel@tonic-gate  * (by submitting new calls before earlier requests have been acked) in the
1457c478bd9Sstevel@tonic-gate  * case of a signal or other early return.  waitforack() depends on
1467c478bd9Sstevel@tonic-gate  * clnt_dispatch_notifyconn() to issue the wakeup when the ack
1477c478bd9Sstevel@tonic-gate  * arrives, so adding new TPI calls may require corresponding changes
1487c478bd9Sstevel@tonic-gate  * to clnt_dispatch_notifyconn(). Presently, the timeout period is based on
1497c478bd9Sstevel@tonic-gate  * CLNT_MIN_TIMEOUT which is 10 seconds. If you modify this value, be sure
1507c478bd9Sstevel@tonic-gate  * not to set it too low or TPI ACKS will be lost.
1517c478bd9Sstevel@tonic-gate  */
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate #include <sys/param.h>
1547c478bd9Sstevel@tonic-gate #include <sys/types.h>
1557c478bd9Sstevel@tonic-gate #include <sys/user.h>
1567c478bd9Sstevel@tonic-gate #include <sys/systm.h>
1577c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
1587c478bd9Sstevel@tonic-gate #include <sys/proc.h>
1597c478bd9Sstevel@tonic-gate #include <sys/socket.h>
1607c478bd9Sstevel@tonic-gate #include <sys/file.h>
1617c478bd9Sstevel@tonic-gate #include <sys/stream.h>
1627c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
1637c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
1647c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
1657c478bd9Sstevel@tonic-gate #include <sys/timod.h>
1667c478bd9Sstevel@tonic-gate #include <sys/tiuser.h>
1677c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
1687c478bd9Sstevel@tonic-gate #include <sys/t_kuser.h>
1697c478bd9Sstevel@tonic-gate #include <sys/fcntl.h>
1707c478bd9Sstevel@tonic-gate #include <sys/errno.h>
1717c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
1727c478bd9Sstevel@tonic-gate #include <sys/debug.h>
1737c478bd9Sstevel@tonic-gate #include <sys/systm.h>
1747c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
1757c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
1767c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
1777c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
1787c478bd9Sstevel@tonic-gate #include <sys/time.h>
1797c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
1807c478bd9Sstevel@tonic-gate #include <sys/callb.h>
1817c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
1827c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
183125a8fd9SSiddheshwar Mahesh #include <sys/sdt.h>
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate #include <netinet/in.h>
1867c478bd9Sstevel@tonic-gate #include <netinet/tcp.h>
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate #include <rpc/types.h>
1897c478bd9Sstevel@tonic-gate #include <rpc/xdr.h>
1907c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
1917c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
1927c478bd9Sstevel@tonic-gate #include <rpc/rpc_msg.h>
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate #define	COTS_DEFAULT_ALLOCSIZE	2048
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate #define	WIRE_HDR_SIZE	20	/* serialized call header, sans proc number */
1977c478bd9Sstevel@tonic-gate #define	MSG_OFFSET	128	/* offset of call into the mblk */
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate const char *kinet_ntop6(uchar_t *, char *, size_t);
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate static int	clnt_cots_ksettimers(CLIENT *, struct rpc_timers *,
2027c478bd9Sstevel@tonic-gate     struct rpc_timers *, int, void(*)(int, int, caddr_t), caddr_t, uint32_t);
2037c478bd9Sstevel@tonic-gate static enum clnt_stat	clnt_cots_kcallit(CLIENT *, rpcproc_t, xdrproc_t,
2047c478bd9Sstevel@tonic-gate     caddr_t, xdrproc_t, caddr_t, struct timeval);
2057c478bd9Sstevel@tonic-gate static void	clnt_cots_kabort(CLIENT *);
2067c478bd9Sstevel@tonic-gate static void	clnt_cots_kerror(CLIENT *, struct rpc_err *);
2077c478bd9Sstevel@tonic-gate static bool_t	clnt_cots_kfreeres(CLIENT *, xdrproc_t, caddr_t);
2087c478bd9Sstevel@tonic-gate static void	clnt_cots_kdestroy(CLIENT *);
2097c478bd9Sstevel@tonic-gate static bool_t	clnt_cots_kcontrol(CLIENT *, int, char *);
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate /* List of transports managed by the connection manager. */
2137c478bd9Sstevel@tonic-gate struct cm_xprt {
2147c478bd9Sstevel@tonic-gate 	TIUSER		*x_tiptr;	/* transport handle */
2157c478bd9Sstevel@tonic-gate 	queue_t		*x_wq;		/* send queue */
2167c478bd9Sstevel@tonic-gate 	clock_t		x_time;		/* last time we handed this xprt out */
2177c478bd9Sstevel@tonic-gate 	clock_t		x_ctime;	/* time we went to CONNECTED */
2187c478bd9Sstevel@tonic-gate 	int		x_tidu_size;    /* TIDU size of this transport */
2197c478bd9Sstevel@tonic-gate 	union {
2207c478bd9Sstevel@tonic-gate 	    struct {
2217c478bd9Sstevel@tonic-gate 		unsigned int
2227c478bd9Sstevel@tonic-gate #ifdef	_BIT_FIELDS_HTOL
2237c478bd9Sstevel@tonic-gate 		b_closing:	1,	/* we've sent a ord rel on this conn */
2247c478bd9Sstevel@tonic-gate 		b_dead:		1,	/* transport is closed or disconn */
2257c478bd9Sstevel@tonic-gate 		b_doomed:	1,	/* too many conns, let this go idle */
2267c478bd9Sstevel@tonic-gate 		b_connected:	1,	/* this connection is connected */
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 		b_ordrel:	1,	/* do an orderly release? */
2297c478bd9Sstevel@tonic-gate 		b_thread:	1,	/* thread doing connect */
2307c478bd9Sstevel@tonic-gate 		b_waitdis:	1,	/* waiting for disconnect ACK */
2317c478bd9Sstevel@tonic-gate 		b_needdis:	1,	/* need T_DISCON_REQ */
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		b_needrel:	1,	/* need T_ORDREL_REQ */
2347c478bd9Sstevel@tonic-gate 		b_early_disc:	1,	/* got a T_ORDREL_IND or T_DISCON_IND */
2357c478bd9Sstevel@tonic-gate 					/* disconnect during connect */
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 		b_pad:		22;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate #endif
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate #ifdef	_BIT_FIELDS_LTOH
2427c478bd9Sstevel@tonic-gate 		b_pad:		22,
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 		b_early_disc:	1,	/* got a T_ORDREL_IND or T_DISCON_IND */
2457c478bd9Sstevel@tonic-gate 					/* disconnect during connect */
2467c478bd9Sstevel@tonic-gate 		b_needrel:	1,	/* need T_ORDREL_REQ */
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 		b_needdis:	1,	/* need T_DISCON_REQ */
2497c478bd9Sstevel@tonic-gate 		b_waitdis:	1,	/* waiting for disconnect ACK */
2507c478bd9Sstevel@tonic-gate 		b_thread:	1,	/* thread doing connect */
2517c478bd9Sstevel@tonic-gate 		b_ordrel:	1,	/* do an orderly release? */
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 		b_connected:	1,	/* this connection is connected */
2547c478bd9Sstevel@tonic-gate 		b_doomed:	1,	/* too many conns, let this go idle */
2557c478bd9Sstevel@tonic-gate 		b_dead:		1,	/* transport is closed or disconn */
2567c478bd9Sstevel@tonic-gate 		b_closing:	1;	/* we've sent a ord rel on this conn */
2577c478bd9Sstevel@tonic-gate #endif
2587c478bd9Sstevel@tonic-gate 	    } bit;	    unsigned int word;
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate #define	x_closing	x_state.bit.b_closing
2617c478bd9Sstevel@tonic-gate #define	x_dead		x_state.bit.b_dead
2627c478bd9Sstevel@tonic-gate #define	x_doomed	x_state.bit.b_doomed
2637c478bd9Sstevel@tonic-gate #define	x_connected	x_state.bit.b_connected
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate #define	x_ordrel	x_state.bit.b_ordrel
2667c478bd9Sstevel@tonic-gate #define	x_thread	x_state.bit.b_thread
2677c478bd9Sstevel@tonic-gate #define	x_waitdis	x_state.bit.b_waitdis
2687c478bd9Sstevel@tonic-gate #define	x_needdis	x_state.bit.b_needdis
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate #define	x_needrel	x_state.bit.b_needrel
2717c478bd9Sstevel@tonic-gate #define	x_early_disc    x_state.bit.b_early_disc
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate #define	x_state_flags	x_state.word
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate #define	X_CLOSING	0x80000000
2767c478bd9Sstevel@tonic-gate #define	X_DEAD		0x40000000
2777c478bd9Sstevel@tonic-gate #define	X_DOOMED	0x20000000
2787c478bd9Sstevel@tonic-gate #define	X_CONNECTED	0x10000000
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate #define	X_ORDREL	0x08000000
2817c478bd9Sstevel@tonic-gate #define	X_THREAD	0x04000000
2827c478bd9Sstevel@tonic-gate #define	X_WAITDIS	0x02000000
2837c478bd9Sstevel@tonic-gate #define	X_NEEDDIS	0x01000000
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate #define	X_NEEDREL	0x00800000
2867c478bd9Sstevel@tonic-gate #define	X_EARLYDISC	0x00400000
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate #define	X_BADSTATES	(X_CLOSING | X_DEAD | X_DOOMED)
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	}		x_state;
2917c478bd9Sstevel@tonic-gate 	int		x_ref;		/* number of users of this xprt */
2927c478bd9Sstevel@tonic-gate 	int		x_family;	/* address family of transport */
2937c478bd9Sstevel@tonic-gate 	dev_t		x_rdev;		/* device number of transport */
2947c478bd9Sstevel@tonic-gate 	struct cm_xprt	*x_next;
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	struct netbuf	x_server;	/* destination address */
2977c478bd9Sstevel@tonic-gate 	struct netbuf	x_src;		/* src address (for retries) */
2987c478bd9Sstevel@tonic-gate 	kmutex_t	x_lock;		/* lock on this entry */
2997c478bd9Sstevel@tonic-gate 	kcondvar_t	x_cv;		/* to signal when can be closed */
3007c478bd9Sstevel@tonic-gate 	kcondvar_t	x_conn_cv;	/* to signal when connection attempt */
3017c478bd9Sstevel@tonic-gate 					/* is complete */
3027c478bd9Sstevel@tonic-gate 	kstat_t		*x_ksp;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	kcondvar_t	x_dis_cv;	/* to signal when disconnect attempt */
3057c478bd9Sstevel@tonic-gate 					/* is complete */
3067c478bd9Sstevel@tonic-gate 	zoneid_t	x_zoneid;	/* zone this xprt belongs to */
3077c478bd9Sstevel@tonic-gate };
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate typedef struct cm_kstat_xprt {
3107c478bd9Sstevel@tonic-gate 	kstat_named_t	x_wq;
3117c478bd9Sstevel@tonic-gate 	kstat_named_t	x_server;
3127c478bd9Sstevel@tonic-gate 	kstat_named_t	x_family;
3137c478bd9Sstevel@tonic-gate 	kstat_named_t	x_rdev;
3147c478bd9Sstevel@tonic-gate 	kstat_named_t	x_time;
3157c478bd9Sstevel@tonic-gate 	kstat_named_t	x_state;
3167c478bd9Sstevel@tonic-gate 	kstat_named_t	x_ref;
3177c478bd9Sstevel@tonic-gate 	kstat_named_t	x_port;
3187c478bd9Sstevel@tonic-gate } cm_kstat_xprt_t;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate static cm_kstat_xprt_t cm_kstat_template = {
3217c478bd9Sstevel@tonic-gate 	{ "write_queue", KSTAT_DATA_UINT32 },
3227c478bd9Sstevel@tonic-gate 	{ "server",	KSTAT_DATA_STRING },
3237c478bd9Sstevel@tonic-gate 	{ "addr_family", KSTAT_DATA_UINT32 },
3247c478bd9Sstevel@tonic-gate 	{ "device",	KSTAT_DATA_UINT32 },
3257c478bd9Sstevel@tonic-gate 	{ "time_stamp",	KSTAT_DATA_UINT32 },
3267c478bd9Sstevel@tonic-gate 	{ "status",	KSTAT_DATA_UINT32 },
3277c478bd9Sstevel@tonic-gate 	{ "ref_count",	KSTAT_DATA_INT32 },
3287c478bd9Sstevel@tonic-gate 	{ "port",	KSTAT_DATA_UINT32 },
3297c478bd9Sstevel@tonic-gate };
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate /*
3327c478bd9Sstevel@tonic-gate  * The inverse of this is connmgr_release().
3337c478bd9Sstevel@tonic-gate  */
3347c478bd9Sstevel@tonic-gate #define	CONN_HOLD(Cm_entry)	{\
3357c478bd9Sstevel@tonic-gate 	mutex_enter(&(Cm_entry)->x_lock);	\
3367c478bd9Sstevel@tonic-gate 	(Cm_entry)->x_ref++;	\
3377c478bd9Sstevel@tonic-gate 	mutex_exit(&(Cm_entry)->x_lock);	\
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate /*
3427c478bd9Sstevel@tonic-gate  * Private data per rpc handle.  This structure is allocated by
3437c478bd9Sstevel@tonic-gate  * clnt_cots_kcreate, and freed by clnt_cots_kdestroy.
3447c478bd9Sstevel@tonic-gate  */
3457c478bd9Sstevel@tonic-gate typedef struct cku_private_s {
3467c478bd9Sstevel@tonic-gate 	CLIENT			cku_client;	/* client handle */
3477c478bd9Sstevel@tonic-gate 	calllist_t		cku_call;	/* for dispatching calls */
3487c478bd9Sstevel@tonic-gate 	struct rpc_err		cku_err;	/* error status */
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	struct netbuf		cku_srcaddr;	/* source address for retries */
3517c478bd9Sstevel@tonic-gate 	int			cku_addrfmly;  /* for binding port */
3527c478bd9Sstevel@tonic-gate 	struct netbuf		cku_addr;	/* remote address */
3537c478bd9Sstevel@tonic-gate 	dev_t			cku_device;	/* device to use */
3547c478bd9Sstevel@tonic-gate 	uint_t			cku_flags;
3557c478bd9Sstevel@tonic-gate #define	CKU_ONQUEUE		0x1
3567c478bd9Sstevel@tonic-gate #define	CKU_SENT		0x2
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	bool_t			cku_progress;	/* for CLSET_PROGRESS */
3597c478bd9Sstevel@tonic-gate 	uint32_t		cku_xid;	/* current XID */
3607c478bd9Sstevel@tonic-gate 	clock_t			cku_ctime;	/* time stamp of when */
3617c478bd9Sstevel@tonic-gate 						/* connection was created */
3627c478bd9Sstevel@tonic-gate 	uint_t			cku_recv_attempts;
3637c478bd9Sstevel@tonic-gate 	XDR			cku_outxdr;	/* xdr routine for output */
3647c478bd9Sstevel@tonic-gate 	XDR			cku_inxdr;	/* xdr routine for input */
3657c478bd9Sstevel@tonic-gate 	char			cku_rpchdr[WIRE_HDR_SIZE + 4];
3667c478bd9Sstevel@tonic-gate 						/* pre-serialized rpc header */
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	uint_t			cku_outbuflen;	/* default output mblk length */
3697c478bd9Sstevel@tonic-gate 	struct cred		*cku_cred;	/* credentials */
3707c478bd9Sstevel@tonic-gate 	bool_t			cku_nodelayonerr;
3717c478bd9Sstevel@tonic-gate 						/* for CLSET_NODELAYONERR */
3727c478bd9Sstevel@tonic-gate 	int			cku_useresvport; /* Use reserved port */
3737c478bd9Sstevel@tonic-gate 	struct rpc_cots_client	*cku_stats;	/* stats for zone */
3747c478bd9Sstevel@tonic-gate } cku_private_t;
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate static struct cm_xprt *connmgr_wrapconnect(struct cm_xprt *,
3777c478bd9Sstevel@tonic-gate 	const struct timeval *, struct netbuf *, int, struct netbuf *,
378de8c4a14SErik Nordmark 	struct rpc_err *, bool_t, bool_t, cred_t *);
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate static bool_t	connmgr_connect(struct cm_xprt *, queue_t *, struct netbuf *,
3817c478bd9Sstevel@tonic-gate 				int, calllist_t *, int *, bool_t reconnect,
382de8c4a14SErik Nordmark 				const struct timeval *, bool_t, cred_t *);
3837c478bd9Sstevel@tonic-gate 
3842081ac19SDai Ngo static void	*connmgr_opt_getoff(mblk_t *mp, t_uscalar_t offset,
3852081ac19SDai Ngo 				t_uscalar_t length, uint_t align_size);
3862081ac19SDai Ngo static bool_t	connmgr_setbufsz(calllist_t *e, queue_t *wq, cred_t *cr);
3872081ac19SDai Ngo static bool_t	connmgr_getopt_int(queue_t *wq, int level, int name, int *val,
3882081ac19SDai Ngo 				calllist_t *e, cred_t *cr);
3892081ac19SDai Ngo static bool_t	connmgr_setopt_int(queue_t *wq, int level, int name, int val,
3902081ac19SDai Ngo 				calllist_t *e, cred_t *cr);
391de8c4a14SErik Nordmark static bool_t	connmgr_setopt(queue_t *, int, int, calllist_t *, cred_t *cr);
3927c478bd9Sstevel@tonic-gate static void	connmgr_sndrel(struct cm_xprt *);
3937c478bd9Sstevel@tonic-gate static void	connmgr_snddis(struct cm_xprt *);
3947c478bd9Sstevel@tonic-gate static void	connmgr_close(struct cm_xprt *);
3957c478bd9Sstevel@tonic-gate static void	connmgr_release(struct cm_xprt *);
3967c478bd9Sstevel@tonic-gate static struct cm_xprt *connmgr_wrapget(struct netbuf *, const struct timeval *,
3977c478bd9Sstevel@tonic-gate 	cku_private_t *);
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate static struct cm_xprt *connmgr_get(struct netbuf *, const struct timeval *,
4007c478bd9Sstevel@tonic-gate 	struct netbuf *, int, struct netbuf *, struct rpc_err *, dev_t,
401de8c4a14SErik Nordmark 	bool_t, int, cred_t *);
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate static void connmgr_cancelconn(struct cm_xprt *);
4047c478bd9Sstevel@tonic-gate static enum clnt_stat connmgr_cwait(struct cm_xprt *, const struct timeval *,
4057c478bd9Sstevel@tonic-gate 	bool_t);
4067c478bd9Sstevel@tonic-gate static void connmgr_dis_and_wait(struct cm_xprt *);
4077c478bd9Sstevel@tonic-gate 
408125a8fd9SSiddheshwar Mahesh static int	clnt_dispatch_send(queue_t *, mblk_t *, calllist_t *, uint_t,
4097c478bd9Sstevel@tonic-gate 					uint_t);
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate static int clnt_delay(clock_t, bool_t);
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate static int waitforack(calllist_t *, t_scalar_t, const struct timeval *, bool_t);
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate /*
4167c478bd9Sstevel@tonic-gate  * Operations vector for TCP/IP based RPC
4177c478bd9Sstevel@tonic-gate  */
4187c478bd9Sstevel@tonic-gate static struct clnt_ops tcp_ops = {
4197c478bd9Sstevel@tonic-gate 	clnt_cots_kcallit,	/* do rpc call */
4207c478bd9Sstevel@tonic-gate 	clnt_cots_kabort,	/* abort call */
4217c478bd9Sstevel@tonic-gate 	clnt_cots_kerror,	/* return error status */
4227c478bd9Sstevel@tonic-gate 	clnt_cots_kfreeres,	/* free results */
4237c478bd9Sstevel@tonic-gate 	clnt_cots_kdestroy,	/* destroy rpc handle */
4247c478bd9Sstevel@tonic-gate 	clnt_cots_kcontrol,	/* the ioctl() of rpc */
4257c478bd9Sstevel@tonic-gate 	clnt_cots_ksettimers,	/* set retry timers */
4267c478bd9Sstevel@tonic-gate };
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate static int rpc_kstat_instance = 0;  /* keeps the current instance */
4297c478bd9Sstevel@tonic-gate 				/* number for the next kstat_create */
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate static struct cm_xprt *cm_hd = NULL;
4327c478bd9Sstevel@tonic-gate static kmutex_t connmgr_lock;	/* for connection mngr's list of transports */
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate extern kmutex_t clnt_max_msg_lock;
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate static calllist_t *clnt_pending = NULL;
4377c478bd9Sstevel@tonic-gate extern kmutex_t clnt_pending_lock;
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate static int clnt_cots_hash_size = DEFAULT_HASH_SIZE;
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate static call_table_t *cots_call_ht;
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate static const struct rpc_cots_client {
4447c478bd9Sstevel@tonic-gate 	kstat_named_t	rccalls;
4457c478bd9Sstevel@tonic-gate 	kstat_named_t	rcbadcalls;
4467c478bd9Sstevel@tonic-gate 	kstat_named_t	rcbadxids;
4477c478bd9Sstevel@tonic-gate 	kstat_named_t	rctimeouts;
4487c478bd9Sstevel@tonic-gate 	kstat_named_t	rcnewcreds;
4497c478bd9Sstevel@tonic-gate 	kstat_named_t	rcbadverfs;
4507c478bd9Sstevel@tonic-gate 	kstat_named_t	rctimers;
4517c478bd9Sstevel@tonic-gate 	kstat_named_t	rccantconn;
4527c478bd9Sstevel@tonic-gate 	kstat_named_t	rcnomem;
4537c478bd9Sstevel@tonic-gate 	kstat_named_t	rcintrs;
4547c478bd9Sstevel@tonic-gate } cots_rcstat_tmpl = {
4557c478bd9Sstevel@tonic-gate 	{ "calls",	KSTAT_DATA_UINT64 },
4567c478bd9Sstevel@tonic-gate 	{ "badcalls",	KSTAT_DATA_UINT64 },
4577c478bd9Sstevel@tonic-gate 	{ "badxids",	KSTAT_DATA_UINT64 },
4587c478bd9Sstevel@tonic-gate 	{ "timeouts",	KSTAT_DATA_UINT64 },
4597c478bd9Sstevel@tonic-gate 	{ "newcreds",	KSTAT_DATA_UINT64 },
4607c478bd9Sstevel@tonic-gate 	{ "badverfs",	KSTAT_DATA_UINT64 },
4617c478bd9Sstevel@tonic-gate 	{ "timers",	KSTAT_DATA_UINT64 },
4627c478bd9Sstevel@tonic-gate 	{ "cantconn",	KSTAT_DATA_UINT64 },
4637c478bd9Sstevel@tonic-gate 	{ "nomem",	KSTAT_DATA_UINT64 },
4647c478bd9Sstevel@tonic-gate 	{ "interrupts", KSTAT_DATA_UINT64 }
4657c478bd9Sstevel@tonic-gate };
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate #define	COTSRCSTAT_INCR(p, x)	\
468*1a5e258fSJosef 'Jeff' Sipek 	atomic_inc_64(&(p)->x.value.ui64)
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate #define	CLNT_MAX_CONNS	1	/* concurrent connections between clnt/srvr */
47181dbf0b5SDai Ngo int clnt_max_conns = CLNT_MAX_CONNS;
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate #define	CLNT_MIN_TIMEOUT	10	/* seconds to wait after we get a */
4747c478bd9Sstevel@tonic-gate 					/* connection reset */
4757c478bd9Sstevel@tonic-gate #define	CLNT_MIN_CONNTIMEOUT	5	/* seconds to wait for a connection */
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 
47881dbf0b5SDai Ngo int clnt_cots_min_tout = CLNT_MIN_TIMEOUT;
47981dbf0b5SDai Ngo int clnt_cots_min_conntout = CLNT_MIN_CONNTIMEOUT;
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate  * Limit the number of times we will attempt to receive a reply without
4837c478bd9Sstevel@tonic-gate  * re-sending a response.
4847c478bd9Sstevel@tonic-gate  */
4857c478bd9Sstevel@tonic-gate #define	CLNT_MAXRECV_WITHOUT_RETRY	3
48681dbf0b5SDai Ngo uint_t clnt_cots_maxrecv	= CLNT_MAXRECV_WITHOUT_RETRY;
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate uint_t *clnt_max_msg_sizep;
4897c478bd9Sstevel@tonic-gate void (*clnt_stop_idle)(queue_t *wq);
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate #define	ptoh(p)		(&((p)->cku_client))
4927c478bd9Sstevel@tonic-gate #define	htop(h)		((cku_private_t *)((h)->cl_private))
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate /*
4957c478bd9Sstevel@tonic-gate  * Times to retry
4967c478bd9Sstevel@tonic-gate  */
4977c478bd9Sstevel@tonic-gate #define	REFRESHES	2	/* authentication refreshes */
4987c478bd9Sstevel@tonic-gate 
4995bd9f8f1Srg137905 /*
5005bd9f8f1Srg137905  * The following is used to determine the global default behavior for
5015bd9f8f1Srg137905  * COTS when binding to a local port.
5025bd9f8f1Srg137905  *
5035bd9f8f1Srg137905  * If the value is set to 1 the default will be to select a reserved
5045bd9f8f1Srg137905  * (aka privileged) port, if the value is zero the default will be to
5055bd9f8f1Srg137905  * use non-reserved ports.  Users of kRPC may override this by using
5065bd9f8f1Srg137905  * CLNT_CONTROL() and CLSET_BINDRESVPORT.
5075bd9f8f1Srg137905  */
50881dbf0b5SDai Ngo int clnt_cots_do_bindresvport = 1;
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate static zone_key_t zone_cots_key;
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate /*
5132081ac19SDai Ngo  * Defaults TCP send and receive buffer size for RPC connections.
5142081ac19SDai Ngo  * These values can be tuned by /etc/system.
5152081ac19SDai Ngo  */
5162081ac19SDai Ngo int rpc_send_bufsz = 1024*1024;
5172081ac19SDai Ngo int rpc_recv_bufsz = 1024*1024;
5182081ac19SDai Ngo /*
5192081ac19SDai Ngo  * To use system-wide default for TCP send and receive buffer size,
5202081ac19SDai Ngo  * use /etc/system to set rpc_default_tcp_bufsz to 1:
5212081ac19SDai Ngo  *
5222081ac19SDai Ngo  * set rpcmod:rpc_default_tcp_bufsz=1
5232081ac19SDai Ngo  */
5242081ac19SDai Ngo int rpc_default_tcp_bufsz = 0;
5252081ac19SDai Ngo 
5262081ac19SDai Ngo /*
5277c478bd9Sstevel@tonic-gate  * We need to do this after all kernel threads in the zone have exited.
5287c478bd9Sstevel@tonic-gate  */
5297c478bd9Sstevel@tonic-gate /* ARGSUSED */
5307c478bd9Sstevel@tonic-gate static void
clnt_zone_destroy(zoneid_t zoneid,void * unused)5317c478bd9Sstevel@tonic-gate clnt_zone_destroy(zoneid_t zoneid, void *unused)
5327c478bd9Sstevel@tonic-gate {
5337c478bd9Sstevel@tonic-gate 	struct cm_xprt **cmp;
5347c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
5357c478bd9Sstevel@tonic-gate 	struct cm_xprt *freelist = NULL;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
5387c478bd9Sstevel@tonic-gate 	cmp = &cm_hd;
5397c478bd9Sstevel@tonic-gate 	while ((cm_entry = *cmp) != NULL) {
5407c478bd9Sstevel@tonic-gate 		if (cm_entry->x_zoneid == zoneid) {
5417c478bd9Sstevel@tonic-gate 			*cmp = cm_entry->x_next;
5427c478bd9Sstevel@tonic-gate 			cm_entry->x_next = freelist;
5437c478bd9Sstevel@tonic-gate 			freelist = cm_entry;
5447c478bd9Sstevel@tonic-gate 		} else {
5457c478bd9Sstevel@tonic-gate 			cmp = &cm_entry->x_next;
5467c478bd9Sstevel@tonic-gate 		}
5477c478bd9Sstevel@tonic-gate 	}
5487c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
5497c478bd9Sstevel@tonic-gate 	while ((cm_entry = freelist) != NULL) {
5507c478bd9Sstevel@tonic-gate 		freelist = cm_entry->x_next;
5517c478bd9Sstevel@tonic-gate 		connmgr_close(cm_entry);
5527c478bd9Sstevel@tonic-gate 	}
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate int
clnt_cots_kcreate(dev_t dev,struct netbuf * addr,int family,rpcprog_t prog,rpcvers_t vers,uint_t max_msgsize,cred_t * cred,CLIENT ** ncl)5567c478bd9Sstevel@tonic-gate clnt_cots_kcreate(dev_t dev, struct netbuf *addr, int family, rpcprog_t prog,
5577c478bd9Sstevel@tonic-gate 	rpcvers_t vers, uint_t max_msgsize, cred_t *cred, CLIENT **ncl)
5587c478bd9Sstevel@tonic-gate {
5597c478bd9Sstevel@tonic-gate 	CLIENT *h;
5607c478bd9Sstevel@tonic-gate 	cku_private_t *p;
5617c478bd9Sstevel@tonic-gate 	struct rpc_msg call_msg;
5627c478bd9Sstevel@tonic-gate 	struct rpcstat *rpcstat;
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_cots_kcreate: prog %u\n", prog);
5657c478bd9Sstevel@tonic-gate 
566108322fbScarlsonj 	rpcstat = zone_getspecific(rpcstat_zone_key, rpc_zone());
5677c478bd9Sstevel@tonic-gate 	ASSERT(rpcstat != NULL);
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	/* Allocate and intialize the client handle. */
5707c478bd9Sstevel@tonic-gate 	p = kmem_zalloc(sizeof (*p), KM_SLEEP);
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 	h = ptoh(p);
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	h->cl_private = (caddr_t)p;
5757c478bd9Sstevel@tonic-gate 	h->cl_auth = authkern_create();
5767c478bd9Sstevel@tonic-gate 	h->cl_ops = &tcp_ops;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	cv_init(&p->cku_call.call_cv, NULL, CV_DEFAULT, NULL);
5797c478bd9Sstevel@tonic-gate 	mutex_init(&p->cku_call.call_lock, NULL, MUTEX_DEFAULT, NULL);
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	/*
5827c478bd9Sstevel@tonic-gate 	 * If the current sanity check size in rpcmod is smaller
5837c478bd9Sstevel@tonic-gate 	 * than the size needed, then increase the sanity check.
5847c478bd9Sstevel@tonic-gate 	 */
5857c478bd9Sstevel@tonic-gate 	if (max_msgsize != 0 && clnt_max_msg_sizep != NULL &&
5867c478bd9Sstevel@tonic-gate 	    max_msgsize > *clnt_max_msg_sizep) {
5877c478bd9Sstevel@tonic-gate 		mutex_enter(&clnt_max_msg_lock);
5887c478bd9Sstevel@tonic-gate 		if (max_msgsize > *clnt_max_msg_sizep)
5897c478bd9Sstevel@tonic-gate 			*clnt_max_msg_sizep = max_msgsize;
5907c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_max_msg_lock);
5917c478bd9Sstevel@tonic-gate 	}
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	p->cku_outbuflen = COTS_DEFAULT_ALLOCSIZE;
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	/* Preserialize the call message header */
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	call_msg.rm_xid = 0;
5987c478bd9Sstevel@tonic-gate 	call_msg.rm_direction = CALL;
5997c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
6007c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_prog = prog;
6017c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_vers = vers;
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	xdrmem_create(&p->cku_outxdr, p->cku_rpchdr, WIRE_HDR_SIZE, XDR_ENCODE);
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	if (!xdr_callhdr(&p->cku_outxdr, &call_msg)) {
6067c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcreate - Fatal header serialization "
6077c478bd9Sstevel@tonic-gate 		    "error\n");
6087c478bd9Sstevel@tonic-gate 		auth_destroy(h->cl_auth);
6097c478bd9Sstevel@tonic-gate 		kmem_free(p, sizeof (cku_private_t));
6107c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcreate: create failed error EINVAL\n");
6117c478bd9Sstevel@tonic-gate 		return (EINVAL);		/* XXX */
6127c478bd9Sstevel@tonic-gate 	}
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	/*
6157c478bd9Sstevel@tonic-gate 	 * The zalloc initialized the fields below.
6167c478bd9Sstevel@tonic-gate 	 * p->cku_xid = 0;
6177c478bd9Sstevel@tonic-gate 	 * p->cku_flags = 0;
6187c478bd9Sstevel@tonic-gate 	 * p->cku_srcaddr.len = 0;
6197c478bd9Sstevel@tonic-gate 	 * p->cku_srcaddr.maxlen = 0;
6207c478bd9Sstevel@tonic-gate 	 */
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	p->cku_cred = cred;
6237c478bd9Sstevel@tonic-gate 	p->cku_device = dev;
6247c478bd9Sstevel@tonic-gate 	p->cku_addrfmly = family;
6257c478bd9Sstevel@tonic-gate 	p->cku_addr.buf = kmem_zalloc(addr->maxlen, KM_SLEEP);
6267c478bd9Sstevel@tonic-gate 	p->cku_addr.maxlen = addr->maxlen;
6277c478bd9Sstevel@tonic-gate 	p->cku_addr.len = addr->len;
6287c478bd9Sstevel@tonic-gate 	bcopy(addr->buf, p->cku_addr.buf, addr->len);
6297c478bd9Sstevel@tonic-gate 	p->cku_stats = rpcstat->rpc_cots_client;
6307c478bd9Sstevel@tonic-gate 	p->cku_useresvport = -1; /* value is has not been set */
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 	*ncl = h;
6337c478bd9Sstevel@tonic-gate 	return (0);
6347c478bd9Sstevel@tonic-gate }
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6377c478bd9Sstevel@tonic-gate static void
clnt_cots_kabort(CLIENT * h)6387c478bd9Sstevel@tonic-gate clnt_cots_kabort(CLIENT *h)
6397c478bd9Sstevel@tonic-gate {
6407c478bd9Sstevel@tonic-gate }
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate /*
6437c478bd9Sstevel@tonic-gate  * Return error info on this handle.
6447c478bd9Sstevel@tonic-gate  */
6457c478bd9Sstevel@tonic-gate static void
clnt_cots_kerror(CLIENT * h,struct rpc_err * err)6467c478bd9Sstevel@tonic-gate clnt_cots_kerror(CLIENT *h, struct rpc_err *err)
6477c478bd9Sstevel@tonic-gate {
6487c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
6497c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	*err = p->cku_err;
6527c478bd9Sstevel@tonic-gate }
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate static bool_t
clnt_cots_kfreeres(CLIENT * h,xdrproc_t xdr_res,caddr_t res_ptr)6557c478bd9Sstevel@tonic-gate clnt_cots_kfreeres(CLIENT *h, xdrproc_t xdr_res, caddr_t res_ptr)
6567c478bd9Sstevel@tonic-gate {
6577c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
6587c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
6597c478bd9Sstevel@tonic-gate 	XDR *xdrs;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	xdrs = &(p->cku_outxdr);
6627c478bd9Sstevel@tonic-gate 	xdrs->x_op = XDR_FREE;
6637c478bd9Sstevel@tonic-gate 	return ((*xdr_res)(xdrs, res_ptr));
6647c478bd9Sstevel@tonic-gate }
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate static bool_t
clnt_cots_kcontrol(CLIENT * h,int cmd,char * arg)6677c478bd9Sstevel@tonic-gate clnt_cots_kcontrol(CLIENT *h, int cmd, char *arg)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	switch (cmd) {
6727c478bd9Sstevel@tonic-gate 	case CLSET_PROGRESS:
6737c478bd9Sstevel@tonic-gate 		p->cku_progress = TRUE;
6747c478bd9Sstevel@tonic-gate 		return (TRUE);
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	case CLSET_XID:
6777c478bd9Sstevel@tonic-gate 		if (arg == NULL)
6787c478bd9Sstevel@tonic-gate 			return (FALSE);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 		p->cku_xid = *((uint32_t *)arg);
6817c478bd9Sstevel@tonic-gate 		return (TRUE);
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	case CLGET_XID:
6847c478bd9Sstevel@tonic-gate 		if (arg == NULL)
6857c478bd9Sstevel@tonic-gate 			return (FALSE);
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 		*((uint32_t *)arg) = p->cku_xid;
6887c478bd9Sstevel@tonic-gate 		return (TRUE);
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	case CLSET_NODELAYONERR:
6917c478bd9Sstevel@tonic-gate 		if (arg == NULL)
6927c478bd9Sstevel@tonic-gate 			return (FALSE);
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 		if (*((bool_t *)arg) == TRUE) {
6957c478bd9Sstevel@tonic-gate 			p->cku_nodelayonerr = TRUE;
6967c478bd9Sstevel@tonic-gate 			return (TRUE);
6977c478bd9Sstevel@tonic-gate 		}
6987c478bd9Sstevel@tonic-gate 		if (*((bool_t *)arg) == FALSE) {
6997c478bd9Sstevel@tonic-gate 			p->cku_nodelayonerr = FALSE;
7007c478bd9Sstevel@tonic-gate 			return (TRUE);
7017c478bd9Sstevel@tonic-gate 		}
7027c478bd9Sstevel@tonic-gate 		return (FALSE);
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 	case CLGET_NODELAYONERR:
7057c478bd9Sstevel@tonic-gate 		if (arg == NULL)
7067c478bd9Sstevel@tonic-gate 			return (FALSE);
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 		*((bool_t *)arg) = p->cku_nodelayonerr;
7097c478bd9Sstevel@tonic-gate 		return (TRUE);
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	case CLSET_BINDRESVPORT:
7127c478bd9Sstevel@tonic-gate 		if (arg == NULL)
7137c478bd9Sstevel@tonic-gate 			return (FALSE);
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 		if (*(int *)arg != 1 && *(int *)arg != 0)
7167c478bd9Sstevel@tonic-gate 			return (FALSE);
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 		p->cku_useresvport = *(int *)arg;
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 		return (TRUE);
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	case CLGET_BINDRESVPORT:
7237c478bd9Sstevel@tonic-gate 		if (arg == NULL)
7247c478bd9Sstevel@tonic-gate 			return (FALSE);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 		*(int *)arg = p->cku_useresvport;
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 		return (TRUE);
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	default:
7317c478bd9Sstevel@tonic-gate 		return (FALSE);
7327c478bd9Sstevel@tonic-gate 	}
7337c478bd9Sstevel@tonic-gate }
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate /*
7367c478bd9Sstevel@tonic-gate  * Destroy rpc handle.  Frees the space used for output buffer,
7377c478bd9Sstevel@tonic-gate  * private data, and handle structure.
7387c478bd9Sstevel@tonic-gate  */
7397c478bd9Sstevel@tonic-gate static void
clnt_cots_kdestroy(CLIENT * h)7407c478bd9Sstevel@tonic-gate clnt_cots_kdestroy(CLIENT *h)
7417c478bd9Sstevel@tonic-gate {
7427c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
7437c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
7447c478bd9Sstevel@tonic-gate 	calllist_t *call = &p->cku_call;
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_cots_kdestroy h: %p\n", (void *)h);
7477c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_cots_kdestroy h: xid=0x%x\n", p->cku_xid);
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 	if (p->cku_flags & CKU_ONQUEUE) {
7507c478bd9Sstevel@tonic-gate 		RPCLOG(64, "clnt_cots_kdestroy h: removing call for xid 0x%x "
7517c478bd9Sstevel@tonic-gate 		    "from dispatch list\n", p->cku_xid);
7527c478bd9Sstevel@tonic-gate 		call_table_remove(call);
7537c478bd9Sstevel@tonic-gate 	}
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	if (call->call_reply)
7567c478bd9Sstevel@tonic-gate 		freemsg(call->call_reply);
7577c478bd9Sstevel@tonic-gate 	cv_destroy(&call->call_cv);
7587c478bd9Sstevel@tonic-gate 	mutex_destroy(&call->call_lock);
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	kmem_free(p->cku_srcaddr.buf, p->cku_srcaddr.maxlen);
7617c478bd9Sstevel@tonic-gate 	kmem_free(p->cku_addr.buf, p->cku_addr.maxlen);
7627c478bd9Sstevel@tonic-gate 	kmem_free(p, sizeof (*p));
7637c478bd9Sstevel@tonic-gate }
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate static int clnt_cots_pulls;
7667c478bd9Sstevel@tonic-gate #define	RM_HDR_SIZE	4	/* record mark header size */
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate /*
7697c478bd9Sstevel@tonic-gate  * Call remote procedure.
7707c478bd9Sstevel@tonic-gate  */
7717c478bd9Sstevel@tonic-gate static enum clnt_stat
clnt_cots_kcallit(CLIENT * h,rpcproc_t procnum,xdrproc_t xdr_args,caddr_t argsp,xdrproc_t xdr_results,caddr_t resultsp,struct timeval wait)7727c478bd9Sstevel@tonic-gate clnt_cots_kcallit(CLIENT *h, rpcproc_t procnum, xdrproc_t xdr_args,
7737c478bd9Sstevel@tonic-gate     caddr_t argsp, xdrproc_t xdr_results, caddr_t resultsp, struct timeval wait)
7747c478bd9Sstevel@tonic-gate {
7757c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
7767c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
7777c478bd9Sstevel@tonic-gate 	calllist_t *call = &p->cku_call;
7787c478bd9Sstevel@tonic-gate 	XDR *xdrs;
7797c478bd9Sstevel@tonic-gate 	struct rpc_msg reply_msg;
7807c478bd9Sstevel@tonic-gate 	mblk_t *mp;
7817c478bd9Sstevel@tonic-gate #ifdef	RPCDEBUG
7827c478bd9Sstevel@tonic-gate 	clock_t time_sent;
7837c478bd9Sstevel@tonic-gate #endif
7847c478bd9Sstevel@tonic-gate 	struct netbuf *retryaddr;
7857c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry = NULL;
7867c478bd9Sstevel@tonic-gate 	queue_t *wq;
787e280ed37SDai Ngo 	int len, waitsecs, max_waitsecs;
7887c478bd9Sstevel@tonic-gate 	int mpsize;
7897c478bd9Sstevel@tonic-gate 	int refreshes = REFRESHES;
7907c478bd9Sstevel@tonic-gate 	int interrupted;
7917c478bd9Sstevel@tonic-gate 	int tidu_size;
7927c478bd9Sstevel@tonic-gate 	enum clnt_stat status;
7937c478bd9Sstevel@tonic-gate 	struct timeval cwait;
7947c478bd9Sstevel@tonic-gate 	bool_t delay_first = FALSE;
795d3d50737SRafael Vanoni 	clock_t ticks, now;
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit, procnum %u\n", procnum);
7987c478bd9Sstevel@tonic-gate 	COTSRCSTAT_INCR(p->cku_stats, rccalls);
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit: wait.tv_sec: %ld\n", wait.tv_sec);
8017c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit: wait.tv_usec: %ld\n", wait.tv_usec);
8027c478bd9Sstevel@tonic-gate 	/*
8037c478bd9Sstevel@tonic-gate 	 * Bug ID 1240234:
8047c478bd9Sstevel@tonic-gate 	 * Look out for zero length timeouts. We don't want to
8057c478bd9Sstevel@tonic-gate 	 * wait zero seconds for a connection to be established.
8067c478bd9Sstevel@tonic-gate 	 */
8077c478bd9Sstevel@tonic-gate 	if (wait.tv_sec < clnt_cots_min_conntout) {
8087c478bd9Sstevel@tonic-gate 		cwait.tv_sec = clnt_cots_min_conntout;
8097c478bd9Sstevel@tonic-gate 		cwait.tv_usec = 0;
8107c478bd9Sstevel@tonic-gate 		RPCLOG(8, "clnt_cots_kcallit: wait.tv_sec (%ld) too low,",
8117c478bd9Sstevel@tonic-gate 		    wait.tv_sec);
8127c478bd9Sstevel@tonic-gate 		RPCLOG(8, " setting to: %d\n", clnt_cots_min_conntout);
8137c478bd9Sstevel@tonic-gate 	} else {
8147c478bd9Sstevel@tonic-gate 		cwait = wait;
8157c478bd9Sstevel@tonic-gate 	}
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate call_again:
8187c478bd9Sstevel@tonic-gate 	if (cm_entry) {
8197c478bd9Sstevel@tonic-gate 		connmgr_release(cm_entry);
8207c478bd9Sstevel@tonic-gate 		cm_entry = NULL;
8217c478bd9Sstevel@tonic-gate 	}
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	mp = NULL;
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 	/*
8267c478bd9Sstevel@tonic-gate 	 * If the call is not a retry, allocate a new xid and cache it
8277c478bd9Sstevel@tonic-gate 	 * for future retries.
8287c478bd9Sstevel@tonic-gate 	 * Bug ID 1246045:
8297c478bd9Sstevel@tonic-gate 	 * Treat call as a retry for purposes of binding the source
8307c478bd9Sstevel@tonic-gate 	 * port only if we actually attempted to send anything on
8317c478bd9Sstevel@tonic-gate 	 * the previous call.
8327c478bd9Sstevel@tonic-gate 	 */
8337c478bd9Sstevel@tonic-gate 	if (p->cku_xid == 0) {
8347c478bd9Sstevel@tonic-gate 		p->cku_xid = alloc_xid();
8358ffff9fdSgt29601 		call->call_zoneid = rpc_zoneid();
8368ffff9fdSgt29601 
8377c478bd9Sstevel@tonic-gate 		/*
8387c478bd9Sstevel@tonic-gate 		 * We need to ASSERT here that our xid != 0 because this
8397c478bd9Sstevel@tonic-gate 		 * determines whether or not our call record gets placed on
8407c478bd9Sstevel@tonic-gate 		 * the hash table or the linked list.  By design, we mandate
8417c478bd9Sstevel@tonic-gate 		 * that RPC calls over cots must have xid's != 0, so we can
8427c478bd9Sstevel@tonic-gate 		 * ensure proper management of the hash table.
8437c478bd9Sstevel@tonic-gate 		 */
8447c478bd9Sstevel@tonic-gate 		ASSERT(p->cku_xid != 0);
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 		retryaddr = NULL;
8477c478bd9Sstevel@tonic-gate 		p->cku_flags &= ~CKU_SENT;
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 		if (p->cku_flags & CKU_ONQUEUE) {
8507c478bd9Sstevel@tonic-gate 			RPCLOG(8, "clnt_cots_kcallit: new call, dequeuing old"
8517c478bd9Sstevel@tonic-gate 			    " one (%p)\n", (void *)call);
8527c478bd9Sstevel@tonic-gate 			call_table_remove(call);
8537c478bd9Sstevel@tonic-gate 			p->cku_flags &= ~CKU_ONQUEUE;
8547c478bd9Sstevel@tonic-gate 			RPCLOG(64, "clnt_cots_kcallit: removing call from "
8557c478bd9Sstevel@tonic-gate 			    "dispatch list because xid was zero (now 0x%x)\n",
8567c478bd9Sstevel@tonic-gate 			    p->cku_xid);
8577c478bd9Sstevel@tonic-gate 		}
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 		if (call->call_reply != NULL) {
8607c478bd9Sstevel@tonic-gate 			freemsg(call->call_reply);
8617c478bd9Sstevel@tonic-gate 			call->call_reply = NULL;
8627c478bd9Sstevel@tonic-gate 		}
8637c478bd9Sstevel@tonic-gate 	} else if (p->cku_srcaddr.buf == NULL || p->cku_srcaddr.len == 0) {
8647c478bd9Sstevel@tonic-gate 		retryaddr = NULL;
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	} else if (p->cku_flags & CKU_SENT) {
8677c478bd9Sstevel@tonic-gate 		retryaddr = &p->cku_srcaddr;
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	} else {
8707c478bd9Sstevel@tonic-gate 		/*
8717c478bd9Sstevel@tonic-gate 		 * Bug ID 1246045: Nothing was sent, so set retryaddr to
8727c478bd9Sstevel@tonic-gate 		 * NULL and let connmgr_get() bind to any source port it
8737c478bd9Sstevel@tonic-gate 		 * can get.
8747c478bd9Sstevel@tonic-gate 		 */
8757c478bd9Sstevel@tonic-gate 		retryaddr = NULL;
8767c478bd9Sstevel@tonic-gate 	}
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	RPCLOG(64, "clnt_cots_kcallit: xid = 0x%x", p->cku_xid);
8797c478bd9Sstevel@tonic-gate 	RPCLOG(64, " flags = 0x%x\n", p->cku_flags);
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 	p->cku_err.re_status = RPC_TIMEDOUT;
8827c478bd9Sstevel@tonic-gate 	p->cku_err.re_errno = p->cku_err.re_terrno = 0;
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	cm_entry = connmgr_wrapget(retryaddr, &cwait, p);
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	if (cm_entry == NULL) {
8877c478bd9Sstevel@tonic-gate 		RPCLOG(1, "clnt_cots_kcallit: can't connect status %s\n",
8887c478bd9Sstevel@tonic-gate 		    clnt_sperrno(p->cku_err.re_status));
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 		/*
8917c478bd9Sstevel@tonic-gate 		 * The reasons why we fail to create a connection are
8927c478bd9Sstevel@tonic-gate 		 * varied. In most cases we don't want the caller to
8937c478bd9Sstevel@tonic-gate 		 * immediately retry. This could have one or more
8947c478bd9Sstevel@tonic-gate 		 * bad effects. This includes flooding the net with
8957c478bd9Sstevel@tonic-gate 		 * connect requests to ports with no listener; a hard
8967c478bd9Sstevel@tonic-gate 		 * kernel loop due to all the "reserved" TCP ports being
8977c478bd9Sstevel@tonic-gate 		 * in use.
8987c478bd9Sstevel@tonic-gate 		 */
8997c478bd9Sstevel@tonic-gate 		delay_first = TRUE;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 		/*
9027c478bd9Sstevel@tonic-gate 		 * Even if we end up returning EINTR, we still count a
9037c478bd9Sstevel@tonic-gate 		 * a "can't connect", because the connection manager
9047c478bd9Sstevel@tonic-gate 		 * might have been committed to waiting for or timing out on
9057c478bd9Sstevel@tonic-gate 		 * a connection.
9067c478bd9Sstevel@tonic-gate 		 */
9077c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(p->cku_stats, rccantconn);
9087c478bd9Sstevel@tonic-gate 		switch (p->cku_err.re_status) {
9097c478bd9Sstevel@tonic-gate 		case RPC_INTR:
9107c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EINTR;
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 			/*
9137c478bd9Sstevel@tonic-gate 			 * No need to delay because a UNIX signal(2)
9147c478bd9Sstevel@tonic-gate 			 * interrupted us. The caller likely won't
9157c478bd9Sstevel@tonic-gate 			 * retry the CLNT_CALL() and even if it does,
9167c478bd9Sstevel@tonic-gate 			 * we assume the caller knows what it is doing.
9177c478bd9Sstevel@tonic-gate 			 */
9187c478bd9Sstevel@tonic-gate 			delay_first = FALSE;
9197c478bd9Sstevel@tonic-gate 			break;
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 		case RPC_TIMEDOUT:
9227c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = ETIMEDOUT;
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 			/*
9257c478bd9Sstevel@tonic-gate 			 * No need to delay because timed out already
9267c478bd9Sstevel@tonic-gate 			 * on the connection request and assume that the
9277c478bd9Sstevel@tonic-gate 			 * transport time out is longer than our minimum
9287c478bd9Sstevel@tonic-gate 			 * timeout, or least not too much smaller.
9297c478bd9Sstevel@tonic-gate 			 */
9307c478bd9Sstevel@tonic-gate 			delay_first = FALSE;
9317c478bd9Sstevel@tonic-gate 			break;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 		case RPC_SYSTEMERROR:
9347c478bd9Sstevel@tonic-gate 		case RPC_TLIERROR:
9357c478bd9Sstevel@tonic-gate 			/*
9367c478bd9Sstevel@tonic-gate 			 * We want to delay here because a transient
9377c478bd9Sstevel@tonic-gate 			 * system error has a better chance of going away
9387c478bd9Sstevel@tonic-gate 			 * if we delay a bit. If it's not transient, then
9397c478bd9Sstevel@tonic-gate 			 * we don't want end up in a hard kernel loop
9407c478bd9Sstevel@tonic-gate 			 * due to retries.
9417c478bd9Sstevel@tonic-gate 			 */
9427c478bd9Sstevel@tonic-gate 			ASSERT(p->cku_err.re_errno != 0);
9437c478bd9Sstevel@tonic-gate 			break;
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 		case RPC_CANTCONNECT:
9477c478bd9Sstevel@tonic-gate 			/*
9487c478bd9Sstevel@tonic-gate 			 * RPC_CANTCONNECT is set on T_ERROR_ACK which
9497c478bd9Sstevel@tonic-gate 			 * implies some error down in the TCP layer or
9507c478bd9Sstevel@tonic-gate 			 * below. If cku_nodelayonerror is set then we
9517c478bd9Sstevel@tonic-gate 			 * assume the caller knows not to try too hard.
9527c478bd9Sstevel@tonic-gate 			 */
9537c478bd9Sstevel@tonic-gate 			RPCLOG0(8, "clnt_cots_kcallit: connection failed,");
9547c478bd9Sstevel@tonic-gate 			RPCLOG0(8, " re_status=RPC_CANTCONNECT,");
9557c478bd9Sstevel@tonic-gate 			RPCLOG(8, " re_errno=%d,", p->cku_err.re_errno);
9567c478bd9Sstevel@tonic-gate 			RPCLOG(8, " cku_nodelayonerr=%d", p->cku_nodelayonerr);
9577c478bd9Sstevel@tonic-gate 			if (p->cku_nodelayonerr == TRUE)
9587c478bd9Sstevel@tonic-gate 				delay_first = FALSE;
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 			break;
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 		case RPC_XPRTFAILED:
9657c478bd9Sstevel@tonic-gate 			/*
9667c478bd9Sstevel@tonic-gate 			 * We want to delay here because we likely
9677c478bd9Sstevel@tonic-gate 			 * got a refused connection.
9687c478bd9Sstevel@tonic-gate 			 */
969af41c4bfSvv149972 			if (p->cku_err.re_errno == 0)
970af41c4bfSvv149972 				p->cku_err.re_errno = EIO;
9717c478bd9Sstevel@tonic-gate 
972af41c4bfSvv149972 			RPCLOG(1, "clnt_cots_kcallit: transport failed: %d\n",
973af41c4bfSvv149972 			    p->cku_err.re_errno);
974af41c4bfSvv149972 
975af41c4bfSvv149972 			break;
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 		default:
9787c478bd9Sstevel@tonic-gate 			/*
9797c478bd9Sstevel@tonic-gate 			 * We delay here because it is better to err
9807c478bd9Sstevel@tonic-gate 			 * on the side of caution. If we got here then
9817c478bd9Sstevel@tonic-gate 			 * status could have been RPC_SUCCESS, but we
9827c478bd9Sstevel@tonic-gate 			 * know that we did not get a connection, so
9837c478bd9Sstevel@tonic-gate 			 * force the rpc status to RPC_CANTCONNECT.
9847c478bd9Sstevel@tonic-gate 			 */
9857c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_CANTCONNECT;
9867c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
9877c478bd9Sstevel@tonic-gate 			break;
9887c478bd9Sstevel@tonic-gate 		}
9897c478bd9Sstevel@tonic-gate 		if (delay_first == TRUE)
9907c478bd9Sstevel@tonic-gate 			ticks = clnt_cots_min_tout * drv_usectohz(1000000);
9917c478bd9Sstevel@tonic-gate 		goto cots_done;
9927c478bd9Sstevel@tonic-gate 	}
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate 	/*
9957c478bd9Sstevel@tonic-gate 	 * If we've never sent any request on this connection (send count
9967c478bd9Sstevel@tonic-gate 	 * is zero, or the connection has been reset), cache the
9977c478bd9Sstevel@tonic-gate 	 * the connection's create time and send a request (possibly a retry)
9987c478bd9Sstevel@tonic-gate 	 */
9997c478bd9Sstevel@tonic-gate 	if ((p->cku_flags & CKU_SENT) == 0 ||
10007c478bd9Sstevel@tonic-gate 	    p->cku_ctime != cm_entry->x_ctime) {
10017c478bd9Sstevel@tonic-gate 		p->cku_ctime = cm_entry->x_ctime;
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 	} else if ((p->cku_flags & CKU_SENT) && (p->cku_flags & CKU_ONQUEUE) &&
10047c478bd9Sstevel@tonic-gate 	    (call->call_reply != NULL ||
10057c478bd9Sstevel@tonic-gate 	    p->cku_recv_attempts < clnt_cots_maxrecv)) {
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 		/*
10087c478bd9Sstevel@tonic-gate 		 * If we've sent a request and our call is on the dispatch
10097c478bd9Sstevel@tonic-gate 		 * queue and we haven't made too many receive attempts, then
10107c478bd9Sstevel@tonic-gate 		 * don't re-send, just receive.
10117c478bd9Sstevel@tonic-gate 		 */
10127c478bd9Sstevel@tonic-gate 		p->cku_recv_attempts++;
10137c478bd9Sstevel@tonic-gate 		goto read_again;
10147c478bd9Sstevel@tonic-gate 	}
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate 	/*
10177c478bd9Sstevel@tonic-gate 	 * Now we create the RPC request in a STREAMS message.  We have to do
10187c478bd9Sstevel@tonic-gate 	 * this after the call to connmgr_get so that we have the correct
10197c478bd9Sstevel@tonic-gate 	 * TIDU size for the transport.
10207c478bd9Sstevel@tonic-gate 	 */
10217c478bd9Sstevel@tonic-gate 	tidu_size = cm_entry->x_tidu_size;
10227c478bd9Sstevel@tonic-gate 	len = MSG_OFFSET + MAX(tidu_size, RM_HDR_SIZE + WIRE_HDR_SIZE);
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	while ((mp = allocb(len, BPRI_MED)) == NULL) {
10257c478bd9Sstevel@tonic-gate 		if (strwaitbuf(len, BPRI_MED)) {
10267c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_SYSTEMERROR;
10277c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = ENOSR;
10287c478bd9Sstevel@tonic-gate 			COTSRCSTAT_INCR(p->cku_stats, rcnomem);
10297c478bd9Sstevel@tonic-gate 			goto cots_done;
10307c478bd9Sstevel@tonic-gate 		}
10317c478bd9Sstevel@tonic-gate 	}
10327c478bd9Sstevel@tonic-gate 	xdrs = &p->cku_outxdr;
10337c478bd9Sstevel@tonic-gate 	xdrmblk_init(xdrs, mp, XDR_ENCODE, tidu_size);
10347c478bd9Sstevel@tonic-gate 	mpsize = MBLKSIZE(mp);
10357c478bd9Sstevel@tonic-gate 	ASSERT(mpsize >= len);
10367c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_rptr == mp->b_datap->db_base);
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	/*
10397c478bd9Sstevel@tonic-gate 	 * If the size of mblk is not appreciably larger than what we
10407c478bd9Sstevel@tonic-gate 	 * asked, then resize the mblk to exactly len bytes. The reason for
10417c478bd9Sstevel@tonic-gate 	 * this: suppose len is 1600 bytes, the tidu is 1460 bytes
10427c478bd9Sstevel@tonic-gate 	 * (from TCP over ethernet), and the arguments to the RPC require
10437c478bd9Sstevel@tonic-gate 	 * 2800 bytes. Ideally we want the protocol to render two
10447c478bd9Sstevel@tonic-gate 	 * ~1400 byte segments over the wire. However if allocb() gives us a 2k
10457c478bd9Sstevel@tonic-gate 	 * mblk, and we allocate a second mblk for the remainder, the protocol
10467c478bd9Sstevel@tonic-gate 	 * module may generate 3 segments over the wire:
10477c478bd9Sstevel@tonic-gate 	 * 1460 bytes for the first, 448 (2048 - 1600) for the second, and
10487c478bd9Sstevel@tonic-gate 	 * 892 for the third. If we "waste" 448 bytes in the first mblk,
10497c478bd9Sstevel@tonic-gate 	 * the XDR encoding will generate two ~1400 byte mblks, and the
10507c478bd9Sstevel@tonic-gate 	 * protocol module is more likely to produce properly sized segments.
10517c478bd9Sstevel@tonic-gate 	 */
10527c478bd9Sstevel@tonic-gate 	if ((mpsize >> 1) <= len)
10537c478bd9Sstevel@tonic-gate 		mp->b_rptr += (mpsize - len);
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 	/*
10567c478bd9Sstevel@tonic-gate 	 * Adjust b_rptr to reserve space for the non-data protocol headers
10577c478bd9Sstevel@tonic-gate 	 * any downstream modules might like to add, and for the
10587c478bd9Sstevel@tonic-gate 	 * record marking header.
10597c478bd9Sstevel@tonic-gate 	 */
10607c478bd9Sstevel@tonic-gate 	mp->b_rptr += (MSG_OFFSET + RM_HDR_SIZE);
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	if (h->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
10637c478bd9Sstevel@tonic-gate 		/* Copy in the preserialized RPC header information. */
10647c478bd9Sstevel@tonic-gate 		bcopy(p->cku_rpchdr, mp->b_rptr, WIRE_HDR_SIZE);
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 		/* Use XDR_SETPOS() to set the b_wptr to past the RPC header. */
10677c478bd9Sstevel@tonic-gate 		XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base +
10687c478bd9Sstevel@tonic-gate 		    WIRE_HDR_SIZE));
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 		ASSERT((mp->b_wptr - mp->b_rptr) == WIRE_HDR_SIZE);
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 		/* Serialize the procedure number and the arguments. */
10737c478bd9Sstevel@tonic-gate 		if ((!XDR_PUTINT32(xdrs, (int32_t *)&procnum)) ||
10747c478bd9Sstevel@tonic-gate 		    (!AUTH_MARSHALL(h->cl_auth, xdrs, p->cku_cred)) ||
10757c478bd9Sstevel@tonic-gate 		    (!(*xdr_args)(xdrs, argsp))) {
10767c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_CANTENCODEARGS;
10777c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
10787c478bd9Sstevel@tonic-gate 			goto cots_done;
10797c478bd9Sstevel@tonic-gate 		}
10807c478bd9Sstevel@tonic-gate 
10817c478bd9Sstevel@tonic-gate 		(*(uint32_t *)(mp->b_rptr)) = p->cku_xid;
10827c478bd9Sstevel@tonic-gate 	} else {
10837c478bd9Sstevel@tonic-gate 		uint32_t *uproc = (uint32_t *)&p->cku_rpchdr[WIRE_HDR_SIZE];
10847c478bd9Sstevel@tonic-gate 		IXDR_PUT_U_INT32(uproc, procnum);
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 		(*(uint32_t *)(&p->cku_rpchdr[0])) = p->cku_xid;
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 		/* Use XDR_SETPOS() to set the b_wptr. */
10897c478bd9Sstevel@tonic-gate 		XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base));
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 		/* Serialize the procedure number and the arguments. */
10927c478bd9Sstevel@tonic-gate 		if (!AUTH_WRAP(h->cl_auth, p->cku_rpchdr, WIRE_HDR_SIZE+4,
10937c478bd9Sstevel@tonic-gate 		    xdrs, xdr_args, argsp)) {
10947c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_CANTENCODEARGS;
10957c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
10967c478bd9Sstevel@tonic-gate 			goto cots_done;
10977c478bd9Sstevel@tonic-gate 		}
10987c478bd9Sstevel@tonic-gate 	}
10997c478bd9Sstevel@tonic-gate 
11007c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit: connected, sending call, tidu_size %d\n",
11017c478bd9Sstevel@tonic-gate 	    tidu_size);
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	wq = cm_entry->x_wq;
1104e280ed37SDai Ngo 	waitsecs = 0;
1105e280ed37SDai Ngo 
1106e280ed37SDai Ngo dispatch_again:
1107125a8fd9SSiddheshwar Mahesh 	status = clnt_dispatch_send(wq, mp, call, p->cku_xid,
11087c478bd9Sstevel@tonic-gate 	    (p->cku_flags & CKU_ONQUEUE));
11097c478bd9Sstevel@tonic-gate 
1110e280ed37SDai Ngo 	if ((status == RPC_CANTSEND) && (call->call_reason == ENOBUFS)) {
1111125a8fd9SSiddheshwar Mahesh 		/*
1112e280ed37SDai Ngo 		 * QFULL condition, allow some time for queue to drain
1113e280ed37SDai Ngo 		 * and try again. Give up after waiting for all timeout
1114e280ed37SDai Ngo 		 * specified for the call, or zone is going away.
1115125a8fd9SSiddheshwar Mahesh 		 */
1116e280ed37SDai Ngo 		max_waitsecs = wait.tv_sec ? wait.tv_sec : clnt_cots_min_tout;
1117e280ed37SDai Ngo 		if ((waitsecs++ < max_waitsecs) &&
1118e280ed37SDai Ngo 		    !(zone_status_get(curproc->p_zone) >=
1119e280ed37SDai Ngo 		    ZONE_IS_SHUTTING_DOWN)) {
1120e280ed37SDai Ngo 
1121e280ed37SDai Ngo 			/* wait 1 sec for queue to drain */
1122e280ed37SDai Ngo 			if (clnt_delay(drv_usectohz(1000000),
1123e280ed37SDai Ngo 			    h->cl_nosignal) == EINTR) {
1124e280ed37SDai Ngo 				p->cku_err.re_errno = EINTR;
1125e280ed37SDai Ngo 				p->cku_err.re_status = RPC_INTR;
1126125a8fd9SSiddheshwar Mahesh 
1127125a8fd9SSiddheshwar Mahesh 				goto cots_done;
1128125a8fd9SSiddheshwar Mahesh 			}
1129125a8fd9SSiddheshwar Mahesh 
1130e280ed37SDai Ngo 			/* and try again */
1131e280ed37SDai Ngo 			goto dispatch_again;
1132e280ed37SDai Ngo 		}
1133e280ed37SDai Ngo 		p->cku_err.re_status = status;
1134e280ed37SDai Ngo 		p->cku_err.re_errno = call->call_reason;
1135e280ed37SDai Ngo 		DTRACE_PROBE(krpc__e__clntcots__kcallit__cantsend);
1136e280ed37SDai Ngo 
1137e280ed37SDai Ngo 		goto cots_done;
1138e280ed37SDai Ngo 	}
1139e280ed37SDai Ngo 
1140e280ed37SDai Ngo 	if (waitsecs) {
1141e280ed37SDai Ngo 		/* adjust timeout to account for time wait to send */
1142e280ed37SDai Ngo 		wait.tv_sec -= waitsecs;
1143e280ed37SDai Ngo 		if (wait.tv_sec < 0) {
1144e280ed37SDai Ngo 			/* pick up reply on next retry */
1145e280ed37SDai Ngo 			wait.tv_sec = 0;
1146e280ed37SDai Ngo 		}
1147e280ed37SDai Ngo 		DTRACE_PROBE2(clnt_cots__sendwait, CLIENT *, h,
1148e280ed37SDai Ngo 		    int, waitsecs);
1149e280ed37SDai Ngo 	}
1150e280ed37SDai Ngo 
11517c478bd9Sstevel@tonic-gate 	RPCLOG(64, "clnt_cots_kcallit: sent call for xid 0x%x\n",
11527c478bd9Sstevel@tonic-gate 	    (uint_t)p->cku_xid);
11537c478bd9Sstevel@tonic-gate 	p->cku_flags = (CKU_ONQUEUE|CKU_SENT);
11547c478bd9Sstevel@tonic-gate 	p->cku_recv_attempts = 1;
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate #ifdef	RPCDEBUG
1157d3d50737SRafael Vanoni 	time_sent = ddi_get_lbolt();
11587c478bd9Sstevel@tonic-gate #endif
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	/*
11617c478bd9Sstevel@tonic-gate 	 * Wait for a reply or a timeout.  If there is no error or timeout,
11627c478bd9Sstevel@tonic-gate 	 * (both indicated by call_status), call->call_reply will contain
11637c478bd9Sstevel@tonic-gate 	 * the RPC reply message.
11647c478bd9Sstevel@tonic-gate 	 */
11657c478bd9Sstevel@tonic-gate read_again:
11667c478bd9Sstevel@tonic-gate 	mutex_enter(&call->call_lock);
11677c478bd9Sstevel@tonic-gate 	interrupted = 0;
11687c478bd9Sstevel@tonic-gate 	if (call->call_status == RPC_TIMEDOUT) {
11697c478bd9Sstevel@tonic-gate 		/*
11707c478bd9Sstevel@tonic-gate 		 * Indicate that the lwp is not to be stopped while waiting
11717c478bd9Sstevel@tonic-gate 		 * for this network traffic.  This is to avoid deadlock while
11727c478bd9Sstevel@tonic-gate 		 * debugging a process via /proc and also to avoid recursive
11737c478bd9Sstevel@tonic-gate 		 * mutex_enter()s due to NFS page faults while stopping
11747c478bd9Sstevel@tonic-gate 		 * (NFS holds locks when it calls here).
11757c478bd9Sstevel@tonic-gate 		 */
11767c478bd9Sstevel@tonic-gate 		clock_t cv_wait_ret;
11777c478bd9Sstevel@tonic-gate 		clock_t timout;
11787c478bd9Sstevel@tonic-gate 		clock_t oldlbolt;
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 		klwp_t *lwp = ttolwp(curthread);
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 		if (lwp != NULL)
11837c478bd9Sstevel@tonic-gate 			lwp->lwp_nostop++;
11847c478bd9Sstevel@tonic-gate 
1185d3d50737SRafael Vanoni 		oldlbolt = ddi_get_lbolt();
11867c478bd9Sstevel@tonic-gate 		timout = wait.tv_sec * drv_usectohz(1000000) +
11877c478bd9Sstevel@tonic-gate 		    drv_usectohz(wait.tv_usec) + oldlbolt;
11887c478bd9Sstevel@tonic-gate 		/*
11897c478bd9Sstevel@tonic-gate 		 * Iterate until the call_status is changed to something
11907c478bd9Sstevel@tonic-gate 		 * other that RPC_TIMEDOUT, or if cv_timedwait_sig() returns
11917c478bd9Sstevel@tonic-gate 		 * something <=0 zero. The latter means that we timed
11927c478bd9Sstevel@tonic-gate 		 * out.
11937c478bd9Sstevel@tonic-gate 		 */
11947c478bd9Sstevel@tonic-gate 		if (h->cl_nosignal)
11957c478bd9Sstevel@tonic-gate 			while ((cv_wait_ret = cv_timedwait(&call->call_cv,
11967c478bd9Sstevel@tonic-gate 			    &call->call_lock, timout)) > 0 &&
11978ffff9fdSgt29601 			    call->call_status == RPC_TIMEDOUT)
11988ffff9fdSgt29601 				;
11997c478bd9Sstevel@tonic-gate 		else
12007c478bd9Sstevel@tonic-gate 			while ((cv_wait_ret = cv_timedwait_sig(
12017c478bd9Sstevel@tonic-gate 			    &call->call_cv,
12027c478bd9Sstevel@tonic-gate 			    &call->call_lock, timout)) > 0 &&
12038ffff9fdSgt29601 			    call->call_status == RPC_TIMEDOUT)
12048ffff9fdSgt29601 				;
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 		switch (cv_wait_ret) {
12077c478bd9Sstevel@tonic-gate 		case 0:
12087c478bd9Sstevel@tonic-gate 			/*
12097c478bd9Sstevel@tonic-gate 			 * If we got out of the above loop with
12107c478bd9Sstevel@tonic-gate 			 * cv_timedwait_sig() returning 0, then we were
12117c478bd9Sstevel@tonic-gate 			 * interrupted regardless what call_status is.
12127c478bd9Sstevel@tonic-gate 			 */
12137c478bd9Sstevel@tonic-gate 			interrupted = 1;
12147c478bd9Sstevel@tonic-gate 			break;
12157c478bd9Sstevel@tonic-gate 		case -1:
12167c478bd9Sstevel@tonic-gate 			/* cv_timedwait_sig() timed out */
12177c478bd9Sstevel@tonic-gate 			break;
12187c478bd9Sstevel@tonic-gate 		default:
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 			/*
12217c478bd9Sstevel@tonic-gate 			 * We were cv_signaled(). If we didn't
12227c478bd9Sstevel@tonic-gate 			 * get a successful call_status and returned
12237c478bd9Sstevel@tonic-gate 			 * before time expired, delay up to clnt_cots_min_tout
12247c478bd9Sstevel@tonic-gate 			 * seconds so that the caller doesn't immediately
12257c478bd9Sstevel@tonic-gate 			 * try to call us again and thus force the
12267c478bd9Sstevel@tonic-gate 			 * same condition that got us here (such
12277c478bd9Sstevel@tonic-gate 			 * as a RPC_XPRTFAILED due to the server not
12287c478bd9Sstevel@tonic-gate 			 * listening on the end-point.
12297c478bd9Sstevel@tonic-gate 			 */
12307c478bd9Sstevel@tonic-gate 			if (call->call_status != RPC_SUCCESS) {
12317c478bd9Sstevel@tonic-gate 				clock_t curlbolt;
12327c478bd9Sstevel@tonic-gate 				clock_t diff;
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 				curlbolt = ddi_get_lbolt();
12357c478bd9Sstevel@tonic-gate 				ticks = clnt_cots_min_tout *
12367c478bd9Sstevel@tonic-gate 				    drv_usectohz(1000000);
12377c478bd9Sstevel@tonic-gate 				diff = curlbolt - oldlbolt;
12387c478bd9Sstevel@tonic-gate 				if (diff < ticks) {
12397c478bd9Sstevel@tonic-gate 					delay_first = TRUE;
12407c478bd9Sstevel@tonic-gate 					if (diff > 0)
12417c478bd9Sstevel@tonic-gate 						ticks -= diff;
12427c478bd9Sstevel@tonic-gate 				}
12437c478bd9Sstevel@tonic-gate 			}
12447c478bd9Sstevel@tonic-gate 			break;
12457c478bd9Sstevel@tonic-gate 		}
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate 		if (lwp != NULL)
12487c478bd9Sstevel@tonic-gate 			lwp->lwp_nostop--;
12497c478bd9Sstevel@tonic-gate 	}
12507c478bd9Sstevel@tonic-gate 	/*
12517c478bd9Sstevel@tonic-gate 	 * Get the reply message, if any.  This will be freed at the end
12527c478bd9Sstevel@tonic-gate 	 * whether or not an error occurred.
12537c478bd9Sstevel@tonic-gate 	 */
12547c478bd9Sstevel@tonic-gate 	mp = call->call_reply;
12557c478bd9Sstevel@tonic-gate 	call->call_reply = NULL;
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	/*
12587c478bd9Sstevel@tonic-gate 	 * call_err is the error info when the call is on dispatch queue.
12597c478bd9Sstevel@tonic-gate 	 * cku_err is the error info returned to the caller.
12607c478bd9Sstevel@tonic-gate 	 * Sync cku_err with call_err for local message processing.
12617c478bd9Sstevel@tonic-gate 	 */
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 	status = call->call_status;
12647c478bd9Sstevel@tonic-gate 	p->cku_err = call->call_err;
12657c478bd9Sstevel@tonic-gate 	mutex_exit(&call->call_lock);
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate 	if (status != RPC_SUCCESS) {
12687c478bd9Sstevel@tonic-gate 		switch (status) {
12697c478bd9Sstevel@tonic-gate 		case RPC_TIMEDOUT:
1270d3d50737SRafael Vanoni 			now = ddi_get_lbolt();
12717c478bd9Sstevel@tonic-gate 			if (interrupted) {
12727c478bd9Sstevel@tonic-gate 				COTSRCSTAT_INCR(p->cku_stats, rcintrs);
12737c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_INTR;
12747c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EINTR;
12757c478bd9Sstevel@tonic-gate 				RPCLOG(1, "clnt_cots_kcallit: xid 0x%x",
12767c478bd9Sstevel@tonic-gate 				    p->cku_xid);
1277d3d50737SRafael Vanoni 				RPCLOG(1, "signal interrupted at %ld", now);
12787c478bd9Sstevel@tonic-gate 				RPCLOG(1, ", was sent at %ld\n", time_sent);
12797c478bd9Sstevel@tonic-gate 			} else {
12807c478bd9Sstevel@tonic-gate 				COTSRCSTAT_INCR(p->cku_stats, rctimeouts);
12817c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = ETIMEDOUT;
12827c478bd9Sstevel@tonic-gate 				RPCLOG(1, "clnt_cots_kcallit: timed out at %ld",
1283d3d50737SRafael Vanoni 				    now);
12847c478bd9Sstevel@tonic-gate 				RPCLOG(1, ", was sent at %ld\n", time_sent);
12857c478bd9Sstevel@tonic-gate 			}
12867c478bd9Sstevel@tonic-gate 			break;
12877c478bd9Sstevel@tonic-gate 
12887c478bd9Sstevel@tonic-gate 		case RPC_XPRTFAILED:
12897c478bd9Sstevel@tonic-gate 			if (p->cku_err.re_errno == 0)
12907c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcallit: transport failed: %d\n",
12937c478bd9Sstevel@tonic-gate 			    p->cku_err.re_errno);
12947c478bd9Sstevel@tonic-gate 			break;
12957c478bd9Sstevel@tonic-gate 
12967c478bd9Sstevel@tonic-gate 		case RPC_SYSTEMERROR:
12977c478bd9Sstevel@tonic-gate 			ASSERT(p->cku_err.re_errno);
12987c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcallit: system error: %d\n",
12997c478bd9Sstevel@tonic-gate 			    p->cku_err.re_errno);
13007c478bd9Sstevel@tonic-gate 			break;
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 		default:
13037c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_SYSTEMERROR;
13047c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
13057c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcallit: error: %s\n",
13067c478bd9Sstevel@tonic-gate 			    clnt_sperrno(status));
13077c478bd9Sstevel@tonic-gate 			break;
13087c478bd9Sstevel@tonic-gate 		}
13097c478bd9Sstevel@tonic-gate 		if (p->cku_err.re_status != RPC_TIMEDOUT) {
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate 			if (p->cku_flags & CKU_ONQUEUE) {
13127c478bd9Sstevel@tonic-gate 				call_table_remove(call);
13137c478bd9Sstevel@tonic-gate 				p->cku_flags &= ~CKU_ONQUEUE;
13147c478bd9Sstevel@tonic-gate 			}
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 			RPCLOG(64, "clnt_cots_kcallit: non TIMEOUT so xid 0x%x "
13177c478bd9Sstevel@tonic-gate 			    "taken off dispatch list\n", p->cku_xid);
13187c478bd9Sstevel@tonic-gate 			if (call->call_reply) {
13197c478bd9Sstevel@tonic-gate 				freemsg(call->call_reply);
13207c478bd9Sstevel@tonic-gate 				call->call_reply = NULL;
13217c478bd9Sstevel@tonic-gate 			}
13227c478bd9Sstevel@tonic-gate 		} else if (wait.tv_sec != 0) {
13237c478bd9Sstevel@tonic-gate 			/*
13247c478bd9Sstevel@tonic-gate 			 * We've sent the request over TCP and so we have
13257c478bd9Sstevel@tonic-gate 			 * every reason to believe it will get
13267c478bd9Sstevel@tonic-gate 			 * delivered. In which case returning a timeout is not
13277c478bd9Sstevel@tonic-gate 			 * appropriate.
13287c478bd9Sstevel@tonic-gate 			 */
13297c478bd9Sstevel@tonic-gate 			if (p->cku_progress == TRUE &&
13307c478bd9Sstevel@tonic-gate 			    p->cku_recv_attempts < clnt_cots_maxrecv) {
13317c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_INPROGRESS;
13327c478bd9Sstevel@tonic-gate 			}
13337c478bd9Sstevel@tonic-gate 		}
13347c478bd9Sstevel@tonic-gate 		goto cots_done;
13357c478bd9Sstevel@tonic-gate 	}
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 	xdrs = &p->cku_inxdr;
13387c478bd9Sstevel@tonic-gate 	xdrmblk_init(xdrs, mp, XDR_DECODE, 0);
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate 	reply_msg.rm_direction = REPLY;
13417c478bd9Sstevel@tonic-gate 	reply_msg.rm_reply.rp_stat = MSG_ACCEPTED;
13427c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_stat = SUCCESS;
13437c478bd9Sstevel@tonic-gate 
13447c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_verf = _null_auth;
13457c478bd9Sstevel@tonic-gate 	/*
13467c478bd9Sstevel@tonic-gate 	 *  xdr_results will be done in AUTH_UNWRAP.
13477c478bd9Sstevel@tonic-gate 	 */
13487c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_results.where = NULL;
13497c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_results.proc = xdr_void;
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 	if (xdr_replymsg(xdrs, &reply_msg)) {
13527c478bd9Sstevel@tonic-gate 		enum clnt_stat re_status;
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 		_seterr_reply(&reply_msg, &p->cku_err);
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 		re_status = p->cku_err.re_status;
13577c478bd9Sstevel@tonic-gate 		if (re_status == RPC_SUCCESS) {
13587c478bd9Sstevel@tonic-gate 			/*
13597c478bd9Sstevel@tonic-gate 			 * Reply is good, check auth.
13607c478bd9Sstevel@tonic-gate 			 */
13617c478bd9Sstevel@tonic-gate 			if (!AUTH_VALIDATE(h->cl_auth,
13627c478bd9Sstevel@tonic-gate 			    &reply_msg.acpted_rply.ar_verf)) {
13637c478bd9Sstevel@tonic-gate 				COTSRCSTAT_INCR(p->cku_stats, rcbadverfs);
13647c478bd9Sstevel@tonic-gate 				RPCLOG0(1, "clnt_cots_kcallit: validation "
13657c478bd9Sstevel@tonic-gate 				    "failure\n");
13667c478bd9Sstevel@tonic-gate 				freemsg(mp);
13677c478bd9Sstevel@tonic-gate 				(void) xdr_rpc_free_verifier(xdrs, &reply_msg);
13687c478bd9Sstevel@tonic-gate 				mutex_enter(&call->call_lock);
13697c478bd9Sstevel@tonic-gate 				if (call->call_reply == NULL)
13707c478bd9Sstevel@tonic-gate 					call->call_status = RPC_TIMEDOUT;
13717c478bd9Sstevel@tonic-gate 				mutex_exit(&call->call_lock);
13727c478bd9Sstevel@tonic-gate 				goto read_again;
13737c478bd9Sstevel@tonic-gate 			} else if (!AUTH_UNWRAP(h->cl_auth, xdrs,
13747c478bd9Sstevel@tonic-gate 			    xdr_results, resultsp)) {
13757c478bd9Sstevel@tonic-gate 				RPCLOG0(1, "clnt_cots_kcallit: validation "
13767c478bd9Sstevel@tonic-gate 				    "failure (unwrap)\n");
13777c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_CANTDECODERES;
13787c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
13797c478bd9Sstevel@tonic-gate 			}
13807c478bd9Sstevel@tonic-gate 		} else {
13817c478bd9Sstevel@tonic-gate 			/* set errno in case we can't recover */
13827c478bd9Sstevel@tonic-gate 			if (re_status != RPC_VERSMISMATCH &&
13837c478bd9Sstevel@tonic-gate 			    re_status != RPC_AUTHERROR &&
13847c478bd9Sstevel@tonic-gate 			    re_status != RPC_PROGVERSMISMATCH)
13857c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate 			if (re_status == RPC_AUTHERROR) {
13887c478bd9Sstevel@tonic-gate 				/*
1389a33e37fdSrg137905 				 * Maybe our credential need to be refreshed
13907c478bd9Sstevel@tonic-gate 				 */
1391a85a6121Srg137905 				if (cm_entry) {
1392a85a6121Srg137905 					/*
1393a85a6121Srg137905 					 * There is the potential that the
1394a85a6121Srg137905 					 * cm_entry has/will be marked dead,
1395a33e37fdSrg137905 					 * so drop the connection altogether,
1396a33e37fdSrg137905 					 * force REFRESH to establish new
1397a33e37fdSrg137905 					 * connection.
1398a85a6121Srg137905 					 */
1399a33e37fdSrg137905 					connmgr_cancelconn(cm_entry);
1400a85a6121Srg137905 					cm_entry = NULL;
1401a85a6121Srg137905 				}
1402a85a6121Srg137905 
1403a85a6121Srg137905 				(void) xdr_rpc_free_verifier(xdrs,
1404a85a6121Srg137905 				    &reply_msg);
14057c478bd9Sstevel@tonic-gate 
14067c478bd9Sstevel@tonic-gate 				if (p->cku_flags & CKU_ONQUEUE) {
14077c478bd9Sstevel@tonic-gate 					call_table_remove(call);
14087c478bd9Sstevel@tonic-gate 					p->cku_flags &= ~CKU_ONQUEUE;
14097c478bd9Sstevel@tonic-gate 				}
1410a85a6121Srg137905 				RPCLOG(64,
1411a85a6121Srg137905 				    "clnt_cots_kcallit: AUTH_ERROR, xid"
1412a85a6121Srg137905 				    " 0x%x removed off dispatch list\n",
14137c478bd9Sstevel@tonic-gate 				    p->cku_xid);
14147c478bd9Sstevel@tonic-gate 				if (call->call_reply) {
14157c478bd9Sstevel@tonic-gate 					freemsg(call->call_reply);
14167c478bd9Sstevel@tonic-gate 					call->call_reply = NULL;
14177c478bd9Sstevel@tonic-gate 				}
1418a85a6121Srg137905 
141928a15eaaSMarcel Telka 				if ((refreshes > 0) &&
142028a15eaaSMarcel Telka 				    AUTH_REFRESH(h->cl_auth, &reply_msg,
142128a15eaaSMarcel Telka 				    p->cku_cred)) {
142228a15eaaSMarcel Telka 					refreshes--;
142328a15eaaSMarcel Telka 					freemsg(mp);
142428a15eaaSMarcel Telka 					mp = NULL;
142528a15eaaSMarcel Telka 
1426a85a6121Srg137905 					COTSRCSTAT_INCR(p->cku_stats,
1427a85a6121Srg137905 					    rcbadcalls);
1428a85a6121Srg137905 					COTSRCSTAT_INCR(p->cku_stats,
1429a85a6121Srg137905 					    rcnewcreds);
14307c478bd9Sstevel@tonic-gate 					goto call_again;
1431a33e37fdSrg137905 				}
1432a33e37fdSrg137905 
14337c478bd9Sstevel@tonic-gate 				/*
14347c478bd9Sstevel@tonic-gate 				 * We have used the client handle to
14357c478bd9Sstevel@tonic-gate 				 * do an AUTH_REFRESH and the RPC status may
14367c478bd9Sstevel@tonic-gate 				 * be set to RPC_SUCCESS; Let's make sure to
14377c478bd9Sstevel@tonic-gate 				 * set it to RPC_AUTHERROR.
14387c478bd9Sstevel@tonic-gate 				 */
14397c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_AUTHERROR;
1440a33e37fdSrg137905 
14417c478bd9Sstevel@tonic-gate 				/*
14427c478bd9Sstevel@tonic-gate 				 * Map recoverable and unrecoverable
14437c478bd9Sstevel@tonic-gate 				 * authentication errors to appropriate errno
14447c478bd9Sstevel@tonic-gate 				 */
14457c478bd9Sstevel@tonic-gate 				switch (p->cku_err.re_why) {
144678598ee3Snd150628 				case AUTH_TOOWEAK:
144778598ee3Snd150628 					/*
1448a85a6121Srg137905 					 * This could be a failure where the
1449a85a6121Srg137905 					 * server requires use of a reserved
1450a85a6121Srg137905 					 * port,  check and optionally set the
1451a85a6121Srg137905 					 * client handle useresvport trying
1452a85a6121Srg137905 					 * one more time. Next go round we
1453a85a6121Srg137905 					 * fall out with the tooweak error.
145478598ee3Snd150628 					 */
145578598ee3Snd150628 					if (p->cku_useresvport != 1) {
145678598ee3Snd150628 						p->cku_useresvport = 1;
145778598ee3Snd150628 						p->cku_xid = 0;
145878598ee3Snd150628 						freemsg(mp);
145928a15eaaSMarcel Telka 						mp = NULL;
146078598ee3Snd150628 						goto call_again;
146178598ee3Snd150628 					}
146278598ee3Snd150628 					/* FALLTHRU */
14637c478bd9Sstevel@tonic-gate 				case AUTH_BADCRED:
14647c478bd9Sstevel@tonic-gate 				case AUTH_BADVERF:
14657c478bd9Sstevel@tonic-gate 				case AUTH_INVALIDRESP:
14667c478bd9Sstevel@tonic-gate 				case AUTH_FAILED:
14677c478bd9Sstevel@tonic-gate 				case RPCSEC_GSS_NOCRED:
14687c478bd9Sstevel@tonic-gate 				case RPCSEC_GSS_FAILED:
14697c478bd9Sstevel@tonic-gate 						p->cku_err.re_errno = EACCES;
14707c478bd9Sstevel@tonic-gate 						break;
14717c478bd9Sstevel@tonic-gate 				case AUTH_REJECTEDCRED:
14727c478bd9Sstevel@tonic-gate 				case AUTH_REJECTEDVERF:
14737c478bd9Sstevel@tonic-gate 				default:	p->cku_err.re_errno = EIO;
14747c478bd9Sstevel@tonic-gate 						break;
14757c478bd9Sstevel@tonic-gate 				}
14767c478bd9Sstevel@tonic-gate 				RPCLOG(1, "clnt_cots_kcallit : authentication"
14777c478bd9Sstevel@tonic-gate 				    " failed with RPC_AUTHERROR of type %d\n",
14787c478bd9Sstevel@tonic-gate 				    (int)p->cku_err.re_why);
147928a15eaaSMarcel Telka 				goto cots_done;
14807c478bd9Sstevel@tonic-gate 			}
14817c478bd9Sstevel@tonic-gate 		}
14827c478bd9Sstevel@tonic-gate 	} else {
14837c478bd9Sstevel@tonic-gate 		/* reply didn't decode properly. */
14847c478bd9Sstevel@tonic-gate 		p->cku_err.re_status = RPC_CANTDECODERES;
14857c478bd9Sstevel@tonic-gate 		p->cku_err.re_errno = EIO;
14867c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcallit: decode failure\n");
14877c478bd9Sstevel@tonic-gate 	}
14887c478bd9Sstevel@tonic-gate 
14897c478bd9Sstevel@tonic-gate 	(void) xdr_rpc_free_verifier(xdrs, &reply_msg);
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 	if (p->cku_flags & CKU_ONQUEUE) {
14927c478bd9Sstevel@tonic-gate 		call_table_remove(call);
14937c478bd9Sstevel@tonic-gate 		p->cku_flags &= ~CKU_ONQUEUE;
14947c478bd9Sstevel@tonic-gate 	}
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	RPCLOG(64, "clnt_cots_kcallit: xid 0x%x taken off dispatch list",
14977c478bd9Sstevel@tonic-gate 	    p->cku_xid);
14987c478bd9Sstevel@tonic-gate 	RPCLOG(64, " status is %s\n", clnt_sperrno(p->cku_err.re_status));
14997c478bd9Sstevel@tonic-gate cots_done:
15007c478bd9Sstevel@tonic-gate 	if (cm_entry)
15017c478bd9Sstevel@tonic-gate 		connmgr_release(cm_entry);
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate 	if (mp != NULL)
15047c478bd9Sstevel@tonic-gate 		freemsg(mp);
15057c478bd9Sstevel@tonic-gate 	if ((p->cku_flags & CKU_ONQUEUE) == 0 && call->call_reply) {
15067c478bd9Sstevel@tonic-gate 		freemsg(call->call_reply);
15077c478bd9Sstevel@tonic-gate 		call->call_reply = NULL;
15087c478bd9Sstevel@tonic-gate 	}
15097c478bd9Sstevel@tonic-gate 	if (p->cku_err.re_status != RPC_SUCCESS) {
15107c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcallit: tail-end failure\n");
15117c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(p->cku_stats, rcbadcalls);
15127c478bd9Sstevel@tonic-gate 	}
15137c478bd9Sstevel@tonic-gate 
15147c478bd9Sstevel@tonic-gate 	/*
15157c478bd9Sstevel@tonic-gate 	 * No point in delaying if the zone is going away.
15167c478bd9Sstevel@tonic-gate 	 */
15177c478bd9Sstevel@tonic-gate 	if (delay_first == TRUE &&
15187c478bd9Sstevel@tonic-gate 	    !(zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)) {
15197c478bd9Sstevel@tonic-gate 		if (clnt_delay(ticks, h->cl_nosignal) == EINTR) {
15207c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EINTR;
15217c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_INTR;
15227c478bd9Sstevel@tonic-gate 		}
15237c478bd9Sstevel@tonic-gate 	}
15247c478bd9Sstevel@tonic-gate 	return (p->cku_err.re_status);
15257c478bd9Sstevel@tonic-gate }
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate /*
15287c478bd9Sstevel@tonic-gate  * Kinit routine for cots.  This sets up the correct operations in
15297c478bd9Sstevel@tonic-gate  * the client handle, as the handle may have previously been a clts
15307c478bd9Sstevel@tonic-gate  * handle, and clears the xid field so there is no way a new call
15317c478bd9Sstevel@tonic-gate  * could be mistaken for a retry.  It also sets in the handle the
15327c478bd9Sstevel@tonic-gate  * information that is passed at create/kinit time but needed at
15337c478bd9Sstevel@tonic-gate  * call time, as cots creates the transport at call time - device,
15347c478bd9Sstevel@tonic-gate  * address of the server, protocol family.
15357c478bd9Sstevel@tonic-gate  */
15367c478bd9Sstevel@tonic-gate void
clnt_cots_kinit(CLIENT * h,dev_t dev,int family,struct netbuf * addr,int max_msgsize,cred_t * cred)15377c478bd9Sstevel@tonic-gate clnt_cots_kinit(CLIENT *h, dev_t dev, int family, struct netbuf *addr,
15387c478bd9Sstevel@tonic-gate 	int max_msgsize, cred_t *cred)
15397c478bd9Sstevel@tonic-gate {
15407c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
15417c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
15427c478bd9Sstevel@tonic-gate 	calllist_t *call = &p->cku_call;
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate 	h->cl_ops = &tcp_ops;
15457c478bd9Sstevel@tonic-gate 	if (p->cku_flags & CKU_ONQUEUE) {
15467c478bd9Sstevel@tonic-gate 		call_table_remove(call);
15477c478bd9Sstevel@tonic-gate 		p->cku_flags &= ~CKU_ONQUEUE;
15487c478bd9Sstevel@tonic-gate 		RPCLOG(64, "clnt_cots_kinit: removing call for xid 0x%x from"
15497c478bd9Sstevel@tonic-gate 		    " dispatch list\n", p->cku_xid);
15507c478bd9Sstevel@tonic-gate 	}
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	if (call->call_reply != NULL) {
15537c478bd9Sstevel@tonic-gate 		freemsg(call->call_reply);
15547c478bd9Sstevel@tonic-gate 		call->call_reply = NULL;
15557c478bd9Sstevel@tonic-gate 	}
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 	call->call_bucket = NULL;
15587c478bd9Sstevel@tonic-gate 	call->call_hash = 0;
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate 	/*
15617c478bd9Sstevel@tonic-gate 	 * We don't clear cku_flags here, because clnt_cots_kcallit()
15627c478bd9Sstevel@tonic-gate 	 * takes care of handling the cku_flags reset.
15637c478bd9Sstevel@tonic-gate 	 */
15647c478bd9Sstevel@tonic-gate 	p->cku_xid = 0;
15657c478bd9Sstevel@tonic-gate 	p->cku_device = dev;
15667c478bd9Sstevel@tonic-gate 	p->cku_addrfmly = family;
15677c478bd9Sstevel@tonic-gate 	p->cku_cred = cred;
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate 	if (p->cku_addr.maxlen < addr->len) {
15707c478bd9Sstevel@tonic-gate 		if (p->cku_addr.maxlen != 0 && p->cku_addr.buf != NULL)
15717c478bd9Sstevel@tonic-gate 			kmem_free(p->cku_addr.buf, p->cku_addr.maxlen);
15727c478bd9Sstevel@tonic-gate 		p->cku_addr.buf = kmem_zalloc(addr->maxlen, KM_SLEEP);
15737c478bd9Sstevel@tonic-gate 		p->cku_addr.maxlen = addr->maxlen;
15747c478bd9Sstevel@tonic-gate 	}
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 	p->cku_addr.len = addr->len;
15777c478bd9Sstevel@tonic-gate 	bcopy(addr->buf, p->cku_addr.buf, addr->len);
15787c478bd9Sstevel@tonic-gate 
15797c478bd9Sstevel@tonic-gate 	/*
15807c478bd9Sstevel@tonic-gate 	 * If the current sanity check size in rpcmod is smaller
15817c478bd9Sstevel@tonic-gate 	 * than the size needed, then increase the sanity check.
15827c478bd9Sstevel@tonic-gate 	 */
15837c478bd9Sstevel@tonic-gate 	if (max_msgsize != 0 && clnt_max_msg_sizep != NULL &&
15847c478bd9Sstevel@tonic-gate 	    max_msgsize > *clnt_max_msg_sizep) {
15857c478bd9Sstevel@tonic-gate 		mutex_enter(&clnt_max_msg_lock);
15867c478bd9Sstevel@tonic-gate 		if (max_msgsize > *clnt_max_msg_sizep)
15877c478bd9Sstevel@tonic-gate 			*clnt_max_msg_sizep = max_msgsize;
15887c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_max_msg_lock);
15897c478bd9Sstevel@tonic-gate 	}
15907c478bd9Sstevel@tonic-gate }
15917c478bd9Sstevel@tonic-gate 
15927c478bd9Sstevel@tonic-gate /*
15937c478bd9Sstevel@tonic-gate  * ksettimers is a no-op for cots, with the exception of setting the xid.
15947c478bd9Sstevel@tonic-gate  */
15957c478bd9Sstevel@tonic-gate /* ARGSUSED */
15967c478bd9Sstevel@tonic-gate static int
clnt_cots_ksettimers(CLIENT * h,struct rpc_timers * t,struct rpc_timers * all,int minimum,void (* feedback)(int,int,caddr_t),caddr_t arg,uint32_t xid)15977c478bd9Sstevel@tonic-gate clnt_cots_ksettimers(CLIENT *h, struct rpc_timers *t, struct rpc_timers *all,
15987c478bd9Sstevel@tonic-gate 	int minimum, void (*feedback)(int, int, caddr_t), caddr_t arg,
15997c478bd9Sstevel@tonic-gate 	uint32_t xid)
16007c478bd9Sstevel@tonic-gate {
16017c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
16027c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
16037c478bd9Sstevel@tonic-gate 
16047c478bd9Sstevel@tonic-gate 	if (xid)
16057c478bd9Sstevel@tonic-gate 		p->cku_xid = xid;
16067c478bd9Sstevel@tonic-gate 	COTSRCSTAT_INCR(p->cku_stats, rctimers);
16077c478bd9Sstevel@tonic-gate 	return (0);
16087c478bd9Sstevel@tonic-gate }
16097c478bd9Sstevel@tonic-gate 
16107c478bd9Sstevel@tonic-gate extern void rpc_poptimod(struct vnode *);
16117c478bd9Sstevel@tonic-gate extern int kstr_push(struct vnode *, char *);
16127c478bd9Sstevel@tonic-gate 
16137c478bd9Sstevel@tonic-gate int
conn_kstat_update(kstat_t * ksp,int rw)16147c478bd9Sstevel@tonic-gate conn_kstat_update(kstat_t *ksp, int rw)
16157c478bd9Sstevel@tonic-gate {
16167c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
16177c478bd9Sstevel@tonic-gate 	struct cm_kstat_xprt *cm_ksp_data;
16187c478bd9Sstevel@tonic-gate 	uchar_t *b;
16197c478bd9Sstevel@tonic-gate 	char *fbuf;
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
16227c478bd9Sstevel@tonic-gate 		return (EACCES);
16237c478bd9Sstevel@tonic-gate 	if (ksp == NULL || ksp->ks_private == NULL)
16247c478bd9Sstevel@tonic-gate 		return (EIO);
16257c478bd9Sstevel@tonic-gate 	cm_entry  = (struct cm_xprt *)ksp->ks_private;
16267c478bd9Sstevel@tonic-gate 	cm_ksp_data = (struct cm_kstat_xprt *)ksp->ks_data;
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_wq.value.ui32 = (uint32_t)(uintptr_t)cm_entry->x_wq;
16297c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_family.value.ui32 = cm_entry->x_family;
16307c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_rdev.value.ui32 = (uint32_t)cm_entry->x_rdev;
16317c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_time.value.ui32 = cm_entry->x_time;
16327c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_ref.value.ui32 = cm_entry->x_ref;
16337c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_state.value.ui32 = cm_entry->x_state_flags;
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 	if (cm_entry->x_server.buf) {
1636a1b5e537Sbmc 		fbuf = cm_ksp_data->x_server.value.str.addr.ptr;
16377c478bd9Sstevel@tonic-gate 		if (cm_entry->x_family == AF_INET &&
16387c478bd9Sstevel@tonic-gate 		    cm_entry->x_server.len ==
16397c478bd9Sstevel@tonic-gate 		    sizeof (struct sockaddr_in)) {
16407c478bd9Sstevel@tonic-gate 			struct sockaddr_in  *sa;
16417c478bd9Sstevel@tonic-gate 			sa = (struct sockaddr_in *)
16427c478bd9Sstevel@tonic-gate 				cm_entry->x_server.buf;
16437c478bd9Sstevel@tonic-gate 			b = (uchar_t *)&sa->sin_addr;
16447c478bd9Sstevel@tonic-gate 			(void) sprintf(fbuf,
16457c478bd9Sstevel@tonic-gate 			    "%03d.%03d.%03d.%03d", b[0] & 0xFF, b[1] & 0xFF,
16467c478bd9Sstevel@tonic-gate 			    b[2] & 0xFF, b[3] & 0xFF);
16477c478bd9Sstevel@tonic-gate 			cm_ksp_data->x_port.value.ui32 =
16487c478bd9Sstevel@tonic-gate 				(uint32_t)sa->sin_port;
16497c478bd9Sstevel@tonic-gate 		} else if (cm_entry->x_family == AF_INET6 &&
16507c478bd9Sstevel@tonic-gate 				cm_entry->x_server.len >=
16517c478bd9Sstevel@tonic-gate 				sizeof (struct sockaddr_in6)) {
16527c478bd9Sstevel@tonic-gate 			/* extract server IP address & port */
16537c478bd9Sstevel@tonic-gate 			struct sockaddr_in6 *sin6;
16547c478bd9Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)cm_entry->x_server.buf;
16557c478bd9Sstevel@tonic-gate 			(void) kinet_ntop6((uchar_t *)&sin6->sin6_addr, fbuf,
16567c478bd9Sstevel@tonic-gate 				INET6_ADDRSTRLEN);
16577c478bd9Sstevel@tonic-gate 			cm_ksp_data->x_port.value.ui32 = sin6->sin6_port;
16587c478bd9Sstevel@tonic-gate 		} else {
16597c478bd9Sstevel@tonic-gate 			struct sockaddr_in  *sa;
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 			sa = (struct sockaddr_in *)cm_entry->x_server.buf;
16627c478bd9Sstevel@tonic-gate 			b = (uchar_t *)&sa->sin_addr;
16637c478bd9Sstevel@tonic-gate 			(void) sprintf(fbuf,
16647c478bd9Sstevel@tonic-gate 			    "%03d.%03d.%03d.%03d", b[0] & 0xFF, b[1] & 0xFF,
16657c478bd9Sstevel@tonic-gate 			    b[2] & 0xFF, b[3] & 0xFF);
16667c478bd9Sstevel@tonic-gate 		}
16677c478bd9Sstevel@tonic-gate 		KSTAT_NAMED_STR_BUFLEN(&cm_ksp_data->x_server) =
16687c478bd9Sstevel@tonic-gate 		    strlen(fbuf) + 1;
16697c478bd9Sstevel@tonic-gate 	}
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate 	return (0);
16727c478bd9Sstevel@tonic-gate }
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate 
16757c478bd9Sstevel@tonic-gate /*
16767c478bd9Sstevel@tonic-gate  * We want a version of delay which is interruptible by a UNIX signal
16777c478bd9Sstevel@tonic-gate  * Return EINTR if an interrupt occured.
16787c478bd9Sstevel@tonic-gate  */
16797c478bd9Sstevel@tonic-gate static int
clnt_delay(clock_t ticks,bool_t nosignal)16807c478bd9Sstevel@tonic-gate clnt_delay(clock_t ticks, bool_t nosignal)
16817c478bd9Sstevel@tonic-gate {
16827c478bd9Sstevel@tonic-gate 	if (nosignal == TRUE) {
16837c478bd9Sstevel@tonic-gate 		delay(ticks);
16847c478bd9Sstevel@tonic-gate 		return (0);
16857c478bd9Sstevel@tonic-gate 	}
16867c478bd9Sstevel@tonic-gate 	return (delay_sig(ticks));
16877c478bd9Sstevel@tonic-gate }
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate /*
16907c478bd9Sstevel@tonic-gate  * Wait for a connection until a timeout, or until we are
16917c478bd9Sstevel@tonic-gate  * signalled that there has been a connection state change.
16927c478bd9Sstevel@tonic-gate  */
16937c478bd9Sstevel@tonic-gate static enum clnt_stat
connmgr_cwait(struct cm_xprt * cm_entry,const struct timeval * waitp,bool_t nosignal)16947c478bd9Sstevel@tonic-gate connmgr_cwait(struct cm_xprt *cm_entry, const struct timeval *waitp,
16957c478bd9Sstevel@tonic-gate 	bool_t nosignal)
16967c478bd9Sstevel@tonic-gate {
16977c478bd9Sstevel@tonic-gate 	bool_t interrupted;
16987c478bd9Sstevel@tonic-gate 	clock_t timout, cv_stat;
16997c478bd9Sstevel@tonic-gate 	enum clnt_stat clstat;
17007c478bd9Sstevel@tonic-gate 	unsigned int old_state;
17017c478bd9Sstevel@tonic-gate 
17027c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
17037c478bd9Sstevel@tonic-gate 	/*
17047c478bd9Sstevel@tonic-gate 	 * We wait for the transport connection to be made, or an
17057c478bd9Sstevel@tonic-gate 	 * indication that it could not be made.
17067c478bd9Sstevel@tonic-gate 	 */
17077c478bd9Sstevel@tonic-gate 	clstat = RPC_TIMEDOUT;
17087c478bd9Sstevel@tonic-gate 	interrupted = FALSE;
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 	old_state = cm_entry->x_state_flags;
17117c478bd9Sstevel@tonic-gate 	/*
17127c478bd9Sstevel@tonic-gate 	 * Now loop until cv_timedwait{_sig} returns because of
17137c478bd9Sstevel@tonic-gate 	 * a signal(0) or timeout(-1) or cv_signal(>0). But it may be
17147c478bd9Sstevel@tonic-gate 	 * cv_signalled for various other reasons too. So loop
17157c478bd9Sstevel@tonic-gate 	 * until there is a state change on the connection.
17167c478bd9Sstevel@tonic-gate 	 */
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 	timout = waitp->tv_sec * drv_usectohz(1000000) +
1719d3d50737SRafael Vanoni 	    drv_usectohz(waitp->tv_usec) + ddi_get_lbolt();
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	if (nosignal) {
17227c478bd9Sstevel@tonic-gate 		while ((cv_stat = cv_timedwait(&cm_entry->x_conn_cv,
17237c478bd9Sstevel@tonic-gate 		    &connmgr_lock, timout)) > 0 &&
17247c478bd9Sstevel@tonic-gate 		    cm_entry->x_state_flags == old_state)
17257c478bd9Sstevel@tonic-gate 			;
17267c478bd9Sstevel@tonic-gate 	} else {
17277c478bd9Sstevel@tonic-gate 		while ((cv_stat = cv_timedwait_sig(&cm_entry->x_conn_cv,
17287c478bd9Sstevel@tonic-gate 		    &connmgr_lock, timout)) > 0 &&
17297c478bd9Sstevel@tonic-gate 		    cm_entry->x_state_flags == old_state)
17307c478bd9Sstevel@tonic-gate 			;
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 		if (cv_stat == 0) /* got intr signal? */
17337c478bd9Sstevel@tonic-gate 			interrupted = TRUE;
17347c478bd9Sstevel@tonic-gate 	}
17357c478bd9Sstevel@tonic-gate 
17367c478bd9Sstevel@tonic-gate 	if ((cm_entry->x_state_flags & (X_BADSTATES|X_CONNECTED)) ==
17377c478bd9Sstevel@tonic-gate 	    X_CONNECTED) {
17387c478bd9Sstevel@tonic-gate 		clstat = RPC_SUCCESS;
17397c478bd9Sstevel@tonic-gate 	} else {
17407c478bd9Sstevel@tonic-gate 		if (interrupted == TRUE)
17417c478bd9Sstevel@tonic-gate 			clstat = RPC_INTR;
17427c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_cwait: can't connect, error: %s\n",
17437c478bd9Sstevel@tonic-gate 		    clnt_sperrno(clstat));
17447c478bd9Sstevel@tonic-gate 	}
17457c478bd9Sstevel@tonic-gate 
17467c478bd9Sstevel@tonic-gate 	return (clstat);
17477c478bd9Sstevel@tonic-gate }
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate /*
17507c478bd9Sstevel@tonic-gate  * Primary interface for how RPC grabs a connection.
17517c478bd9Sstevel@tonic-gate  */
17527c478bd9Sstevel@tonic-gate static struct cm_xprt *
connmgr_wrapget(struct netbuf * retryaddr,const struct timeval * waitp,cku_private_t * p)17537c478bd9Sstevel@tonic-gate connmgr_wrapget(
17547c478bd9Sstevel@tonic-gate 	struct netbuf *retryaddr,
17557c478bd9Sstevel@tonic-gate 	const struct timeval *waitp,
17567c478bd9Sstevel@tonic-gate 	cku_private_t *p)
17577c478bd9Sstevel@tonic-gate {
17587c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 	cm_entry = connmgr_get(retryaddr, waitp, &p->cku_addr, p->cku_addrfmly,
17617c478bd9Sstevel@tonic-gate 	    &p->cku_srcaddr, &p->cku_err, p->cku_device,
1762de8c4a14SErik Nordmark 	    p->cku_client.cl_nosignal, p->cku_useresvport, p->cku_cred);
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate 	if (cm_entry == NULL) {
17657c478bd9Sstevel@tonic-gate 		/*
17667c478bd9Sstevel@tonic-gate 		 * Re-map the call status to RPC_INTR if the err code is
17677c478bd9Sstevel@tonic-gate 		 * EINTR. This can happen if calls status is RPC_TLIERROR.
17687c478bd9Sstevel@tonic-gate 		 * However, don't re-map if signalling has been turned off.
17697c478bd9Sstevel@tonic-gate 		 * XXX Really need to create a separate thread whenever
17707c478bd9Sstevel@tonic-gate 		 * there isn't an existing connection.
17717c478bd9Sstevel@tonic-gate 		 */
17727c478bd9Sstevel@tonic-gate 		if (p->cku_err.re_errno == EINTR) {
17737c478bd9Sstevel@tonic-gate 			if (p->cku_client.cl_nosignal == TRUE)
17747c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
17757c478bd9Sstevel@tonic-gate 			else
17767c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_INTR;
17777c478bd9Sstevel@tonic-gate 		}
17787c478bd9Sstevel@tonic-gate 	}
17797c478bd9Sstevel@tonic-gate 
17807c478bd9Sstevel@tonic-gate 	return (cm_entry);
17817c478bd9Sstevel@tonic-gate }
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate /*
17847c478bd9Sstevel@tonic-gate  * Obtains a transport to the server specified in addr.  If a suitable transport
17857c478bd9Sstevel@tonic-gate  * does not already exist in the list of cached transports, a new connection
17867c478bd9Sstevel@tonic-gate  * is created, connected, and added to the list. The connection is for sending
17877c478bd9Sstevel@tonic-gate  * only - the reply message may come back on another transport connection.
178881dbf0b5SDai Ngo  *
178981dbf0b5SDai Ngo  * To implement round-robin load balancing with multiple client connections,
179081dbf0b5SDai Ngo  * the last entry on the list is always selected. Once the entry is selected
179181dbf0b5SDai Ngo  * it's re-inserted to the head of the list.
17927c478bd9Sstevel@tonic-gate  */
17937c478bd9Sstevel@tonic-gate static struct cm_xprt *
connmgr_get(struct netbuf * retryaddr,const struct timeval * waitp,struct netbuf * destaddr,int addrfmly,struct netbuf * srcaddr,struct rpc_err * rpcerr,dev_t device,bool_t nosignal,int useresvport,cred_t * cr)17947c478bd9Sstevel@tonic-gate connmgr_get(
17957c478bd9Sstevel@tonic-gate 	struct netbuf	*retryaddr,
17967c478bd9Sstevel@tonic-gate 	const struct timeval	*waitp,	/* changed to a ptr to converse stack */
17977c478bd9Sstevel@tonic-gate 	struct netbuf	*destaddr,
17987c478bd9Sstevel@tonic-gate 	int		addrfmly,
17997c478bd9Sstevel@tonic-gate 	struct netbuf	*srcaddr,
18007c478bd9Sstevel@tonic-gate 	struct rpc_err	*rpcerr,
18017c478bd9Sstevel@tonic-gate 	dev_t		device,
18027c478bd9Sstevel@tonic-gate 	bool_t		nosignal,
1803de8c4a14SErik Nordmark 	int		useresvport,
1804de8c4a14SErik Nordmark 	cred_t		*cr)
18057c478bd9Sstevel@tonic-gate {
18067c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
18077c478bd9Sstevel@tonic-gate 	struct cm_xprt *lru_entry;
180881dbf0b5SDai Ngo 	struct cm_xprt **cmp, **prev;
18097c478bd9Sstevel@tonic-gate 	queue_t *wq;
18107c478bd9Sstevel@tonic-gate 	TIUSER *tiptr;
18117c478bd9Sstevel@tonic-gate 	int i;
18127c478bd9Sstevel@tonic-gate 	int retval;
18137c478bd9Sstevel@tonic-gate 	int tidu_size;
18147c478bd9Sstevel@tonic-gate 	bool_t	connected;
1815108322fbScarlsonj 	zoneid_t zoneid = rpc_zoneid();
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate 	/*
18187c478bd9Sstevel@tonic-gate 	 * If the call is not a retry, look for a transport entry that
18197c478bd9Sstevel@tonic-gate 	 * goes to the server of interest.
18207c478bd9Sstevel@tonic-gate 	 */
18217c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 	if (retryaddr == NULL) {
18247c478bd9Sstevel@tonic-gate use_new_conn:
18257c478bd9Sstevel@tonic-gate 		i = 0;
18267c478bd9Sstevel@tonic-gate 		cm_entry = lru_entry = NULL;
18277c478bd9Sstevel@tonic-gate 
182881dbf0b5SDai Ngo 		prev = cmp = &cm_hd;
18297c478bd9Sstevel@tonic-gate 		while ((cm_entry = *cmp) != NULL) {
18307c478bd9Sstevel@tonic-gate 			ASSERT(cm_entry != cm_entry->x_next);
18317c478bd9Sstevel@tonic-gate 			/*
18327c478bd9Sstevel@tonic-gate 			 * Garbage collect conections that are marked
18337c478bd9Sstevel@tonic-gate 			 * for needs disconnect.
18347c478bd9Sstevel@tonic-gate 			 */
18357c478bd9Sstevel@tonic-gate 			if (cm_entry->x_needdis) {
1836418d27f3Sshepler 				CONN_HOLD(cm_entry);
18377c478bd9Sstevel@tonic-gate 				connmgr_dis_and_wait(cm_entry);
1838418d27f3Sshepler 				connmgr_release(cm_entry);
18397c478bd9Sstevel@tonic-gate 				/*
18407c478bd9Sstevel@tonic-gate 				 * connmgr_lock could have been
18417c478bd9Sstevel@tonic-gate 				 * dropped for the disconnect
18427c478bd9Sstevel@tonic-gate 				 * processing so start over.
18437c478bd9Sstevel@tonic-gate 				 */
18447c478bd9Sstevel@tonic-gate 				goto use_new_conn;
18457c478bd9Sstevel@tonic-gate 			}
18467c478bd9Sstevel@tonic-gate 
18477c478bd9Sstevel@tonic-gate 			/*
18487c478bd9Sstevel@tonic-gate 			 * Garbage collect the dead connections that have
18497c478bd9Sstevel@tonic-gate 			 * no threads working on them.
18507c478bd9Sstevel@tonic-gate 			 */
18517c478bd9Sstevel@tonic-gate 			if ((cm_entry->x_state_flags & (X_DEAD|X_THREAD)) ==
18527c478bd9Sstevel@tonic-gate 			    X_DEAD) {
18538f7c43eaSmaheshvs 				mutex_enter(&cm_entry->x_lock);
18548f7c43eaSmaheshvs 				if (cm_entry->x_ref != 0) {
18558f7c43eaSmaheshvs 					/*
18568f7c43eaSmaheshvs 					 * Currently in use.
18578f7c43eaSmaheshvs 					 * Cleanup later.
18588f7c43eaSmaheshvs 					 */
18598f7c43eaSmaheshvs 					cmp = &cm_entry->x_next;
18608f7c43eaSmaheshvs 					mutex_exit(&cm_entry->x_lock);
18618f7c43eaSmaheshvs 					continue;
18628f7c43eaSmaheshvs 				}
18638f7c43eaSmaheshvs 				mutex_exit(&cm_entry->x_lock);
18647c478bd9Sstevel@tonic-gate 				*cmp = cm_entry->x_next;
18657c478bd9Sstevel@tonic-gate 				mutex_exit(&connmgr_lock);
18667c478bd9Sstevel@tonic-gate 				connmgr_close(cm_entry);
18677c478bd9Sstevel@tonic-gate 				mutex_enter(&connmgr_lock);
18687c478bd9Sstevel@tonic-gate 				goto use_new_conn;
18697c478bd9Sstevel@tonic-gate 			}
18707c478bd9Sstevel@tonic-gate 
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 			if ((cm_entry->x_state_flags & X_BADSTATES) == 0 &&
18737c478bd9Sstevel@tonic-gate 			    cm_entry->x_zoneid == zoneid &&
18747c478bd9Sstevel@tonic-gate 			    cm_entry->x_rdev == device &&
18757c478bd9Sstevel@tonic-gate 			    destaddr->len == cm_entry->x_server.len &&
18767c478bd9Sstevel@tonic-gate 			    bcmp(destaddr->buf, cm_entry->x_server.buf,
18777c478bd9Sstevel@tonic-gate 			    destaddr->len) == 0) {
18787c478bd9Sstevel@tonic-gate 				/*
18797c478bd9Sstevel@tonic-gate 				 * If the matching entry isn't connected,
18807c478bd9Sstevel@tonic-gate 				 * attempt to reconnect it.
18817c478bd9Sstevel@tonic-gate 				 */
18827c478bd9Sstevel@tonic-gate 				if (cm_entry->x_connected == FALSE) {
18837c478bd9Sstevel@tonic-gate 					/*
18847c478bd9Sstevel@tonic-gate 					 * We don't go through trying
18857c478bd9Sstevel@tonic-gate 					 * to find the least recently
18867c478bd9Sstevel@tonic-gate 					 * used connected because
18877c478bd9Sstevel@tonic-gate 					 * connmgr_reconnect() briefly
18887c478bd9Sstevel@tonic-gate 					 * dropped the connmgr_lock,
18897c478bd9Sstevel@tonic-gate 					 * allowing a window for our
18907c478bd9Sstevel@tonic-gate 					 * accounting to be messed up.
18917c478bd9Sstevel@tonic-gate 					 * In any case, a re-connected
18927c478bd9Sstevel@tonic-gate 					 * connection is as good as
18937c478bd9Sstevel@tonic-gate 					 * a LRU connection.
18947c478bd9Sstevel@tonic-gate 					 */
18957c478bd9Sstevel@tonic-gate 					return (connmgr_wrapconnect(cm_entry,
18967c478bd9Sstevel@tonic-gate 					    waitp, destaddr, addrfmly, srcaddr,
1897de8c4a14SErik Nordmark 					    rpcerr, TRUE, nosignal, cr));
18987c478bd9Sstevel@tonic-gate 				}
18997c478bd9Sstevel@tonic-gate 				i++;
190081dbf0b5SDai Ngo 
190181dbf0b5SDai Ngo 				/* keep track of the last entry */
19027c478bd9Sstevel@tonic-gate 				lru_entry = cm_entry;
190381dbf0b5SDai Ngo 				prev = cmp;
19047c478bd9Sstevel@tonic-gate 			}
19057c478bd9Sstevel@tonic-gate 			cmp = &cm_entry->x_next;
19067c478bd9Sstevel@tonic-gate 		}
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 		if (i > clnt_max_conns) {
19097c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_get: too many conns, dooming entry"
19107c478bd9Sstevel@tonic-gate 			    " %p\n", (void *)lru_entry->x_tiptr);
19117c478bd9Sstevel@tonic-gate 			lru_entry->x_doomed = TRUE;
19127c478bd9Sstevel@tonic-gate 			goto use_new_conn;
19137c478bd9Sstevel@tonic-gate 		}
19147c478bd9Sstevel@tonic-gate 
19157c478bd9Sstevel@tonic-gate 		/*
19167c478bd9Sstevel@tonic-gate 		 * If we are at the maximum number of connections to
19177c478bd9Sstevel@tonic-gate 		 * the server, hand back the least recently used one.
19187c478bd9Sstevel@tonic-gate 		 */
19197c478bd9Sstevel@tonic-gate 		if (i == clnt_max_conns) {
19207c478bd9Sstevel@tonic-gate 			/*
19217c478bd9Sstevel@tonic-gate 			 * Copy into the handle the source address of
19227c478bd9Sstevel@tonic-gate 			 * the connection, which we will use in case of
19237c478bd9Sstevel@tonic-gate 			 * a later retry.
19247c478bd9Sstevel@tonic-gate 			 */
19257c478bd9Sstevel@tonic-gate 			if (srcaddr->len != lru_entry->x_src.len) {
19267c478bd9Sstevel@tonic-gate 				if (srcaddr->len > 0)
19277c478bd9Sstevel@tonic-gate 					kmem_free(srcaddr->buf,
19287c478bd9Sstevel@tonic-gate 					    srcaddr->maxlen);
19297c478bd9Sstevel@tonic-gate 				srcaddr->buf = kmem_zalloc(
19307c478bd9Sstevel@tonic-gate 				    lru_entry->x_src.len, KM_SLEEP);
19317c478bd9Sstevel@tonic-gate 				srcaddr->maxlen = srcaddr->len =
19327c478bd9Sstevel@tonic-gate 				    lru_entry->x_src.len;
19337c478bd9Sstevel@tonic-gate 			}
19347c478bd9Sstevel@tonic-gate 			bcopy(lru_entry->x_src.buf, srcaddr->buf, srcaddr->len);
19357c478bd9Sstevel@tonic-gate 			RPCLOG(2, "connmgr_get: call going out on %p\n",
19367c478bd9Sstevel@tonic-gate 			    (void *)lru_entry);
1937d3d50737SRafael Vanoni 			lru_entry->x_time = ddi_get_lbolt();
19387c478bd9Sstevel@tonic-gate 			CONN_HOLD(lru_entry);
193981dbf0b5SDai Ngo 
194081dbf0b5SDai Ngo 			if ((i > 1) && (prev != &cm_hd)) {
194181dbf0b5SDai Ngo 				/*
194281dbf0b5SDai Ngo 				 * remove and re-insert entry at head of list.
194381dbf0b5SDai Ngo 				 */
194481dbf0b5SDai Ngo 				*prev = lru_entry->x_next;
194581dbf0b5SDai Ngo 				lru_entry->x_next = cm_hd;
194681dbf0b5SDai Ngo 				cm_hd = lru_entry;
194781dbf0b5SDai Ngo 			}
194881dbf0b5SDai Ngo 
19497c478bd9Sstevel@tonic-gate 			mutex_exit(&connmgr_lock);
19507c478bd9Sstevel@tonic-gate 			return (lru_entry);
19517c478bd9Sstevel@tonic-gate 		}
19527c478bd9Sstevel@tonic-gate 
19537c478bd9Sstevel@tonic-gate 	} else {
19547c478bd9Sstevel@tonic-gate 		/*
19557c478bd9Sstevel@tonic-gate 		 * This is the retry case (retryaddr != NULL).  Retries must
19567c478bd9Sstevel@tonic-gate 		 * be sent on the same source port as the original call.
19577c478bd9Sstevel@tonic-gate 		 */
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate 		/*
19607c478bd9Sstevel@tonic-gate 		 * Walk the list looking for a connection with a source address
19617c478bd9Sstevel@tonic-gate 		 * that matches the retry address.
19627c478bd9Sstevel@tonic-gate 		 */
19633807480aSGerald Thornbrugh start_retry_loop:
19647c478bd9Sstevel@tonic-gate 		cmp = &cm_hd;
19657c478bd9Sstevel@tonic-gate 		while ((cm_entry = *cmp) != NULL) {
19667c478bd9Sstevel@tonic-gate 			ASSERT(cm_entry != cm_entry->x_next);
19673807480aSGerald Thornbrugh 
19683807480aSGerald Thornbrugh 			/*
19693807480aSGerald Thornbrugh 			 * determine if this connection matches the passed
19703807480aSGerald Thornbrugh 			 * in retry address.  If it does not match, advance
19713807480aSGerald Thornbrugh 			 * to the next element on the list.
19723807480aSGerald Thornbrugh 			 */
19737c478bd9Sstevel@tonic-gate 			if (zoneid != cm_entry->x_zoneid ||
19747c478bd9Sstevel@tonic-gate 			    device != cm_entry->x_rdev ||
19757c478bd9Sstevel@tonic-gate 			    retryaddr->len != cm_entry->x_src.len ||
19767c478bd9Sstevel@tonic-gate 			    bcmp(retryaddr->buf, cm_entry->x_src.buf,
19777c478bd9Sstevel@tonic-gate 			    retryaddr->len) != 0) {
19787c478bd9Sstevel@tonic-gate 				cmp = &cm_entry->x_next;
19797c478bd9Sstevel@tonic-gate 				continue;
19807c478bd9Sstevel@tonic-gate 			}
19813807480aSGerald Thornbrugh 			/*
19823807480aSGerald Thornbrugh 			 * Garbage collect conections that are marked
19833807480aSGerald Thornbrugh 			 * for needs disconnect.
19843807480aSGerald Thornbrugh 			 */
19853807480aSGerald Thornbrugh 			if (cm_entry->x_needdis) {
19863807480aSGerald Thornbrugh 				CONN_HOLD(cm_entry);
19873807480aSGerald Thornbrugh 				connmgr_dis_and_wait(cm_entry);
19883807480aSGerald Thornbrugh 				connmgr_release(cm_entry);
19893807480aSGerald Thornbrugh 				/*
19903807480aSGerald Thornbrugh 				 * connmgr_lock could have been
19913807480aSGerald Thornbrugh 				 * dropped for the disconnect
19923807480aSGerald Thornbrugh 				 * processing so start over.
19933807480aSGerald Thornbrugh 				 */
19943807480aSGerald Thornbrugh 				goto start_retry_loop;
19953807480aSGerald Thornbrugh 			}
19963807480aSGerald Thornbrugh 			/*
19973807480aSGerald Thornbrugh 			 * Garbage collect the dead connections that have
19983807480aSGerald Thornbrugh 			 * no threads working on them.
19993807480aSGerald Thornbrugh 			 */
20003807480aSGerald Thornbrugh 			if ((cm_entry->x_state_flags & (X_DEAD|X_THREAD)) ==
20013807480aSGerald Thornbrugh 			    X_DEAD) {
20023807480aSGerald Thornbrugh 				mutex_enter(&cm_entry->x_lock);
20033807480aSGerald Thornbrugh 				if (cm_entry->x_ref != 0) {
20043807480aSGerald Thornbrugh 					/*
20053807480aSGerald Thornbrugh 					 * Currently in use.
20063807480aSGerald Thornbrugh 					 * Cleanup later.
20073807480aSGerald Thornbrugh 					 */
20083807480aSGerald Thornbrugh 					cmp = &cm_entry->x_next;
20093807480aSGerald Thornbrugh 					mutex_exit(&cm_entry->x_lock);
20103807480aSGerald Thornbrugh 					continue;
20113807480aSGerald Thornbrugh 				}
20123807480aSGerald Thornbrugh 				mutex_exit(&cm_entry->x_lock);
20133807480aSGerald Thornbrugh 				*cmp = cm_entry->x_next;
20143807480aSGerald Thornbrugh 				mutex_exit(&connmgr_lock);
20153807480aSGerald Thornbrugh 				connmgr_close(cm_entry);
20163807480aSGerald Thornbrugh 				mutex_enter(&connmgr_lock);
20173807480aSGerald Thornbrugh 				goto start_retry_loop;
20183807480aSGerald Thornbrugh 			}
20197c478bd9Sstevel@tonic-gate 
20207c478bd9Sstevel@tonic-gate 			/*
20217c478bd9Sstevel@tonic-gate 			 * Sanity check: if the connection with our source
20227c478bd9Sstevel@tonic-gate 			 * port is going to some other server, something went
20237c478bd9Sstevel@tonic-gate 			 * wrong, as we never delete connections (i.e. release
20247c478bd9Sstevel@tonic-gate 			 * ports) unless they have been idle.  In this case,
20257c478bd9Sstevel@tonic-gate 			 * it is probably better to send the call out using
20267c478bd9Sstevel@tonic-gate 			 * a new source address than to fail it altogether,
20277c478bd9Sstevel@tonic-gate 			 * since that port may never be released.
20287c478bd9Sstevel@tonic-gate 			 */
20297c478bd9Sstevel@tonic-gate 			if (destaddr->len != cm_entry->x_server.len ||
20307c478bd9Sstevel@tonic-gate 			    bcmp(destaddr->buf, cm_entry->x_server.buf,
20317c478bd9Sstevel@tonic-gate 			    destaddr->len) != 0) {
20327c478bd9Sstevel@tonic-gate 				RPCLOG(1, "connmgr_get: tiptr %p"
20337c478bd9Sstevel@tonic-gate 				    " is going to a different server"
20347c478bd9Sstevel@tonic-gate 				    " with the port that belongs"
20357c478bd9Sstevel@tonic-gate 				    " to us!\n", (void *)cm_entry->x_tiptr);
20367c478bd9Sstevel@tonic-gate 				retryaddr = NULL;
20377c478bd9Sstevel@tonic-gate 				goto use_new_conn;
20387c478bd9Sstevel@tonic-gate 			}
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 			/*
20417c478bd9Sstevel@tonic-gate 			 * If the connection of interest is not connected and we
20427c478bd9Sstevel@tonic-gate 			 * can't reconnect it, then the server is probably
20437c478bd9Sstevel@tonic-gate 			 * still down.  Return NULL to the caller and let it
20447c478bd9Sstevel@tonic-gate 			 * retry later if it wants to.  We have a delay so the
20457c478bd9Sstevel@tonic-gate 			 * machine doesn't go into a tight retry loop.  If the
20467c478bd9Sstevel@tonic-gate 			 * entry was already connected, or the reconnected was
20477c478bd9Sstevel@tonic-gate 			 * successful, return this entry.
20487c478bd9Sstevel@tonic-gate 			 */
20497c478bd9Sstevel@tonic-gate 			if (cm_entry->x_connected == FALSE) {
20507c478bd9Sstevel@tonic-gate 				return (connmgr_wrapconnect(cm_entry,
20517c478bd9Sstevel@tonic-gate 				    waitp, destaddr, addrfmly, NULL,
2052de8c4a14SErik Nordmark 				    rpcerr, TRUE, nosignal, cr));
20537c478bd9Sstevel@tonic-gate 			} else {
20547c478bd9Sstevel@tonic-gate 				CONN_HOLD(cm_entry);
20557c478bd9Sstevel@tonic-gate 
2056d3d50737SRafael Vanoni 				cm_entry->x_time = ddi_get_lbolt();
20577c478bd9Sstevel@tonic-gate 				mutex_exit(&connmgr_lock);
20587c478bd9Sstevel@tonic-gate 				RPCLOG(2, "connmgr_get: found old "
20597c478bd9Sstevel@tonic-gate 				    "transport %p for retry\n",
20607c478bd9Sstevel@tonic-gate 				    (void *)cm_entry);
20617c478bd9Sstevel@tonic-gate 				return (cm_entry);
20627c478bd9Sstevel@tonic-gate 			}
20637c478bd9Sstevel@tonic-gate 		}
20647c478bd9Sstevel@tonic-gate 
20657c478bd9Sstevel@tonic-gate 		/*
20667c478bd9Sstevel@tonic-gate 		 * We cannot find an entry in the list for this retry.
20677c478bd9Sstevel@tonic-gate 		 * Either the entry has been removed temporarily to be
20687c478bd9Sstevel@tonic-gate 		 * reconnected by another thread, or the original call
20697c478bd9Sstevel@tonic-gate 		 * got a port but never got connected,
20707c478bd9Sstevel@tonic-gate 		 * and hence the transport never got put in the
20717c478bd9Sstevel@tonic-gate 		 * list.  Fall through to the "create new connection" code -
20727c478bd9Sstevel@tonic-gate 		 * the former case will fail there trying to rebind the port,
20737c478bd9Sstevel@tonic-gate 		 * and the later case (and any other pathological cases) will
20747c478bd9Sstevel@tonic-gate 		 * rebind and reconnect and not hang the client machine.
20757c478bd9Sstevel@tonic-gate 		 */
20767c478bd9Sstevel@tonic-gate 		RPCLOG0(8, "connmgr_get: no entry in list for retry\n");
20777c478bd9Sstevel@tonic-gate 	}
20787c478bd9Sstevel@tonic-gate 	/*
20797c478bd9Sstevel@tonic-gate 	 * Set up a transport entry in the connection manager's list.
20807c478bd9Sstevel@tonic-gate 	 */
20817c478bd9Sstevel@tonic-gate 	cm_entry = (struct cm_xprt *)
20827c478bd9Sstevel@tonic-gate 	    kmem_zalloc(sizeof (struct cm_xprt), KM_SLEEP);
20837c478bd9Sstevel@tonic-gate 
20847c478bd9Sstevel@tonic-gate 	cm_entry->x_server.buf = kmem_zalloc(destaddr->len, KM_SLEEP);
20857c478bd9Sstevel@tonic-gate 	bcopy(destaddr->buf, cm_entry->x_server.buf, destaddr->len);
20867c478bd9Sstevel@tonic-gate 	cm_entry->x_server.len = cm_entry->x_server.maxlen = destaddr->len;
20877c478bd9Sstevel@tonic-gate 
20887c478bd9Sstevel@tonic-gate 	cm_entry->x_state_flags = X_THREAD;
20897c478bd9Sstevel@tonic-gate 	cm_entry->x_ref = 1;
20907c478bd9Sstevel@tonic-gate 	cm_entry->x_family = addrfmly;
20917c478bd9Sstevel@tonic-gate 	cm_entry->x_rdev = device;
20927c478bd9Sstevel@tonic-gate 	cm_entry->x_zoneid = zoneid;
20937c478bd9Sstevel@tonic-gate 	mutex_init(&cm_entry->x_lock, NULL, MUTEX_DEFAULT, NULL);
20947c478bd9Sstevel@tonic-gate 	cv_init(&cm_entry->x_cv, NULL, CV_DEFAULT, NULL);
20957c478bd9Sstevel@tonic-gate 	cv_init(&cm_entry->x_conn_cv, NULL, CV_DEFAULT, NULL);
20967c478bd9Sstevel@tonic-gate 	cv_init(&cm_entry->x_dis_cv, NULL, CV_DEFAULT, NULL);
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate 	/*
20997c478bd9Sstevel@tonic-gate 	 * Note that we add this partially initialized entry to the
21007c478bd9Sstevel@tonic-gate 	 * connection list. This is so that we don't have connections to
21017c478bd9Sstevel@tonic-gate 	 * the same server.
21027c478bd9Sstevel@tonic-gate 	 *
21037c478bd9Sstevel@tonic-gate 	 * Note that x_src is not initialized at this point. This is because
21047c478bd9Sstevel@tonic-gate 	 * retryaddr might be NULL in which case x_src is whatever
21057c478bd9Sstevel@tonic-gate 	 * t_kbind/bindresvport gives us. If another thread wants a
21067c478bd9Sstevel@tonic-gate 	 * connection to the same server, seemingly we have an issue, but we
21077c478bd9Sstevel@tonic-gate 	 * don't. If the other thread comes in with retryaddr == NULL, then it
21087c478bd9Sstevel@tonic-gate 	 * will never look at x_src, and it will end up waiting in
21097c478bd9Sstevel@tonic-gate 	 * connmgr_cwait() for the first thread to finish the connection
21107c478bd9Sstevel@tonic-gate 	 * attempt. If the other thread comes in with retryaddr != NULL, then
21117c478bd9Sstevel@tonic-gate 	 * that means there was a request sent on a connection, in which case
21127c478bd9Sstevel@tonic-gate 	 * the the connection should already exist. Thus the first thread
21137c478bd9Sstevel@tonic-gate 	 * never gets here ... it finds the connection it its server in the
21147c478bd9Sstevel@tonic-gate 	 * connection list.
21157c478bd9Sstevel@tonic-gate 	 *
21167c478bd9Sstevel@tonic-gate 	 * But even if theory is wrong, in the retryaddr != NULL case, the 2nd
21177c478bd9Sstevel@tonic-gate 	 * thread will skip us because x_src.len == 0.
21187c478bd9Sstevel@tonic-gate 	 */
21197c478bd9Sstevel@tonic-gate 	cm_entry->x_next = cm_hd;
21207c478bd9Sstevel@tonic-gate 	cm_hd = cm_entry;
21217c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
21227c478bd9Sstevel@tonic-gate 
21237c478bd9Sstevel@tonic-gate 	/*
21247c478bd9Sstevel@tonic-gate 	 * Either we didn't find an entry to the server of interest, or we
21257c478bd9Sstevel@tonic-gate 	 * don't have the maximum number of connections to that server -
21267c478bd9Sstevel@tonic-gate 	 * create a new connection.
21277c478bd9Sstevel@tonic-gate 	 */
21287c478bd9Sstevel@tonic-gate 	RPCLOG0(8, "connmgr_get: creating new connection\n");
21297c478bd9Sstevel@tonic-gate 	rpcerr->re_status = RPC_TLIERROR;
21307c478bd9Sstevel@tonic-gate 
213145916cd2Sjpk 	i = t_kopen(NULL, device, FREAD|FWRITE|FNDELAY, &tiptr, zone_kcred());
21327c478bd9Sstevel@tonic-gate 	if (i) {
21337c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't open cots device, error %d\n", i);
21347c478bd9Sstevel@tonic-gate 		rpcerr->re_errno = i;
21357c478bd9Sstevel@tonic-gate 		connmgr_cancelconn(cm_entry);
21367c478bd9Sstevel@tonic-gate 		return (NULL);
21377c478bd9Sstevel@tonic-gate 	}
21387c478bd9Sstevel@tonic-gate 	rpc_poptimod(tiptr->fp->f_vnode);
21397c478bd9Sstevel@tonic-gate 
21407c478bd9Sstevel@tonic-gate 	if (i = strioctl(tiptr->fp->f_vnode, I_PUSH, (intptr_t)"rpcmod", 0,
21417c478bd9Sstevel@tonic-gate 	    K_TO_K, kcred, &retval)) {
21427c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't push cots module, %d\n", i);
21437c478bd9Sstevel@tonic-gate 		(void) t_kclose(tiptr, 1);
21447c478bd9Sstevel@tonic-gate 		rpcerr->re_errno = i;
21457c478bd9Sstevel@tonic-gate 		connmgr_cancelconn(cm_entry);
21467c478bd9Sstevel@tonic-gate 		return (NULL);
21477c478bd9Sstevel@tonic-gate 	}
21487c478bd9Sstevel@tonic-gate 
21497c478bd9Sstevel@tonic-gate 	if (i = strioctl(tiptr->fp->f_vnode, RPC_CLIENT, 0, 0, K_TO_K,
21507c478bd9Sstevel@tonic-gate 	    kcred, &retval)) {
21517c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't set client status with cots "
21527c478bd9Sstevel@tonic-gate 		    "module, %d\n", i);
21537c478bd9Sstevel@tonic-gate 		(void) t_kclose(tiptr, 1);
21547c478bd9Sstevel@tonic-gate 		rpcerr->re_errno = i;
21557c478bd9Sstevel@tonic-gate 		connmgr_cancelconn(cm_entry);
21567c478bd9Sstevel@tonic-gate 		return (NULL);
21577c478bd9Sstevel@tonic-gate 	}
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
21607c478bd9Sstevel@tonic-gate 
21617c478bd9Sstevel@tonic-gate 	wq = tiptr->fp->f_vnode->v_stream->sd_wrq->q_next;
21627c478bd9Sstevel@tonic-gate 	cm_entry->x_wq = wq;
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
21657c478bd9Sstevel@tonic-gate 
21667c478bd9Sstevel@tonic-gate 	if (i = strioctl(tiptr->fp->f_vnode, I_PUSH, (intptr_t)"timod", 0,
21677c478bd9Sstevel@tonic-gate 	    K_TO_K, kcred, &retval)) {
21687c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't push timod, %d\n", i);
21697c478bd9Sstevel@tonic-gate 		(void) t_kclose(tiptr, 1);
21707c478bd9Sstevel@tonic-gate 		rpcerr->re_errno = i;
21717c478bd9Sstevel@tonic-gate 		connmgr_cancelconn(cm_entry);
21727c478bd9Sstevel@tonic-gate 		return (NULL);
21737c478bd9Sstevel@tonic-gate 	}
21747c478bd9Sstevel@tonic-gate 
21757c478bd9Sstevel@tonic-gate 	/*
21767c478bd9Sstevel@tonic-gate 	 * If the caller has not specified reserved port usage then
21777c478bd9Sstevel@tonic-gate 	 * take the system default.
21787c478bd9Sstevel@tonic-gate 	 */
21797c478bd9Sstevel@tonic-gate 	if (useresvport == -1)
21807c478bd9Sstevel@tonic-gate 		useresvport = clnt_cots_do_bindresvport;
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate 	if ((useresvport || retryaddr != NULL) &&
21837c478bd9Sstevel@tonic-gate 	    (addrfmly == AF_INET || addrfmly == AF_INET6)) {
21847c478bd9Sstevel@tonic-gate 		bool_t alloc_src = FALSE;
21857c478bd9Sstevel@tonic-gate 
21867c478bd9Sstevel@tonic-gate 		if (srcaddr->len != destaddr->len) {
21877c478bd9Sstevel@tonic-gate 			kmem_free(srcaddr->buf, srcaddr->maxlen);
21887c478bd9Sstevel@tonic-gate 			srcaddr->buf = kmem_zalloc(destaddr->len, KM_SLEEP);
21897c478bd9Sstevel@tonic-gate 			srcaddr->maxlen = destaddr->len;
21907c478bd9Sstevel@tonic-gate 			srcaddr->len = destaddr->len;
21917c478bd9Sstevel@tonic-gate 			alloc_src = TRUE;
21927c478bd9Sstevel@tonic-gate 		}
21937c478bd9Sstevel@tonic-gate 
21947c478bd9Sstevel@tonic-gate 		if ((i = bindresvport(tiptr, retryaddr, srcaddr, TRUE)) != 0) {
21957c478bd9Sstevel@tonic-gate 			(void) t_kclose(tiptr, 1);
21967c478bd9Sstevel@tonic-gate 			RPCLOG(1, "connmgr_get: couldn't bind, retryaddr: "
21977c478bd9Sstevel@tonic-gate 			    "%p\n", (void *)retryaddr);
21987c478bd9Sstevel@tonic-gate 
21997c478bd9Sstevel@tonic-gate 			/*
22007c478bd9Sstevel@tonic-gate 			 * 1225408: If we allocated a source address, then it
22017c478bd9Sstevel@tonic-gate 			 * is either garbage or all zeroes. In that case
22027c478bd9Sstevel@tonic-gate 			 * we need to clear srcaddr.
22037c478bd9Sstevel@tonic-gate 			 */
22047c478bd9Sstevel@tonic-gate 			if (alloc_src == TRUE) {
22057c478bd9Sstevel@tonic-gate 				kmem_free(srcaddr->buf, srcaddr->maxlen);
22067c478bd9Sstevel@tonic-gate 				srcaddr->maxlen = srcaddr->len = 0;
22077c478bd9Sstevel@tonic-gate 				srcaddr->buf = NULL;
22087c478bd9Sstevel@tonic-gate 			}
22097c478bd9Sstevel@tonic-gate 			rpcerr->re_errno = i;
22107c478bd9Sstevel@tonic-gate 			connmgr_cancelconn(cm_entry);
22117c478bd9Sstevel@tonic-gate 			return (NULL);
22127c478bd9Sstevel@tonic-gate 		}
22137c478bd9Sstevel@tonic-gate 	} else {
22147c478bd9Sstevel@tonic-gate 		if ((i = t_kbind(tiptr, NULL, NULL)) != 0) {
22157c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcreate: t_kbind: %d\n", i);
22167c478bd9Sstevel@tonic-gate 			(void) t_kclose(tiptr, 1);
22177c478bd9Sstevel@tonic-gate 			rpcerr->re_errno = i;
22187c478bd9Sstevel@tonic-gate 			connmgr_cancelconn(cm_entry);
22197c478bd9Sstevel@tonic-gate 			return (NULL);
22207c478bd9Sstevel@tonic-gate 		}
22217c478bd9Sstevel@tonic-gate 	}
22227c478bd9Sstevel@tonic-gate 
22237c478bd9Sstevel@tonic-gate 	{
22247c478bd9Sstevel@tonic-gate 		/*
22257c478bd9Sstevel@tonic-gate 		 * Keep the kernel stack lean. Don't move this call
22267c478bd9Sstevel@tonic-gate 		 * declaration to the top of this function because a
22277c478bd9Sstevel@tonic-gate 		 * call is declared in connmgr_wrapconnect()
22287c478bd9Sstevel@tonic-gate 		 */
22297c478bd9Sstevel@tonic-gate 		calllist_t call;
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 		bzero(&call, sizeof (call));
22327c478bd9Sstevel@tonic-gate 		cv_init(&call.call_cv, NULL, CV_DEFAULT, NULL);
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 		/*
22357c478bd9Sstevel@tonic-gate 		 * This is a bound end-point so don't close it's stream.
22367c478bd9Sstevel@tonic-gate 		 */
22377c478bd9Sstevel@tonic-gate 		connected = connmgr_connect(cm_entry, wq, destaddr, addrfmly,
2238de8c4a14SErik Nordmark 		    &call, &tidu_size, FALSE, waitp, nosignal, cr);
22397c478bd9Sstevel@tonic-gate 		*rpcerr = call.call_err;
22407c478bd9Sstevel@tonic-gate 		cv_destroy(&call.call_cv);
22417c478bd9Sstevel@tonic-gate 
22427c478bd9Sstevel@tonic-gate 	}
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
22457c478bd9Sstevel@tonic-gate 
22467c478bd9Sstevel@tonic-gate 	/*
22477c478bd9Sstevel@tonic-gate 	 * Set up a transport entry in the connection manager's list.
22487c478bd9Sstevel@tonic-gate 	 */
22497c478bd9Sstevel@tonic-gate 	cm_entry->x_src.buf = kmem_zalloc(srcaddr->len, KM_SLEEP);
22507c478bd9Sstevel@tonic-gate 	bcopy(srcaddr->buf, cm_entry->x_src.buf, srcaddr->len);
22517c478bd9Sstevel@tonic-gate 	cm_entry->x_src.len = cm_entry->x_src.maxlen = srcaddr->len;
22527c478bd9Sstevel@tonic-gate 
22537c478bd9Sstevel@tonic-gate 	cm_entry->x_tiptr = tiptr;
2254d3d50737SRafael Vanoni 	cm_entry->x_time = ddi_get_lbolt();
22557c478bd9Sstevel@tonic-gate 
22567c478bd9Sstevel@tonic-gate 	if (tiptr->tp_info.servtype == T_COTS_ORD)
22577c478bd9Sstevel@tonic-gate 		cm_entry->x_ordrel = TRUE;
22587c478bd9Sstevel@tonic-gate 	else
22597c478bd9Sstevel@tonic-gate 		cm_entry->x_ordrel = FALSE;
22607c478bd9Sstevel@tonic-gate 
22617c478bd9Sstevel@tonic-gate 	cm_entry->x_tidu_size = tidu_size;
22627c478bd9Sstevel@tonic-gate 
2263af41c4bfSvv149972 	if (cm_entry->x_early_disc) {
2264af41c4bfSvv149972 		/*
2265af41c4bfSvv149972 		 * We need to check if a disconnect request has come
2266af41c4bfSvv149972 		 * while we are connected, if so, then we need to
2267af41c4bfSvv149972 		 * set rpcerr->re_status appropriately before returning
2268af41c4bfSvv149972 		 * NULL to caller.
2269af41c4bfSvv149972 		 */
2270af41c4bfSvv149972 		if (rpcerr->re_status == RPC_SUCCESS)
2271af41c4bfSvv149972 			rpcerr->re_status = RPC_XPRTFAILED;
22727c478bd9Sstevel@tonic-gate 		cm_entry->x_connected = FALSE;
2273af41c4bfSvv149972 	} else
22747c478bd9Sstevel@tonic-gate 		cm_entry->x_connected = connected;
22757c478bd9Sstevel@tonic-gate 
22767c478bd9Sstevel@tonic-gate 	/*
22777c478bd9Sstevel@tonic-gate 	 * There could be a discrepancy here such that
22787c478bd9Sstevel@tonic-gate 	 * x_early_disc is TRUE yet connected is TRUE as well
22797c478bd9Sstevel@tonic-gate 	 * and the connection is actually connected. In that case
22807c478bd9Sstevel@tonic-gate 	 * lets be conservative and declare the connection as not
22817c478bd9Sstevel@tonic-gate 	 * connected.
22827c478bd9Sstevel@tonic-gate 	 */
22837c478bd9Sstevel@tonic-gate 	cm_entry->x_early_disc = FALSE;
22847c478bd9Sstevel@tonic-gate 	cm_entry->x_needdis = (cm_entry->x_connected == FALSE);
2285d3d50737SRafael Vanoni 	cm_entry->x_ctime = ddi_get_lbolt();
22867c478bd9Sstevel@tonic-gate 
22877c478bd9Sstevel@tonic-gate 	/*
22887c478bd9Sstevel@tonic-gate 	 * Notify any threads waiting that the connection attempt is done.
22897c478bd9Sstevel@tonic-gate 	 */
22907c478bd9Sstevel@tonic-gate 	cm_entry->x_thread = FALSE;
22917c478bd9Sstevel@tonic-gate 	cv_broadcast(&cm_entry->x_conn_cv);
22927c478bd9Sstevel@tonic-gate 
22937c478bd9Sstevel@tonic-gate 	if (cm_entry->x_connected == FALSE) {
2294af41c4bfSvv149972 		mutex_exit(&connmgr_lock);
22957c478bd9Sstevel@tonic-gate 		connmgr_release(cm_entry);
22967c478bd9Sstevel@tonic-gate 		return (NULL);
22977c478bd9Sstevel@tonic-gate 	}
2298af41c4bfSvv149972 
2299af41c4bfSvv149972 	mutex_exit(&connmgr_lock);
2300af41c4bfSvv149972 
23017c478bd9Sstevel@tonic-gate 	return (cm_entry);
23027c478bd9Sstevel@tonic-gate }
23037c478bd9Sstevel@tonic-gate 
23047c478bd9Sstevel@tonic-gate /*
23057c478bd9Sstevel@tonic-gate  * Keep the cm_xprt entry on the connecton list when making a connection. This
23067c478bd9Sstevel@tonic-gate  * is to prevent multiple connections to a slow server from appearing.
23077c478bd9Sstevel@tonic-gate  * We use the bit field x_thread to tell if a thread is doing a connection
23087c478bd9Sstevel@tonic-gate  * which keeps other interested threads from messing with connection.
23097c478bd9Sstevel@tonic-gate  * Those other threads just wait if x_thread is set.
23107c478bd9Sstevel@tonic-gate  *
23117c478bd9Sstevel@tonic-gate  * If x_thread is not set, then we do the actual work of connecting via
23127c478bd9Sstevel@tonic-gate  * connmgr_connect().
23137c478bd9Sstevel@tonic-gate  *
23147c478bd9Sstevel@tonic-gate  * mutex convention: called with connmgr_lock held, returns with it released.
23157c478bd9Sstevel@tonic-gate  */
23167c478bd9Sstevel@tonic-gate static struct cm_xprt *
connmgr_wrapconnect(struct cm_xprt * cm_entry,const struct timeval * waitp,struct netbuf * destaddr,int addrfmly,struct netbuf * srcaddr,struct rpc_err * rpcerr,bool_t reconnect,bool_t nosignal,cred_t * cr)23177c478bd9Sstevel@tonic-gate connmgr_wrapconnect(
23187c478bd9Sstevel@tonic-gate 	struct cm_xprt	*cm_entry,
23197c478bd9Sstevel@tonic-gate 	const struct timeval	*waitp,
23207c478bd9Sstevel@tonic-gate 	struct netbuf	*destaddr,
23217c478bd9Sstevel@tonic-gate 	int		addrfmly,
23227c478bd9Sstevel@tonic-gate 	struct netbuf	*srcaddr,
23237c478bd9Sstevel@tonic-gate 	struct rpc_err	*rpcerr,
23247c478bd9Sstevel@tonic-gate 	bool_t		reconnect,
2325de8c4a14SErik Nordmark 	bool_t		nosignal,
2326de8c4a14SErik Nordmark 	cred_t		*cr)
23277c478bd9Sstevel@tonic-gate {
23287c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
23297c478bd9Sstevel@tonic-gate 	/*
23307c478bd9Sstevel@tonic-gate 	 * Hold this entry as we are about to drop connmgr_lock.
23317c478bd9Sstevel@tonic-gate 	 */
23327c478bd9Sstevel@tonic-gate 	CONN_HOLD(cm_entry);
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate 	/*
23357c478bd9Sstevel@tonic-gate 	 * If there is a thread already making a connection for us, then
23367c478bd9Sstevel@tonic-gate 	 * wait for it to complete the connection.
23377c478bd9Sstevel@tonic-gate 	 */
23387c478bd9Sstevel@tonic-gate 	if (cm_entry->x_thread == TRUE) {
23397c478bd9Sstevel@tonic-gate 		rpcerr->re_status = connmgr_cwait(cm_entry, waitp, nosignal);
23407c478bd9Sstevel@tonic-gate 
23417c478bd9Sstevel@tonic-gate 		if (rpcerr->re_status != RPC_SUCCESS) {
23427c478bd9Sstevel@tonic-gate 			mutex_exit(&connmgr_lock);
23437c478bd9Sstevel@tonic-gate 			connmgr_release(cm_entry);
23447c478bd9Sstevel@tonic-gate 			return (NULL);
23457c478bd9Sstevel@tonic-gate 		}
23467c478bd9Sstevel@tonic-gate 	} else {
23477c478bd9Sstevel@tonic-gate 		bool_t connected;
23487c478bd9Sstevel@tonic-gate 		calllist_t call;
23497c478bd9Sstevel@tonic-gate 
23507c478bd9Sstevel@tonic-gate 		cm_entry->x_thread = TRUE;
23517c478bd9Sstevel@tonic-gate 
23527c478bd9Sstevel@tonic-gate 		while (cm_entry->x_needrel == TRUE) {
23537c478bd9Sstevel@tonic-gate 			cm_entry->x_needrel = FALSE;
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate 			connmgr_sndrel(cm_entry);
23567c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(1000000));
23577c478bd9Sstevel@tonic-gate 
23587c478bd9Sstevel@tonic-gate 			mutex_enter(&connmgr_lock);
23597c478bd9Sstevel@tonic-gate 		}
23607c478bd9Sstevel@tonic-gate 
23617c478bd9Sstevel@tonic-gate 		/*
23627c478bd9Sstevel@tonic-gate 		 * If we need to send a T_DISCON_REQ, send one.
23637c478bd9Sstevel@tonic-gate 		 */
23647c478bd9Sstevel@tonic-gate 		connmgr_dis_and_wait(cm_entry);
23657c478bd9Sstevel@tonic-gate 
23667c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 		bzero(&call, sizeof (call));
23697c478bd9Sstevel@tonic-gate 		cv_init(&call.call_cv, NULL, CV_DEFAULT, NULL);
23707c478bd9Sstevel@tonic-gate 
23717c478bd9Sstevel@tonic-gate 		connected = connmgr_connect(cm_entry, cm_entry->x_wq,
23728ffff9fdSgt29601 		    destaddr, addrfmly, &call, &cm_entry->x_tidu_size,
2373de8c4a14SErik Nordmark 		    reconnect, waitp, nosignal, cr);
23747c478bd9Sstevel@tonic-gate 
23757c478bd9Sstevel@tonic-gate 		*rpcerr = call.call_err;
23767c478bd9Sstevel@tonic-gate 		cv_destroy(&call.call_cv);
23777c478bd9Sstevel@tonic-gate 
23787c478bd9Sstevel@tonic-gate 		mutex_enter(&connmgr_lock);
23797c478bd9Sstevel@tonic-gate 
23807c478bd9Sstevel@tonic-gate 
2381af41c4bfSvv149972 		if (cm_entry->x_early_disc) {
2382af41c4bfSvv149972 			/*
2383af41c4bfSvv149972 			 * We need to check if a disconnect request has come
2384af41c4bfSvv149972 			 * while we are connected, if so, then we need to
2385af41c4bfSvv149972 			 * set rpcerr->re_status appropriately before returning
2386af41c4bfSvv149972 			 * NULL to caller.
2387af41c4bfSvv149972 			 */
2388af41c4bfSvv149972 			if (rpcerr->re_status == RPC_SUCCESS)
2389af41c4bfSvv149972 				rpcerr->re_status = RPC_XPRTFAILED;
23907c478bd9Sstevel@tonic-gate 			cm_entry->x_connected = FALSE;
2391af41c4bfSvv149972 		} else
23927c478bd9Sstevel@tonic-gate 			cm_entry->x_connected = connected;
23937c478bd9Sstevel@tonic-gate 
23947c478bd9Sstevel@tonic-gate 		/*
23957c478bd9Sstevel@tonic-gate 		 * There could be a discrepancy here such that
23967c478bd9Sstevel@tonic-gate 		 * x_early_disc is TRUE yet connected is TRUE as well
23977c478bd9Sstevel@tonic-gate 		 * and the connection is actually connected. In that case
23987c478bd9Sstevel@tonic-gate 		 * lets be conservative and declare the connection as not
23997c478bd9Sstevel@tonic-gate 		 * connected.
24007c478bd9Sstevel@tonic-gate 		 */
24017c478bd9Sstevel@tonic-gate 
24027c478bd9Sstevel@tonic-gate 		cm_entry->x_early_disc = FALSE;
24037c478bd9Sstevel@tonic-gate 		cm_entry->x_needdis = (cm_entry->x_connected == FALSE);
24047c478bd9Sstevel@tonic-gate 
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate 		/*
24077c478bd9Sstevel@tonic-gate 		 * connmgr_connect() may have given up before the connection
24087c478bd9Sstevel@tonic-gate 		 * actually timed out. So ensure that before the next
24097c478bd9Sstevel@tonic-gate 		 * connection attempt we do a disconnect.
24107c478bd9Sstevel@tonic-gate 		 */
2411d3d50737SRafael Vanoni 		cm_entry->x_ctime = ddi_get_lbolt();
24127c478bd9Sstevel@tonic-gate 		cm_entry->x_thread = FALSE;
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 		cv_broadcast(&cm_entry->x_conn_cv);
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate 		if (cm_entry->x_connected == FALSE) {
24177c478bd9Sstevel@tonic-gate 			mutex_exit(&connmgr_lock);
24187c478bd9Sstevel@tonic-gate 			connmgr_release(cm_entry);
24197c478bd9Sstevel@tonic-gate 			return (NULL);
24207c478bd9Sstevel@tonic-gate 		}
24217c478bd9Sstevel@tonic-gate 	}
24227c478bd9Sstevel@tonic-gate 
24237c478bd9Sstevel@tonic-gate 	if (srcaddr != NULL) {
24247c478bd9Sstevel@tonic-gate 		/*
24257c478bd9Sstevel@tonic-gate 		 * Copy into the handle the
24267c478bd9Sstevel@tonic-gate 		 * source address of the
24277c478bd9Sstevel@tonic-gate 		 * connection, which we will use
24287c478bd9Sstevel@tonic-gate 		 * in case of a later retry.
24297c478bd9Sstevel@tonic-gate 		 */
24307c478bd9Sstevel@tonic-gate 		if (srcaddr->len != cm_entry->x_src.len) {
24317c478bd9Sstevel@tonic-gate 			if (srcaddr->maxlen > 0)
24327c478bd9Sstevel@tonic-gate 				kmem_free(srcaddr->buf, srcaddr->maxlen);
24337c478bd9Sstevel@tonic-gate 			srcaddr->buf = kmem_zalloc(cm_entry->x_src.len,
24347c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
24357c478bd9Sstevel@tonic-gate 			srcaddr->maxlen = srcaddr->len =
24367c478bd9Sstevel@tonic-gate 			    cm_entry->x_src.len;
24377c478bd9Sstevel@tonic-gate 		}
24387c478bd9Sstevel@tonic-gate 		bcopy(cm_entry->x_src.buf, srcaddr->buf, srcaddr->len);
24397c478bd9Sstevel@tonic-gate 	}
2440d3d50737SRafael Vanoni 	cm_entry->x_time = ddi_get_lbolt();
24417c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
24427c478bd9Sstevel@tonic-gate 	return (cm_entry);
24437c478bd9Sstevel@tonic-gate }
24447c478bd9Sstevel@tonic-gate 
24457c478bd9Sstevel@tonic-gate /*
24467c478bd9Sstevel@tonic-gate  * If we need to send a T_DISCON_REQ, send one.
24477c478bd9Sstevel@tonic-gate  */
24487c478bd9Sstevel@tonic-gate static void
connmgr_dis_and_wait(struct cm_xprt * cm_entry)24497c478bd9Sstevel@tonic-gate connmgr_dis_and_wait(struct cm_xprt *cm_entry)
24507c478bd9Sstevel@tonic-gate {
24517c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
24527c478bd9Sstevel@tonic-gate 	for (;;) {
24537c478bd9Sstevel@tonic-gate 		while (cm_entry->x_needdis == TRUE) {
24547c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_dis_and_wait: need "
24557c478bd9Sstevel@tonic-gate 			    "T_DISCON_REQ for connection 0x%p\n",
24567c478bd9Sstevel@tonic-gate 			    (void *)cm_entry);
24577c478bd9Sstevel@tonic-gate 			cm_entry->x_needdis = FALSE;
24587c478bd9Sstevel@tonic-gate 			cm_entry->x_waitdis = TRUE;
24597c478bd9Sstevel@tonic-gate 
24607c478bd9Sstevel@tonic-gate 			connmgr_snddis(cm_entry);
24617c478bd9Sstevel@tonic-gate 
24627c478bd9Sstevel@tonic-gate 			mutex_enter(&connmgr_lock);
24637c478bd9Sstevel@tonic-gate 		}
24647c478bd9Sstevel@tonic-gate 
24657c478bd9Sstevel@tonic-gate 		if (cm_entry->x_waitdis == TRUE) {
24667c478bd9Sstevel@tonic-gate 			clock_t timout;
24677c478bd9Sstevel@tonic-gate 
24687c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_dis_and_wait waiting for "
24697c478bd9Sstevel@tonic-gate 			    "T_DISCON_REQ's ACK for connection %p\n",
24707c478bd9Sstevel@tonic-gate 			    (void *)cm_entry);
24717c478bd9Sstevel@tonic-gate 
2472d3d50737SRafael Vanoni 			timout = clnt_cots_min_conntout * drv_usectohz(1000000);
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 			/*
24757c478bd9Sstevel@tonic-gate 			 * The TPI spec says that the T_DISCON_REQ
24767c478bd9Sstevel@tonic-gate 			 * will get acknowledged, but in practice
24777c478bd9Sstevel@tonic-gate 			 * the ACK may never get sent. So don't
24787c478bd9Sstevel@tonic-gate 			 * block forever.
24797c478bd9Sstevel@tonic-gate 			 */
2480d3d50737SRafael Vanoni 			(void) cv_reltimedwait(&cm_entry->x_dis_cv,
2481d3d50737SRafael Vanoni 			    &connmgr_lock, timout, TR_CLOCK_TICK);
24827c478bd9Sstevel@tonic-gate 		}
24837c478bd9Sstevel@tonic-gate 		/*
24847c478bd9Sstevel@tonic-gate 		 * If we got the ACK, break. If we didn't,
24857c478bd9Sstevel@tonic-gate 		 * then send another T_DISCON_REQ.
24867c478bd9Sstevel@tonic-gate 		 */
24877c478bd9Sstevel@tonic-gate 		if (cm_entry->x_waitdis == FALSE) {
24887c478bd9Sstevel@tonic-gate 			break;
24897c478bd9Sstevel@tonic-gate 		} else {
24907c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_dis_and_wait: did"
24917c478bd9Sstevel@tonic-gate 			    "not get T_DISCON_REQ's ACK for "
24927c478bd9Sstevel@tonic-gate 			    "connection  %p\n", (void *)cm_entry);
24937c478bd9Sstevel@tonic-gate 			cm_entry->x_needdis = TRUE;
24947c478bd9Sstevel@tonic-gate 		}
24957c478bd9Sstevel@tonic-gate 	}
24967c478bd9Sstevel@tonic-gate }
24977c478bd9Sstevel@tonic-gate 
24987c478bd9Sstevel@tonic-gate static void
connmgr_cancelconn(struct cm_xprt * cm_entry)24997c478bd9Sstevel@tonic-gate connmgr_cancelconn(struct cm_xprt *cm_entry)
25007c478bd9Sstevel@tonic-gate {
25017c478bd9Sstevel@tonic-gate 	/*
25027c478bd9Sstevel@tonic-gate 	 * Mark the connection table entry as dead; the next thread that
25037c478bd9Sstevel@tonic-gate 	 * goes through connmgr_release() will notice this and deal with it.
25047c478bd9Sstevel@tonic-gate 	 */
25057c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
25067c478bd9Sstevel@tonic-gate 	cm_entry->x_dead = TRUE;
25077c478bd9Sstevel@tonic-gate 
25087c478bd9Sstevel@tonic-gate 	/*
25097c478bd9Sstevel@tonic-gate 	 * Notify any threads waiting for the connection that it isn't
25107c478bd9Sstevel@tonic-gate 	 * going to happen.
25117c478bd9Sstevel@tonic-gate 	 */
25127c478bd9Sstevel@tonic-gate 	cm_entry->x_thread = FALSE;
25137c478bd9Sstevel@tonic-gate 	cv_broadcast(&cm_entry->x_conn_cv);
25147c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
25157c478bd9Sstevel@tonic-gate 
25167c478bd9Sstevel@tonic-gate 	connmgr_release(cm_entry);
25177c478bd9Sstevel@tonic-gate }
25187c478bd9Sstevel@tonic-gate 
25197c478bd9Sstevel@tonic-gate static void
connmgr_close(struct cm_xprt * cm_entry)25207c478bd9Sstevel@tonic-gate connmgr_close(struct cm_xprt *cm_entry)
25217c478bd9Sstevel@tonic-gate {
25227c478bd9Sstevel@tonic-gate 	mutex_enter(&cm_entry->x_lock);
25237c478bd9Sstevel@tonic-gate 	while (cm_entry->x_ref != 0) {
25247c478bd9Sstevel@tonic-gate 		/*
25257c478bd9Sstevel@tonic-gate 		 * Must be a noninterruptible wait.
25267c478bd9Sstevel@tonic-gate 		 */
25277c478bd9Sstevel@tonic-gate 		cv_wait(&cm_entry->x_cv, &cm_entry->x_lock);
25287c478bd9Sstevel@tonic-gate 	}
25297c478bd9Sstevel@tonic-gate 
25307c478bd9Sstevel@tonic-gate 	if (cm_entry->x_tiptr != NULL)
25317c478bd9Sstevel@tonic-gate 		(void) t_kclose(cm_entry->x_tiptr, 1);
25327c478bd9Sstevel@tonic-gate 
25337c478bd9Sstevel@tonic-gate 	mutex_exit(&cm_entry->x_lock);
25347c478bd9Sstevel@tonic-gate 	if (cm_entry->x_ksp != NULL) {
25357c478bd9Sstevel@tonic-gate 		mutex_enter(&connmgr_lock);
25367c478bd9Sstevel@tonic-gate 		cm_entry->x_ksp->ks_private = NULL;
25377c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
25387c478bd9Sstevel@tonic-gate 
25397c478bd9Sstevel@tonic-gate 		/*
25407c478bd9Sstevel@tonic-gate 		 * Must free the buffer we allocated for the
25417c478bd9Sstevel@tonic-gate 		 * server address in the update function
25427c478bd9Sstevel@tonic-gate 		 */
25437c478bd9Sstevel@tonic-gate 		if (((struct cm_kstat_xprt *)(cm_entry->x_ksp->ks_data))->
2544a1b5e537Sbmc 		    x_server.value.str.addr.ptr != NULL)
25457c478bd9Sstevel@tonic-gate 			kmem_free(((struct cm_kstat_xprt *)(cm_entry->x_ksp->
2546a1b5e537Sbmc 			    ks_data))->x_server.value.str.addr.ptr,
25477c478bd9Sstevel@tonic-gate 			    INET6_ADDRSTRLEN);
25487c478bd9Sstevel@tonic-gate 		kmem_free(cm_entry->x_ksp->ks_data,
25497c478bd9Sstevel@tonic-gate 		    cm_entry->x_ksp->ks_data_size);
25507c478bd9Sstevel@tonic-gate 		kstat_delete(cm_entry->x_ksp);
25517c478bd9Sstevel@tonic-gate 	}
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 	mutex_destroy(&cm_entry->x_lock);
25547c478bd9Sstevel@tonic-gate 	cv_destroy(&cm_entry->x_cv);
25557c478bd9Sstevel@tonic-gate 	cv_destroy(&cm_entry->x_conn_cv);
25567c478bd9Sstevel@tonic-gate 	cv_destroy(&cm_entry->x_dis_cv);
25577c478bd9Sstevel@tonic-gate 
25587c478bd9Sstevel@tonic-gate 	if (cm_entry->x_server.buf != NULL)
25597c478bd9Sstevel@tonic-gate 		kmem_free(cm_entry->x_server.buf, cm_entry->x_server.maxlen);
25607c478bd9Sstevel@tonic-gate 	if (cm_entry->x_src.buf != NULL)
25617c478bd9Sstevel@tonic-gate 		kmem_free(cm_entry->x_src.buf, cm_entry->x_src.maxlen);
25627c478bd9Sstevel@tonic-gate 	kmem_free(cm_entry, sizeof (struct cm_xprt));
25637c478bd9Sstevel@tonic-gate }
25647c478bd9Sstevel@tonic-gate 
25657c478bd9Sstevel@tonic-gate /*
25667c478bd9Sstevel@tonic-gate  * Called by KRPC after sending the call message to release the connection
25677c478bd9Sstevel@tonic-gate  * it was using.
25687c478bd9Sstevel@tonic-gate  */
25697c478bd9Sstevel@tonic-gate static void
connmgr_release(struct cm_xprt * cm_entry)25707c478bd9Sstevel@tonic-gate connmgr_release(struct cm_xprt *cm_entry)
25717c478bd9Sstevel@tonic-gate {
25727c478bd9Sstevel@tonic-gate 	mutex_enter(&cm_entry->x_lock);
25737c478bd9Sstevel@tonic-gate 	cm_entry->x_ref--;
25747c478bd9Sstevel@tonic-gate 	if (cm_entry->x_ref == 0)
25757c478bd9Sstevel@tonic-gate 		cv_signal(&cm_entry->x_cv);
25767c478bd9Sstevel@tonic-gate 	mutex_exit(&cm_entry->x_lock);
25777c478bd9Sstevel@tonic-gate }
25787c478bd9Sstevel@tonic-gate 
25797c478bd9Sstevel@tonic-gate /*
25802081ac19SDai Ngo  * Set TCP receive and xmit buffer size for RPC connections.
25812081ac19SDai Ngo  */
25822081ac19SDai Ngo static bool_t
connmgr_setbufsz(calllist_t * e,queue_t * wq,cred_t * cr)25832081ac19SDai Ngo connmgr_setbufsz(calllist_t *e, queue_t *wq, cred_t *cr)
25842081ac19SDai Ngo {
25852081ac19SDai Ngo 	int ok = FALSE;
25862081ac19SDai Ngo 	int val;
25872081ac19SDai Ngo 
25882081ac19SDai Ngo 	if (rpc_default_tcp_bufsz)
25892081ac19SDai Ngo 		return (FALSE);
25902081ac19SDai Ngo 
25912081ac19SDai Ngo 	/*
25922081ac19SDai Ngo 	 * Only set new buffer size if it's larger than the system
25932081ac19SDai Ngo 	 * default buffer size. If smaller buffer size is needed
25942081ac19SDai Ngo 	 * then use /etc/system to set rpc_default_tcp_bufsz to 1.
25952081ac19SDai Ngo 	 */
25962081ac19SDai Ngo 	ok = connmgr_getopt_int(wq, SOL_SOCKET, SO_RCVBUF, &val, e, cr);
25972081ac19SDai Ngo 	if ((ok == TRUE) && (val < rpc_send_bufsz)) {
25982081ac19SDai Ngo 		ok = connmgr_setopt_int(wq, SOL_SOCKET, SO_RCVBUF,
25992081ac19SDai Ngo 		    rpc_send_bufsz, e, cr);
26002081ac19SDai Ngo 		DTRACE_PROBE2(krpc__i__connmgr_rcvbufsz,
26012081ac19SDai Ngo 		    int, ok, calllist_t *, e);
26022081ac19SDai Ngo 	}
26032081ac19SDai Ngo 
26042081ac19SDai Ngo 	ok = connmgr_getopt_int(wq, SOL_SOCKET, SO_SNDBUF, &val, e, cr);
26052081ac19SDai Ngo 	if ((ok == TRUE) && (val < rpc_recv_bufsz)) {
26062081ac19SDai Ngo 		ok = connmgr_setopt_int(wq, SOL_SOCKET, SO_SNDBUF,
26072081ac19SDai Ngo 		    rpc_recv_bufsz, e, cr);
26082081ac19SDai Ngo 		DTRACE_PROBE2(krpc__i__connmgr_sndbufsz,
26092081ac19SDai Ngo 		    int, ok, calllist_t *, e);
26102081ac19SDai Ngo 	}
26112081ac19SDai Ngo 	return (TRUE);
26122081ac19SDai Ngo }
26132081ac19SDai Ngo 
26142081ac19SDai Ngo /*
26157c478bd9Sstevel@tonic-gate  * Given an open stream, connect to the remote.  Returns true if connected,
26167c478bd9Sstevel@tonic-gate  * false otherwise.
26177c478bd9Sstevel@tonic-gate  */
26187c478bd9Sstevel@tonic-gate static bool_t
connmgr_connect(struct cm_xprt * cm_entry,queue_t * wq,struct netbuf * addr,int addrfmly,calllist_t * e,int * tidu_ptr,bool_t reconnect,const struct timeval * waitp,bool_t nosignal,cred_t * cr)26197c478bd9Sstevel@tonic-gate connmgr_connect(
26207c478bd9Sstevel@tonic-gate 	struct cm_xprt		*cm_entry,
26217c478bd9Sstevel@tonic-gate 	queue_t			*wq,
26227c478bd9Sstevel@tonic-gate 	struct netbuf		*addr,
26237c478bd9Sstevel@tonic-gate 	int			addrfmly,
26247c478bd9Sstevel@tonic-gate 	calllist_t 		*e,
26257c478bd9Sstevel@tonic-gate 	int 			*tidu_ptr,
26267c478bd9Sstevel@tonic-gate 	bool_t 			reconnect,
26277c478bd9Sstevel@tonic-gate 	const struct timeval 	*waitp,
2628de8c4a14SErik Nordmark 	bool_t 			nosignal,
2629de8c4a14SErik Nordmark 	cred_t			*cr)
26307c478bd9Sstevel@tonic-gate {
26317c478bd9Sstevel@tonic-gate 	mblk_t *mp;
26327c478bd9Sstevel@tonic-gate 	struct T_conn_req *tcr;
26337c478bd9Sstevel@tonic-gate 	struct T_info_ack *tinfo;
26347c478bd9Sstevel@tonic-gate 	int interrupted, error;
26357c478bd9Sstevel@tonic-gate 	int tidu_size, kstat_instance;
26367c478bd9Sstevel@tonic-gate 
26377c478bd9Sstevel@tonic-gate 	/* if it's a reconnect, flush any lingering data messages */
26387c478bd9Sstevel@tonic-gate 	if (reconnect)
26397c478bd9Sstevel@tonic-gate 		(void) putctl1(wq, M_FLUSH, FLUSHRW);
26407c478bd9Sstevel@tonic-gate 
2641de8c4a14SErik Nordmark 	/*
2642de8c4a14SErik Nordmark 	 * Note: if the receiver uses SCM_UCRED/getpeerucred the pid will
2643de8c4a14SErik Nordmark 	 * appear as -1.
2644de8c4a14SErik Nordmark 	 */
2645de8c4a14SErik Nordmark 	mp = allocb_cred(sizeof (*tcr) + addr->len, cr, NOPID);
26467c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
26477c478bd9Sstevel@tonic-gate 		/*
26487c478bd9Sstevel@tonic-gate 		 * This is unfortunate, but we need to look up the stats for
26497c478bd9Sstevel@tonic-gate 		 * this zone to increment the "memory allocation failed"
26507c478bd9Sstevel@tonic-gate 		 * counter.  curproc->p_zone is safe since we're initiating a
26517c478bd9Sstevel@tonic-gate 		 * connection and not in some strange streams context.
26527c478bd9Sstevel@tonic-gate 		 */
26537c478bd9Sstevel@tonic-gate 		struct rpcstat *rpcstat;
26547c478bd9Sstevel@tonic-gate 
2655108322fbScarlsonj 		rpcstat = zone_getspecific(rpcstat_zone_key, rpc_zone());
26567c478bd9Sstevel@tonic-gate 		ASSERT(rpcstat != NULL);
26577c478bd9Sstevel@tonic-gate 
26587c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "connmgr_connect: cannot alloc mp for "
26597c478bd9Sstevel@tonic-gate 		    "sending conn request\n");
26607c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(rpcstat->rpc_cots_client, rcnomem);
26617c478bd9Sstevel@tonic-gate 		e->call_status = RPC_SYSTEMERROR;
26627c478bd9Sstevel@tonic-gate 		e->call_reason = ENOSR;
26637c478bd9Sstevel@tonic-gate 		return (FALSE);
26647c478bd9Sstevel@tonic-gate 	}
26657c478bd9Sstevel@tonic-gate 
26662081ac19SDai Ngo 	/* Set TCP buffer size for RPC connections if needed */
26672081ac19SDai Ngo 	if (addrfmly == AF_INET || addrfmly == AF_INET6)
26682081ac19SDai Ngo 		(void) connmgr_setbufsz(e, wq, cr);
26692081ac19SDai Ngo 
26707c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
26717c478bd9Sstevel@tonic-gate 	tcr = (struct T_conn_req *)mp->b_rptr;
26727c478bd9Sstevel@tonic-gate 	bzero(tcr, sizeof (*tcr));
26737c478bd9Sstevel@tonic-gate 	tcr->PRIM_type = T_CONN_REQ;
26747c478bd9Sstevel@tonic-gate 	tcr->DEST_length = addr->len;
26757c478bd9Sstevel@tonic-gate 	tcr->DEST_offset = sizeof (struct T_conn_req);
26767c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (*tcr);
26777c478bd9Sstevel@tonic-gate 
26787c478bd9Sstevel@tonic-gate 	bcopy(addr->buf, mp->b_wptr, tcr->DEST_length);
26797c478bd9Sstevel@tonic-gate 	mp->b_wptr += tcr->DEST_length;
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_connect: sending conn request on queue "
26827c478bd9Sstevel@tonic-gate 	    "%p", (void *)wq);
26837c478bd9Sstevel@tonic-gate 	RPCLOG(8, " call %p\n", (void *)wq);
26847c478bd9Sstevel@tonic-gate 	/*
26857c478bd9Sstevel@tonic-gate 	 * We use the entry in the handle that is normally used for
26867c478bd9Sstevel@tonic-gate 	 * waiting for RPC replies to wait for the connection accept.
26877c478bd9Sstevel@tonic-gate 	 */
2688125a8fd9SSiddheshwar Mahesh 	if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) {
2689125a8fd9SSiddheshwar Mahesh 		DTRACE_PROBE(krpc__e__connmgr__connect__cantsend);
2690125a8fd9SSiddheshwar Mahesh 		freemsg(mp);
2691125a8fd9SSiddheshwar Mahesh 		return (FALSE);
2692125a8fd9SSiddheshwar Mahesh 	}
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
26957c478bd9Sstevel@tonic-gate 
26967c478bd9Sstevel@tonic-gate 	/*
26977c478bd9Sstevel@tonic-gate 	 * We wait for the transport connection to be made, or an
26987c478bd9Sstevel@tonic-gate 	 * indication that it could not be made.
26997c478bd9Sstevel@tonic-gate 	 */
27007c478bd9Sstevel@tonic-gate 	interrupted = 0;
27017c478bd9Sstevel@tonic-gate 
27027c478bd9Sstevel@tonic-gate 	/*
27037c478bd9Sstevel@tonic-gate 	 * waitforack should have been called with T_OK_ACK, but the
27047c478bd9Sstevel@tonic-gate 	 * present implementation needs to be passed T_INFO_ACK to
27057c478bd9Sstevel@tonic-gate 	 * work correctly.
27067c478bd9Sstevel@tonic-gate 	 */
27077c478bd9Sstevel@tonic-gate 	error = waitforack(e, T_INFO_ACK, waitp, nosignal);
27087c478bd9Sstevel@tonic-gate 	if (error == EINTR)
27097c478bd9Sstevel@tonic-gate 		interrupted = 1;
27107c478bd9Sstevel@tonic-gate 	if (zone_status_get(curproc->p_zone) >= ZONE_IS_EMPTY) {
27117c478bd9Sstevel@tonic-gate 		/*
27127c478bd9Sstevel@tonic-gate 		 * No time to lose; we essentially have been signaled to
27137c478bd9Sstevel@tonic-gate 		 * quit.
27147c478bd9Sstevel@tonic-gate 		 */
27157c478bd9Sstevel@tonic-gate 		interrupted = 1;
27167c478bd9Sstevel@tonic-gate 	}
27177c478bd9Sstevel@tonic-gate #ifdef RPCDEBUG
27187c478bd9Sstevel@tonic-gate 	if (error == ETIME)
27197c478bd9Sstevel@tonic-gate 		RPCLOG0(8, "connmgr_connect: giving up "
27207c478bd9Sstevel@tonic-gate 		    "on connection attempt; "
27217c478bd9Sstevel@tonic-gate 		    "clnt_dispatch notifyconn "
27227c478bd9Sstevel@tonic-gate 		    "diagnostic 'no one waiting for "
27237c478bd9Sstevel@tonic-gate 		    "connection' should not be "
27247c478bd9Sstevel@tonic-gate 		    "unexpected\n");
27257c478bd9Sstevel@tonic-gate #endif
27267c478bd9Sstevel@tonic-gate 	if (e->call_prev)
27277c478bd9Sstevel@tonic-gate 		e->call_prev->call_next = e->call_next;
27287c478bd9Sstevel@tonic-gate 	else
27297c478bd9Sstevel@tonic-gate 		clnt_pending = e->call_next;
27307c478bd9Sstevel@tonic-gate 	if (e->call_next)
27317c478bd9Sstevel@tonic-gate 		e->call_next->call_prev = e->call_prev;
27327c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
27337c478bd9Sstevel@tonic-gate 
27347c478bd9Sstevel@tonic-gate 	if (e->call_status != RPC_SUCCESS || error != 0) {
27357c478bd9Sstevel@tonic-gate 		if (interrupted)
27367c478bd9Sstevel@tonic-gate 			e->call_status = RPC_INTR;
27377c478bd9Sstevel@tonic-gate 		else if (error == ETIME)
27387c478bd9Sstevel@tonic-gate 			e->call_status = RPC_TIMEDOUT;
27398c3630f0SGerald Thornbrugh 		else if (error == EPROTO) {
27407c478bd9Sstevel@tonic-gate 			e->call_status = RPC_SYSTEMERROR;
27418c3630f0SGerald Thornbrugh 			e->call_reason = EPROTO;
27428c3630f0SGerald Thornbrugh 		}
27437c478bd9Sstevel@tonic-gate 
27447c478bd9Sstevel@tonic-gate 		RPCLOG(8, "connmgr_connect: can't connect, status: "
27457c478bd9Sstevel@tonic-gate 		    "%s\n", clnt_sperrno(e->call_status));
27467c478bd9Sstevel@tonic-gate 
27477c478bd9Sstevel@tonic-gate 		if (e->call_reply) {
27487c478bd9Sstevel@tonic-gate 			freemsg(e->call_reply);
27497c478bd9Sstevel@tonic-gate 			e->call_reply = NULL;
27507c478bd9Sstevel@tonic-gate 		}
27517c478bd9Sstevel@tonic-gate 
27527c478bd9Sstevel@tonic-gate 		return (FALSE);
27537c478bd9Sstevel@tonic-gate 	}
27547c478bd9Sstevel@tonic-gate 	/*
27557c478bd9Sstevel@tonic-gate 	 * The result of the "connection accept" is a T_info_ack
27567c478bd9Sstevel@tonic-gate 	 * in the call_reply field.
27577c478bd9Sstevel@tonic-gate 	 */
27587c478bd9Sstevel@tonic-gate 	ASSERT(e->call_reply != NULL);
27597c478bd9Sstevel@tonic-gate 	mp = e->call_reply;
27607c478bd9Sstevel@tonic-gate 	e->call_reply = NULL;
27617c478bd9Sstevel@tonic-gate 	tinfo = (struct T_info_ack *)mp->b_rptr;
27627c478bd9Sstevel@tonic-gate 
27637c478bd9Sstevel@tonic-gate 	tidu_size = tinfo->TIDU_size;
27647c478bd9Sstevel@tonic-gate 	tidu_size -= (tidu_size % BYTES_PER_XDR_UNIT);
27657c478bd9Sstevel@tonic-gate 	if (tidu_size > COTS_DEFAULT_ALLOCSIZE || (tidu_size <= 0))
27667c478bd9Sstevel@tonic-gate 		tidu_size = COTS_DEFAULT_ALLOCSIZE;
27677c478bd9Sstevel@tonic-gate 	*tidu_ptr = tidu_size;
27687c478bd9Sstevel@tonic-gate 
27697c478bd9Sstevel@tonic-gate 	freemsg(mp);
27707c478bd9Sstevel@tonic-gate 
27717c478bd9Sstevel@tonic-gate 	/*
27727c478bd9Sstevel@tonic-gate 	 * Set up the pertinent options.  NODELAY is so the transport doesn't
27737c478bd9Sstevel@tonic-gate 	 * buffer up RPC messages on either end.  This may not be valid for
27747c478bd9Sstevel@tonic-gate 	 * all transports.  Failure to set this option is not cause to
27757c478bd9Sstevel@tonic-gate 	 * bail out so we return success anyway.  Note that lack of NODELAY
27767c478bd9Sstevel@tonic-gate 	 * or some other way to flush the message on both ends will cause
27777c478bd9Sstevel@tonic-gate 	 * lots of retries and terrible performance.
27787c478bd9Sstevel@tonic-gate 	 */
27797c478bd9Sstevel@tonic-gate 	if (addrfmly == AF_INET || addrfmly == AF_INET6) {
2780de8c4a14SErik Nordmark 		(void) connmgr_setopt(wq, IPPROTO_TCP, TCP_NODELAY, e, cr);
27817c478bd9Sstevel@tonic-gate 		if (e->call_status == RPC_XPRTFAILED)
27827c478bd9Sstevel@tonic-gate 			return (FALSE);
27837c478bd9Sstevel@tonic-gate 	}
27847c478bd9Sstevel@tonic-gate 
27857c478bd9Sstevel@tonic-gate 	/*
27867c478bd9Sstevel@tonic-gate 	 * Since we have a connection, we now need to figure out if
27877c478bd9Sstevel@tonic-gate 	 * we need to create a kstat. If x_ksp is not NULL then we
27887c478bd9Sstevel@tonic-gate 	 * are reusing a connection and so we do not need to create
27897c478bd9Sstevel@tonic-gate 	 * another kstat -- lets just return.
27907c478bd9Sstevel@tonic-gate 	 */
27917c478bd9Sstevel@tonic-gate 	if (cm_entry->x_ksp != NULL)
27927c478bd9Sstevel@tonic-gate 		return (TRUE);
27937c478bd9Sstevel@tonic-gate 
27947c478bd9Sstevel@tonic-gate 	/*
27957c478bd9Sstevel@tonic-gate 	 * We need to increment rpc_kstat_instance atomically to prevent
27967c478bd9Sstevel@tonic-gate 	 * two kstats being created with the same instance.
27977c478bd9Sstevel@tonic-gate 	 */
2798*1a5e258fSJosef 'Jeff' Sipek 	kstat_instance = atomic_inc_32_nv((uint32_t *)&rpc_kstat_instance);
27997c478bd9Sstevel@tonic-gate 
28007c478bd9Sstevel@tonic-gate 	if ((cm_entry->x_ksp = kstat_create_zone("unix", kstat_instance,
28017c478bd9Sstevel@tonic-gate 	    "rpc_cots_connections", "rpc", KSTAT_TYPE_NAMED,
28027c478bd9Sstevel@tonic-gate 	    (uint_t)(sizeof (cm_kstat_xprt_t) / sizeof (kstat_named_t)),
28037c478bd9Sstevel@tonic-gate 	    KSTAT_FLAG_VIRTUAL, cm_entry->x_zoneid)) == NULL) {
28047c478bd9Sstevel@tonic-gate 		return (TRUE);
28057c478bd9Sstevel@tonic-gate 	}
28067c478bd9Sstevel@tonic-gate 
28077c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_lock = &connmgr_lock;
28087c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_private = cm_entry;
28097c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_data_size = ((INET6_ADDRSTRLEN * sizeof (char))
28107c478bd9Sstevel@tonic-gate 	    + sizeof (cm_kstat_template));
28117c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_data = kmem_alloc(cm_entry->x_ksp->ks_data_size,
28127c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
28137c478bd9Sstevel@tonic-gate 	bcopy(&cm_kstat_template, cm_entry->x_ksp->ks_data,
28147c478bd9Sstevel@tonic-gate 	    cm_entry->x_ksp->ks_data_size);
28157c478bd9Sstevel@tonic-gate 	((struct cm_kstat_xprt *)(cm_entry->x_ksp->ks_data))->
2816a1b5e537Sbmc 	    x_server.value.str.addr.ptr =
28177c478bd9Sstevel@tonic-gate 	    kmem_alloc(INET6_ADDRSTRLEN, KM_SLEEP);
28187c478bd9Sstevel@tonic-gate 
28197c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_update = conn_kstat_update;
28207c478bd9Sstevel@tonic-gate 	kstat_install(cm_entry->x_ksp);
28217c478bd9Sstevel@tonic-gate 	return (TRUE);
28227c478bd9Sstevel@tonic-gate }
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate /*
28252081ac19SDai Ngo  * Verify that the specified offset falls within the mblk and
28262081ac19SDai Ngo  * that the resulting pointer is aligned.
28272081ac19SDai Ngo  * Returns NULL if not.
28282081ac19SDai Ngo  *
28292081ac19SDai Ngo  * code from fs/sockfs/socksubr.c
28302081ac19SDai Ngo  */
28312081ac19SDai Ngo static void *
connmgr_opt_getoff(mblk_t * mp,t_uscalar_t offset,t_uscalar_t length,uint_t align_size)28322081ac19SDai Ngo connmgr_opt_getoff(mblk_t *mp, t_uscalar_t offset,
28332081ac19SDai Ngo     t_uscalar_t length, uint_t align_size)
28342081ac19SDai Ngo {
28352081ac19SDai Ngo 	uintptr_t ptr1, ptr2;
28362081ac19SDai Ngo 
28372081ac19SDai Ngo 	ASSERT(mp && mp->b_wptr >= mp->b_rptr);
28382081ac19SDai Ngo 	ptr1 = (uintptr_t)mp->b_rptr + offset;
28392081ac19SDai Ngo 	ptr2 = (uintptr_t)ptr1 + length;
28402081ac19SDai Ngo 	if (ptr1 < (uintptr_t)mp->b_rptr || ptr2 > (uintptr_t)mp->b_wptr) {
28412081ac19SDai Ngo 		return (NULL);
28422081ac19SDai Ngo 	}
28432081ac19SDai Ngo 	if ((ptr1 & (align_size - 1)) != 0) {
28442081ac19SDai Ngo 		return (NULL);
28452081ac19SDai Ngo 	}
28462081ac19SDai Ngo 	return ((void *)ptr1);
28472081ac19SDai Ngo }
28482081ac19SDai Ngo 
28492081ac19SDai Ngo static bool_t
connmgr_getopt_int(queue_t * wq,int level,int name,int * val,calllist_t * e,cred_t * cr)28502081ac19SDai Ngo connmgr_getopt_int(queue_t *wq, int level, int name, int *val,
28512081ac19SDai Ngo     calllist_t *e, cred_t *cr)
28522081ac19SDai Ngo {
28532081ac19SDai Ngo 	mblk_t *mp;
28542081ac19SDai Ngo 	struct opthdr *opt, *opt_res;
28552081ac19SDai Ngo 	struct T_optmgmt_req *tor;
28562081ac19SDai Ngo 	struct T_optmgmt_ack *opt_ack;
28572081ac19SDai Ngo 	struct timeval waitp;
28582081ac19SDai Ngo 	int error;
28592081ac19SDai Ngo 
28602081ac19SDai Ngo 	mp = allocb_cred(sizeof (struct T_optmgmt_req) +
28612081ac19SDai Ngo 	    sizeof (struct opthdr) + sizeof (int), cr, NOPID);
28622081ac19SDai Ngo 	if (mp == NULL)
28632081ac19SDai Ngo 		return (FALSE);
28642081ac19SDai Ngo 
28652081ac19SDai Ngo 	mp->b_datap->db_type = M_PROTO;
28662081ac19SDai Ngo 	tor = (struct T_optmgmt_req *)(mp->b_rptr);
28672081ac19SDai Ngo 	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
28682081ac19SDai Ngo 	tor->MGMT_flags = T_CURRENT;
28692081ac19SDai Ngo 	tor->OPT_length = sizeof (struct opthdr) + sizeof (int);
28702081ac19SDai Ngo 	tor->OPT_offset = sizeof (struct T_optmgmt_req);
28712081ac19SDai Ngo 
28722081ac19SDai Ngo 	opt = (struct opthdr *)(mp->b_rptr + sizeof (struct T_optmgmt_req));
28732081ac19SDai Ngo 	opt->level = level;
28742081ac19SDai Ngo 	opt->name = name;
28752081ac19SDai Ngo 	opt->len = sizeof (int);
28762081ac19SDai Ngo 	mp->b_wptr += sizeof (struct T_optmgmt_req) + sizeof (struct opthdr) +
28772081ac19SDai Ngo 	    sizeof (int);
28782081ac19SDai Ngo 
28792081ac19SDai Ngo 	/*
28802081ac19SDai Ngo 	 * We will use this connection regardless
28812081ac19SDai Ngo 	 * of whether or not the option is readable.
28822081ac19SDai Ngo 	 */
28832081ac19SDai Ngo 	if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) {
28842081ac19SDai Ngo 		DTRACE_PROBE(krpc__e__connmgr__getopt__cantsend);
28852081ac19SDai Ngo 		freemsg(mp);
28862081ac19SDai Ngo 		return (FALSE);
28872081ac19SDai Ngo 	}
28882081ac19SDai Ngo 
28892081ac19SDai Ngo 	mutex_enter(&clnt_pending_lock);
28902081ac19SDai Ngo 
28912081ac19SDai Ngo 	waitp.tv_sec = clnt_cots_min_conntout;
28922081ac19SDai Ngo 	waitp.tv_usec = 0;
28932081ac19SDai Ngo 	error = waitforack(e, T_OPTMGMT_ACK, &waitp, 1);
28942081ac19SDai Ngo 
28952081ac19SDai Ngo 	if (e->call_prev)
28962081ac19SDai Ngo 		e->call_prev->call_next = e->call_next;
28972081ac19SDai Ngo 	else
28982081ac19SDai Ngo 		clnt_pending = e->call_next;
28992081ac19SDai Ngo 	if (e->call_next)
29002081ac19SDai Ngo 		e->call_next->call_prev = e->call_prev;
29012081ac19SDai Ngo 	mutex_exit(&clnt_pending_lock);
29022081ac19SDai Ngo 
29032081ac19SDai Ngo 	/* get reply message */
29042081ac19SDai Ngo 	mp = e->call_reply;
29052081ac19SDai Ngo 	e->call_reply = NULL;
29062081ac19SDai Ngo 
29072081ac19SDai Ngo 	if ((!mp) || (e->call_status != RPC_SUCCESS) || (error != 0)) {
29082081ac19SDai Ngo 
29092081ac19SDai Ngo 		DTRACE_PROBE4(krpc__e__connmgr_getopt, int, name,
29102081ac19SDai Ngo 		    int, e->call_status, int, error, mblk_t *, mp);
29112081ac19SDai Ngo 
29122081ac19SDai Ngo 		if (mp)
29132081ac19SDai Ngo 			freemsg(mp);
29142081ac19SDai Ngo 		return (FALSE);
29152081ac19SDai Ngo 	}
29162081ac19SDai Ngo 
29172081ac19SDai Ngo 	opt_ack = (struct T_optmgmt_ack *)mp->b_rptr;
29182081ac19SDai Ngo 	opt_res = (struct opthdr *)connmgr_opt_getoff(mp, opt_ack->OPT_offset,
29192081ac19SDai Ngo 	    opt_ack->OPT_length, __TPI_ALIGN_SIZE);
29202081ac19SDai Ngo 
29212081ac19SDai Ngo 	if (!opt_res) {
29222081ac19SDai Ngo 		DTRACE_PROBE4(krpc__e__connmgr_optres, mblk_t *, mp, int, name,
29232081ac19SDai Ngo 		    int, opt_ack->OPT_offset, int, opt_ack->OPT_length);
29242081ac19SDai Ngo 		freemsg(mp);
29252081ac19SDai Ngo 		return (FALSE);
29262081ac19SDai Ngo 	}
29272081ac19SDai Ngo 	*val = *(int *)&opt_res[1];
29282081ac19SDai Ngo 
29292081ac19SDai Ngo 	DTRACE_PROBE2(connmgr_getopt__ok, int, name, int, *val);
29302081ac19SDai Ngo 
29312081ac19SDai Ngo 	freemsg(mp);
29322081ac19SDai Ngo 	return (TRUE);
29332081ac19SDai Ngo }
29342081ac19SDai Ngo 
29352081ac19SDai Ngo /*
29367c478bd9Sstevel@tonic-gate  * Called by connmgr_connect to set an option on the new stream.
29377c478bd9Sstevel@tonic-gate  */
29387c478bd9Sstevel@tonic-gate static bool_t
connmgr_setopt_int(queue_t * wq,int level,int name,int val,calllist_t * e,cred_t * cr)29392081ac19SDai Ngo connmgr_setopt_int(queue_t *wq, int level, int name, int val,
29402081ac19SDai Ngo     calllist_t *e, cred_t *cr)
29417c478bd9Sstevel@tonic-gate {
29427c478bd9Sstevel@tonic-gate 	mblk_t *mp;
29437c478bd9Sstevel@tonic-gate 	struct opthdr *opt;
29447c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor;
29457c478bd9Sstevel@tonic-gate 	struct timeval waitp;
29467c478bd9Sstevel@tonic-gate 	int error;
29477c478bd9Sstevel@tonic-gate 
2948de8c4a14SErik Nordmark 	mp = allocb_cred(sizeof (struct T_optmgmt_req) +
2949de8c4a14SErik Nordmark 	    sizeof (struct opthdr) + sizeof (int), cr, NOPID);
29507c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
29517c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "connmgr_setopt: cannot alloc mp for option "
29527c478bd9Sstevel@tonic-gate 		    "request\n");
29537c478bd9Sstevel@tonic-gate 		return (FALSE);
29547c478bd9Sstevel@tonic-gate 	}
29557c478bd9Sstevel@tonic-gate 
29567c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
29577c478bd9Sstevel@tonic-gate 	tor = (struct T_optmgmt_req *)(mp->b_rptr);
29587c478bd9Sstevel@tonic-gate 	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
29597c478bd9Sstevel@tonic-gate 	tor->MGMT_flags = T_NEGOTIATE;
29607c478bd9Sstevel@tonic-gate 	tor->OPT_length = sizeof (struct opthdr) + sizeof (int);
29617c478bd9Sstevel@tonic-gate 	tor->OPT_offset = sizeof (struct T_optmgmt_req);
29627c478bd9Sstevel@tonic-gate 
29637c478bd9Sstevel@tonic-gate 	opt = (struct opthdr *)(mp->b_rptr + sizeof (struct T_optmgmt_req));
29647c478bd9Sstevel@tonic-gate 	opt->level = level;
29657c478bd9Sstevel@tonic-gate 	opt->name = name;
29667c478bd9Sstevel@tonic-gate 	opt->len = sizeof (int);
29672081ac19SDai Ngo 	*(int *)((char *)opt + sizeof (*opt)) = val;
29687c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (struct T_optmgmt_req) + sizeof (struct opthdr) +
29697c478bd9Sstevel@tonic-gate 	    sizeof (int);
29707c478bd9Sstevel@tonic-gate 
29717c478bd9Sstevel@tonic-gate 	/*
29727c478bd9Sstevel@tonic-gate 	 * We will use this connection regardless
29737c478bd9Sstevel@tonic-gate 	 * of whether or not the option is settable.
29747c478bd9Sstevel@tonic-gate 	 */
2975125a8fd9SSiddheshwar Mahesh 	if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) {
2976125a8fd9SSiddheshwar Mahesh 		DTRACE_PROBE(krpc__e__connmgr__setopt__cantsend);
2977125a8fd9SSiddheshwar Mahesh 		freemsg(mp);
2978125a8fd9SSiddheshwar Mahesh 		return (FALSE);
2979125a8fd9SSiddheshwar Mahesh 	}
2980125a8fd9SSiddheshwar Mahesh 
29817c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
29827c478bd9Sstevel@tonic-gate 
29837c478bd9Sstevel@tonic-gate 	waitp.tv_sec = clnt_cots_min_conntout;
29847c478bd9Sstevel@tonic-gate 	waitp.tv_usec = 0;
29857c478bd9Sstevel@tonic-gate 	error = waitforack(e, T_OPTMGMT_ACK, &waitp, 1);
29867c478bd9Sstevel@tonic-gate 
29877c478bd9Sstevel@tonic-gate 	if (e->call_prev)
29887c478bd9Sstevel@tonic-gate 		e->call_prev->call_next = e->call_next;
29897c478bd9Sstevel@tonic-gate 	else
29907c478bd9Sstevel@tonic-gate 		clnt_pending = e->call_next;
29917c478bd9Sstevel@tonic-gate 	if (e->call_next)
29927c478bd9Sstevel@tonic-gate 		e->call_next->call_prev = e->call_prev;
29937c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 	if (e->call_reply != NULL) {
29967c478bd9Sstevel@tonic-gate 		freemsg(e->call_reply);
29977c478bd9Sstevel@tonic-gate 		e->call_reply = NULL;
29987c478bd9Sstevel@tonic-gate 	}
29997c478bd9Sstevel@tonic-gate 
30007c478bd9Sstevel@tonic-gate 	if (e->call_status != RPC_SUCCESS || error != 0) {
30017c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_setopt: can't set option: %d\n", name);
30027c478bd9Sstevel@tonic-gate 		return (FALSE);
30037c478bd9Sstevel@tonic-gate 	}
30047c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_setopt: successfully set option: %d\n", name);
30057c478bd9Sstevel@tonic-gate 	return (TRUE);
30067c478bd9Sstevel@tonic-gate }
30077c478bd9Sstevel@tonic-gate 
30082081ac19SDai Ngo static bool_t
connmgr_setopt(queue_t * wq,int level,int name,calllist_t * e,cred_t * cr)30092081ac19SDai Ngo connmgr_setopt(queue_t *wq, int level, int name, calllist_t *e, cred_t *cr)
30102081ac19SDai Ngo {
30112081ac19SDai Ngo 	return (connmgr_setopt_int(wq, level, name, 1, e, cr));
30122081ac19SDai Ngo }
30132081ac19SDai Ngo 
30147c478bd9Sstevel@tonic-gate #ifdef	DEBUG
30157c478bd9Sstevel@tonic-gate 
30167c478bd9Sstevel@tonic-gate /*
30177c478bd9Sstevel@tonic-gate  * This is a knob to let us force code coverage in allocation failure
30187c478bd9Sstevel@tonic-gate  * case.
30197c478bd9Sstevel@tonic-gate  */
30207c478bd9Sstevel@tonic-gate static int	connmgr_failsnd;
30217c478bd9Sstevel@tonic-gate #define	CONN_SND_ALLOC(Size, Pri)	\
30227c478bd9Sstevel@tonic-gate 	((connmgr_failsnd-- > 0) ? NULL : allocb(Size, Pri))
30237c478bd9Sstevel@tonic-gate 
30247c478bd9Sstevel@tonic-gate #else
30257c478bd9Sstevel@tonic-gate 
30267c478bd9Sstevel@tonic-gate #define	CONN_SND_ALLOC(Size, Pri)	allocb(Size, Pri)
30277c478bd9Sstevel@tonic-gate 
30287c478bd9Sstevel@tonic-gate #endif
30297c478bd9Sstevel@tonic-gate 
30307c478bd9Sstevel@tonic-gate /*
30317c478bd9Sstevel@tonic-gate  * Sends an orderly release on the specified queue.
30327c478bd9Sstevel@tonic-gate  * Entered with connmgr_lock. Exited without connmgr_lock
30337c478bd9Sstevel@tonic-gate  */
30347c478bd9Sstevel@tonic-gate static void
connmgr_sndrel(struct cm_xprt * cm_entry)30357c478bd9Sstevel@tonic-gate connmgr_sndrel(struct cm_xprt *cm_entry)
30367c478bd9Sstevel@tonic-gate {
30377c478bd9Sstevel@tonic-gate 	struct T_ordrel_req *torr;
30387c478bd9Sstevel@tonic-gate 	mblk_t *mp;
30397c478bd9Sstevel@tonic-gate 	queue_t *q = cm_entry->x_wq;
30407c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
30417c478bd9Sstevel@tonic-gate 	mp = CONN_SND_ALLOC(sizeof (struct T_ordrel_req), BPRI_LO);
30427c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
30437c478bd9Sstevel@tonic-gate 		cm_entry->x_needrel = TRUE;
30447c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
30457c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_sndrel: cannot alloc mp for sending ordrel "
30467c478bd9Sstevel@tonic-gate 		    "to queue %p\n", (void *)q);
30477c478bd9Sstevel@tonic-gate 		return;
30487c478bd9Sstevel@tonic-gate 	}
30497c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
30507c478bd9Sstevel@tonic-gate 
30517c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
30527c478bd9Sstevel@tonic-gate 	torr = (struct T_ordrel_req *)(mp->b_rptr);
30537c478bd9Sstevel@tonic-gate 	torr->PRIM_type = T_ORDREL_REQ;
30547c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (struct T_ordrel_req);
30557c478bd9Sstevel@tonic-gate 
30567c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_sndrel: sending ordrel to queue %p\n", (void *)q);
30577c478bd9Sstevel@tonic-gate 	put(q, mp);
30587c478bd9Sstevel@tonic-gate }
30597c478bd9Sstevel@tonic-gate 
30607c478bd9Sstevel@tonic-gate /*
30617c478bd9Sstevel@tonic-gate  * Sends an disconnect on the specified queue.
30627c478bd9Sstevel@tonic-gate  * Entered with connmgr_lock. Exited without connmgr_lock
30637c478bd9Sstevel@tonic-gate  */
30647c478bd9Sstevel@tonic-gate static void
connmgr_snddis(struct cm_xprt * cm_entry)30657c478bd9Sstevel@tonic-gate connmgr_snddis(struct cm_xprt *cm_entry)
30667c478bd9Sstevel@tonic-gate {
30677c478bd9Sstevel@tonic-gate 	struct T_discon_req *tdis;
30687c478bd9Sstevel@tonic-gate 	mblk_t *mp;
30697c478bd9Sstevel@tonic-gate 	queue_t *q = cm_entry->x_wq;
30707c478bd9Sstevel@tonic-gate 
30717c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
30727c478bd9Sstevel@tonic-gate 	mp = CONN_SND_ALLOC(sizeof (*tdis), BPRI_LO);
30737c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
30747c478bd9Sstevel@tonic-gate 		cm_entry->x_needdis = TRUE;
30757c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
30767c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_snddis: cannot alloc mp for sending discon "
30777c478bd9Sstevel@tonic-gate 		    "to queue %p\n", (void *)q);
30787c478bd9Sstevel@tonic-gate 		return;
30797c478bd9Sstevel@tonic-gate 	}
30807c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
30817c478bd9Sstevel@tonic-gate 
30827c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
30837c478bd9Sstevel@tonic-gate 	tdis = (struct T_discon_req *)mp->b_rptr;
30847c478bd9Sstevel@tonic-gate 	tdis->PRIM_type = T_DISCON_REQ;
30857c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (*tdis);
30867c478bd9Sstevel@tonic-gate 
30877c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_snddis: sending discon to queue %p\n", (void *)q);
30887c478bd9Sstevel@tonic-gate 	put(q, mp);
30897c478bd9Sstevel@tonic-gate }
30907c478bd9Sstevel@tonic-gate 
30917c478bd9Sstevel@tonic-gate /*
30927c478bd9Sstevel@tonic-gate  * Sets up the entry for receiving replies, and calls rpcmod's write put proc
30937c478bd9Sstevel@tonic-gate  * (through put) to send the call.
30947c478bd9Sstevel@tonic-gate  */
3095125a8fd9SSiddheshwar Mahesh static int
clnt_dispatch_send(queue_t * q,mblk_t * mp,calllist_t * e,uint_t xid,uint_t queue_flag)30967c478bd9Sstevel@tonic-gate clnt_dispatch_send(queue_t *q, mblk_t *mp, calllist_t *e, uint_t xid,
30977c478bd9Sstevel@tonic-gate 			uint_t queue_flag)
30987c478bd9Sstevel@tonic-gate {
30997c478bd9Sstevel@tonic-gate 	ASSERT(e != NULL);
31007c478bd9Sstevel@tonic-gate 
31017c478bd9Sstevel@tonic-gate 	e->call_status = RPC_TIMEDOUT;	/* optimistic, eh? */
31027c478bd9Sstevel@tonic-gate 	e->call_reason = 0;
31037c478bd9Sstevel@tonic-gate 	e->call_wq = q;
31047c478bd9Sstevel@tonic-gate 	e->call_xid = xid;
31057c478bd9Sstevel@tonic-gate 	e->call_notified = FALSE;
31067c478bd9Sstevel@tonic-gate 
3107125a8fd9SSiddheshwar Mahesh 	if (!canput(q)) {
3108125a8fd9SSiddheshwar Mahesh 		e->call_status = RPC_CANTSEND;
3109e280ed37SDai Ngo 		e->call_reason = ENOBUFS;
3110125a8fd9SSiddheshwar Mahesh 		return (RPC_CANTSEND);
3111125a8fd9SSiddheshwar Mahesh 	}
3112125a8fd9SSiddheshwar Mahesh 
31137c478bd9Sstevel@tonic-gate 	/*
31147c478bd9Sstevel@tonic-gate 	 * If queue_flag is set then the calllist_t is already on the hash
31157c478bd9Sstevel@tonic-gate 	 * queue.  In this case just send the message and return.
31167c478bd9Sstevel@tonic-gate 	 */
31177c478bd9Sstevel@tonic-gate 	if (queue_flag) {
31187c478bd9Sstevel@tonic-gate 		put(q, mp);
3119125a8fd9SSiddheshwar Mahesh 		return (RPC_SUCCESS);
3120125a8fd9SSiddheshwar Mahesh 
31217c478bd9Sstevel@tonic-gate 	}
31227c478bd9Sstevel@tonic-gate 
31237c478bd9Sstevel@tonic-gate 	/*
31247c478bd9Sstevel@tonic-gate 	 * Set up calls for RPC requests (with XID != 0) on the hash
31257c478bd9Sstevel@tonic-gate 	 * queue for fast lookups and place other calls (i.e.
31267c478bd9Sstevel@tonic-gate 	 * connection management) on the linked list.
31277c478bd9Sstevel@tonic-gate 	 */
31287c478bd9Sstevel@tonic-gate 	if (xid != 0) {
31297c478bd9Sstevel@tonic-gate 		RPCLOG(64, "clnt_dispatch_send: putting xid 0x%x on "
31307c478bd9Sstevel@tonic-gate 		    "dispatch list\n", xid);
31317c478bd9Sstevel@tonic-gate 		e->call_hash = call_hash(xid, clnt_cots_hash_size);
31327c478bd9Sstevel@tonic-gate 		e->call_bucket = &cots_call_ht[e->call_hash];
31337c478bd9Sstevel@tonic-gate 		call_table_enter(e);
31347c478bd9Sstevel@tonic-gate 	} else {
31357c478bd9Sstevel@tonic-gate 		mutex_enter(&clnt_pending_lock);
31367c478bd9Sstevel@tonic-gate 		if (clnt_pending)
31377c478bd9Sstevel@tonic-gate 			clnt_pending->call_prev = e;
31387c478bd9Sstevel@tonic-gate 		e->call_next = clnt_pending;
31397c478bd9Sstevel@tonic-gate 		e->call_prev = NULL;
31407c478bd9Sstevel@tonic-gate 		clnt_pending = e;
31417c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_pending_lock);
31427c478bd9Sstevel@tonic-gate 	}
31437c478bd9Sstevel@tonic-gate 
31447c478bd9Sstevel@tonic-gate 	put(q, mp);
3145125a8fd9SSiddheshwar Mahesh 	return (RPC_SUCCESS);
31467c478bd9Sstevel@tonic-gate }
31477c478bd9Sstevel@tonic-gate 
31487c478bd9Sstevel@tonic-gate /*
31497c478bd9Sstevel@tonic-gate  * Called by rpcmod to notify a client with a clnt_pending call that its reply
31507c478bd9Sstevel@tonic-gate  * has arrived.  If we can't find a client waiting for this reply, we log
31517c478bd9Sstevel@tonic-gate  * the error and return.
31527c478bd9Sstevel@tonic-gate  */
31537c478bd9Sstevel@tonic-gate bool_t
clnt_dispatch_notify(mblk_t * mp,zoneid_t zoneid)31547c478bd9Sstevel@tonic-gate clnt_dispatch_notify(mblk_t *mp, zoneid_t zoneid)
31557c478bd9Sstevel@tonic-gate {
31567c478bd9Sstevel@tonic-gate 	calllist_t *e = NULL;
31577c478bd9Sstevel@tonic-gate 	call_table_t *chtp;
31587c478bd9Sstevel@tonic-gate 	uint32_t xid;
31597c478bd9Sstevel@tonic-gate 	uint_t hash;
31607c478bd9Sstevel@tonic-gate 
31617c478bd9Sstevel@tonic-gate 	if ((IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) &&
31627c478bd9Sstevel@tonic-gate 	    (mp->b_wptr - mp->b_rptr) >= sizeof (xid))
31637c478bd9Sstevel@tonic-gate 		xid = *((uint32_t *)mp->b_rptr);
31647c478bd9Sstevel@tonic-gate 	else {
31657c478bd9Sstevel@tonic-gate 		int i = 0;
31667c478bd9Sstevel@tonic-gate 		unsigned char *p = (unsigned char *)&xid;
31677c478bd9Sstevel@tonic-gate 		unsigned char *rptr;
31687c478bd9Sstevel@tonic-gate 		mblk_t *tmp = mp;
31697c478bd9Sstevel@tonic-gate 
31707c478bd9Sstevel@tonic-gate 		/*
31717c478bd9Sstevel@tonic-gate 		 * Copy the xid, byte-by-byte into xid.
31727c478bd9Sstevel@tonic-gate 		 */
31737c478bd9Sstevel@tonic-gate 		while (tmp) {
31747c478bd9Sstevel@tonic-gate 			rptr = tmp->b_rptr;
31757c478bd9Sstevel@tonic-gate 			while (rptr < tmp->b_wptr) {
31767c478bd9Sstevel@tonic-gate 				*p++ = *rptr++;
31777c478bd9Sstevel@tonic-gate 				if (++i >= sizeof (xid))
31787c478bd9Sstevel@tonic-gate 					goto done_xid_copy;
31797c478bd9Sstevel@tonic-gate 			}
31807c478bd9Sstevel@tonic-gate 			tmp = tmp->b_cont;
31817c478bd9Sstevel@tonic-gate 		}
31827c478bd9Sstevel@tonic-gate 
31837c478bd9Sstevel@tonic-gate 		/*
31847c478bd9Sstevel@tonic-gate 		 * If we got here, we ran out of mblk space before the
31857c478bd9Sstevel@tonic-gate 		 * xid could be copied.
31867c478bd9Sstevel@tonic-gate 		 */
31877c478bd9Sstevel@tonic-gate 		ASSERT(tmp == NULL && i < sizeof (xid));
31887c478bd9Sstevel@tonic-gate 
31897c478bd9Sstevel@tonic-gate 		RPCLOG0(1,
31907c478bd9Sstevel@tonic-gate 		    "clnt_dispatch_notify: message less than size of xid\n");
31917c478bd9Sstevel@tonic-gate 		return (FALSE);
31927c478bd9Sstevel@tonic-gate 
31937c478bd9Sstevel@tonic-gate 	}
31947c478bd9Sstevel@tonic-gate done_xid_copy:
31957c478bd9Sstevel@tonic-gate 
31967c478bd9Sstevel@tonic-gate 	hash = call_hash(xid, clnt_cots_hash_size);
31977c478bd9Sstevel@tonic-gate 	chtp = &cots_call_ht[hash];
31987c478bd9Sstevel@tonic-gate 	/* call_table_find returns with the hash bucket locked */
31997c478bd9Sstevel@tonic-gate 	call_table_find(chtp, xid, e);
32007c478bd9Sstevel@tonic-gate 
32017c478bd9Sstevel@tonic-gate 	if (e != NULL) {
32027c478bd9Sstevel@tonic-gate 		/*
32037c478bd9Sstevel@tonic-gate 		 * Found thread waiting for this reply
32047c478bd9Sstevel@tonic-gate 		 */
32057c478bd9Sstevel@tonic-gate 		mutex_enter(&e->call_lock);
32068ffff9fdSgt29601 
32078ffff9fdSgt29601 		/*
32088ffff9fdSgt29601 		 * verify that the reply is coming in on
32098ffff9fdSgt29601 		 * the same zone that it was sent from.
32108ffff9fdSgt29601 		 */
32118ffff9fdSgt29601 		if (e->call_zoneid != zoneid) {
32128ffff9fdSgt29601 			mutex_exit(&e->call_lock);
32138ffff9fdSgt29601 			mutex_exit(&chtp->ct_lock);
32148c3630f0SGerald Thornbrugh 			RPCLOG0(1, "clnt_dispatch_notify: incorrect zoneid\n");
32158ffff9fdSgt29601 			return (FALSE);
32168ffff9fdSgt29601 		}
32178ffff9fdSgt29601 
32187c478bd9Sstevel@tonic-gate 		if (e->call_reply)
32197c478bd9Sstevel@tonic-gate 			/*
32207c478bd9Sstevel@tonic-gate 			 * This can happen under the following scenario:
32217c478bd9Sstevel@tonic-gate 			 * clnt_cots_kcallit() times out on the response,
32227c478bd9Sstevel@tonic-gate 			 * rfscall() repeats the CLNT_CALL() with
32237c478bd9Sstevel@tonic-gate 			 * the same xid, clnt_cots_kcallit() sends the retry,
32247c478bd9Sstevel@tonic-gate 			 * thereby putting the clnt handle on the pending list,
32257c478bd9Sstevel@tonic-gate 			 * the first response arrives, signalling the thread
32267c478bd9Sstevel@tonic-gate 			 * in clnt_cots_kcallit(). Before that thread is
32277c478bd9Sstevel@tonic-gate 			 * dispatched, the second response arrives as well,
32287c478bd9Sstevel@tonic-gate 			 * and clnt_dispatch_notify still finds the handle on
32297c478bd9Sstevel@tonic-gate 			 * the pending list, with call_reply set. So free the
32307c478bd9Sstevel@tonic-gate 			 * old reply now.
32317c478bd9Sstevel@tonic-gate 			 *
32327c478bd9Sstevel@tonic-gate 			 * It is also possible for a response intended for
32337c478bd9Sstevel@tonic-gate 			 * an RPC call with a different xid to reside here.
32347c478bd9Sstevel@tonic-gate 			 * This can happen if the thread that owned this
32357c478bd9Sstevel@tonic-gate 			 * client handle prior to the current owner bailed
32367c478bd9Sstevel@tonic-gate 			 * out and left its call record on the dispatch
32377c478bd9Sstevel@tonic-gate 			 * queue.  A window exists where the response can
32387c478bd9Sstevel@tonic-gate 			 * arrive before the current owner dispatches its
32397c478bd9Sstevel@tonic-gate 			 * RPC call.
32407c478bd9Sstevel@tonic-gate 			 *
32417c478bd9Sstevel@tonic-gate 			 * In any case, this is the very last point where we
32427c478bd9Sstevel@tonic-gate 			 * can safely check the call_reply field before
32437c478bd9Sstevel@tonic-gate 			 * placing the new response there.
32447c478bd9Sstevel@tonic-gate 			 */
32457c478bd9Sstevel@tonic-gate 			freemsg(e->call_reply);
32467c478bd9Sstevel@tonic-gate 		e->call_reply = mp;
32477c478bd9Sstevel@tonic-gate 		e->call_status = RPC_SUCCESS;
32487c478bd9Sstevel@tonic-gate 		e->call_notified = TRUE;
32497c478bd9Sstevel@tonic-gate 		cv_signal(&e->call_cv);
32507c478bd9Sstevel@tonic-gate 		mutex_exit(&e->call_lock);
32517c478bd9Sstevel@tonic-gate 		mutex_exit(&chtp->ct_lock);
32527c478bd9Sstevel@tonic-gate 		return (TRUE);
32537c478bd9Sstevel@tonic-gate 	} else {
32547c478bd9Sstevel@tonic-gate 		zone_t *zone;
32557c478bd9Sstevel@tonic-gate 		struct rpcstat *rpcstat;
32567c478bd9Sstevel@tonic-gate 
32577c478bd9Sstevel@tonic-gate 		mutex_exit(&chtp->ct_lock);
32587c478bd9Sstevel@tonic-gate 		RPCLOG(65, "clnt_dispatch_notify: no caller for reply 0x%x\n",
32597c478bd9Sstevel@tonic-gate 		    xid);
32607c478bd9Sstevel@tonic-gate 		/*
32617c478bd9Sstevel@tonic-gate 		 * This is unfortunate, but we need to lookup the zone so we
32627c478bd9Sstevel@tonic-gate 		 * can increment its "rcbadxids" counter.
32637c478bd9Sstevel@tonic-gate 		 */
32647c478bd9Sstevel@tonic-gate 		zone = zone_find_by_id(zoneid);
32657c478bd9Sstevel@tonic-gate 		if (zone == NULL) {
32667c478bd9Sstevel@tonic-gate 			/*
32677c478bd9Sstevel@tonic-gate 			 * The zone went away...
32687c478bd9Sstevel@tonic-gate 			 */
32697c478bd9Sstevel@tonic-gate 			return (FALSE);
32707c478bd9Sstevel@tonic-gate 		}
32717c478bd9Sstevel@tonic-gate 		rpcstat = zone_getspecific(rpcstat_zone_key, zone);
32727c478bd9Sstevel@tonic-gate 		if (zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN) {
32737c478bd9Sstevel@tonic-gate 			/*
32747c478bd9Sstevel@tonic-gate 			 * Not interested
32757c478bd9Sstevel@tonic-gate 			 */
32767c478bd9Sstevel@tonic-gate 			zone_rele(zone);
32777c478bd9Sstevel@tonic-gate 			return (FALSE);
32787c478bd9Sstevel@tonic-gate 		}
32797c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(rpcstat->rpc_cots_client, rcbadxids);
32807c478bd9Sstevel@tonic-gate 		zone_rele(zone);
32817c478bd9Sstevel@tonic-gate 	}
32827c478bd9Sstevel@tonic-gate 	return (FALSE);
32837c478bd9Sstevel@tonic-gate }
32847c478bd9Sstevel@tonic-gate 
32857c478bd9Sstevel@tonic-gate /*
32867c478bd9Sstevel@tonic-gate  * Called by rpcmod when a non-data indication arrives.  The ones in which we
32877c478bd9Sstevel@tonic-gate  * are interested are connection indications and options acks.  We dispatch
32887c478bd9Sstevel@tonic-gate  * based on the queue the indication came in on.  If we are not interested in
32897c478bd9Sstevel@tonic-gate  * what came in, we return false to rpcmod, who will then pass it upstream.
32907c478bd9Sstevel@tonic-gate  */
32917c478bd9Sstevel@tonic-gate bool_t
clnt_dispatch_notifyconn(queue_t * q,mblk_t * mp)32927c478bd9Sstevel@tonic-gate clnt_dispatch_notifyconn(queue_t *q, mblk_t *mp)
32937c478bd9Sstevel@tonic-gate {
32947c478bd9Sstevel@tonic-gate 	calllist_t *e;
32957c478bd9Sstevel@tonic-gate 	int type;
32967c478bd9Sstevel@tonic-gate 
32977c478bd9Sstevel@tonic-gate 	ASSERT((q->q_flag & QREADR) == 0);
32987c478bd9Sstevel@tonic-gate 
32997c478bd9Sstevel@tonic-gate 	type = ((union T_primitives *)mp->b_rptr)->type;
33007c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_dispatch_notifyconn: prim type: [%s]\n",
33017c478bd9Sstevel@tonic-gate 	    rpc_tpiprim2name(type));
33027c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
33037c478bd9Sstevel@tonic-gate 	for (e = clnt_pending; /* NO CONDITION */; e = e->call_next) {
33047c478bd9Sstevel@tonic-gate 		if (e == NULL) {
33057c478bd9Sstevel@tonic-gate 			mutex_exit(&clnt_pending_lock);
33067c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_dispatch_notifyconn: no one waiting "
33077c478bd9Sstevel@tonic-gate 			    "for connection on queue 0x%p\n", (void *)q);
33087c478bd9Sstevel@tonic-gate 			return (FALSE);
33097c478bd9Sstevel@tonic-gate 		}
33107c478bd9Sstevel@tonic-gate 		if (e->call_wq == q)
33117c478bd9Sstevel@tonic-gate 			break;
33127c478bd9Sstevel@tonic-gate 	}
33137c478bd9Sstevel@tonic-gate 
33147c478bd9Sstevel@tonic-gate 	switch (type) {
33157c478bd9Sstevel@tonic-gate 	case T_CONN_CON:
33167c478bd9Sstevel@tonic-gate 		/*
33177c478bd9Sstevel@tonic-gate 		 * The transport is now connected, send a T_INFO_REQ to get
33187c478bd9Sstevel@tonic-gate 		 * the tidu size.
33197c478bd9Sstevel@tonic-gate 		 */
33207c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_pending_lock);
33217c478bd9Sstevel@tonic-gate 		ASSERT(mp->b_datap->db_lim - mp->b_datap->db_base >=
33227c478bd9Sstevel@tonic-gate 		    sizeof (struct T_info_req));
33237c478bd9Sstevel@tonic-gate 		mp->b_rptr = mp->b_datap->db_base;
33247c478bd9Sstevel@tonic-gate 		((union T_primitives *)mp->b_rptr)->type = T_INFO_REQ;
33257c478bd9Sstevel@tonic-gate 		mp->b_wptr = mp->b_rptr + sizeof (struct T_info_req);
33267c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PCPROTO;
33277c478bd9Sstevel@tonic-gate 		put(q, mp);
33287c478bd9Sstevel@tonic-gate 		return (TRUE);
33297c478bd9Sstevel@tonic-gate 	case T_INFO_ACK:
33307c478bd9Sstevel@tonic-gate 	case T_OPTMGMT_ACK:
33317c478bd9Sstevel@tonic-gate 		e->call_status = RPC_SUCCESS;
33327c478bd9Sstevel@tonic-gate 		e->call_reply = mp;
33337c478bd9Sstevel@tonic-gate 		e->call_notified = TRUE;
33347c478bd9Sstevel@tonic-gate 		cv_signal(&e->call_cv);
33357c478bd9Sstevel@tonic-gate 		break;
33367c478bd9Sstevel@tonic-gate 	case T_ERROR_ACK:
33377c478bd9Sstevel@tonic-gate 		e->call_status = RPC_CANTCONNECT;
33387c478bd9Sstevel@tonic-gate 		e->call_reply = mp;
33397c478bd9Sstevel@tonic-gate 		e->call_notified = TRUE;
33407c478bd9Sstevel@tonic-gate 		cv_signal(&e->call_cv);
33417c478bd9Sstevel@tonic-gate 		break;
33427c478bd9Sstevel@tonic-gate 	case T_OK_ACK:
33437c478bd9Sstevel@tonic-gate 		/*
33447c478bd9Sstevel@tonic-gate 		 * Great, but we are really waiting for a T_CONN_CON
33457c478bd9Sstevel@tonic-gate 		 */
33467c478bd9Sstevel@tonic-gate 		freemsg(mp);
33477c478bd9Sstevel@tonic-gate 		break;
33487c478bd9Sstevel@tonic-gate 	default:
33497c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_pending_lock);
33507c478bd9Sstevel@tonic-gate 		RPCLOG(1, "clnt_dispatch_notifyconn: bad type %d\n", type);
33517c478bd9Sstevel@tonic-gate 		return (FALSE);
33527c478bd9Sstevel@tonic-gate 	}
33537c478bd9Sstevel@tonic-gate 
33547c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
33557c478bd9Sstevel@tonic-gate 	return (TRUE);
33567c478bd9Sstevel@tonic-gate }
33577c478bd9Sstevel@tonic-gate 
33587c478bd9Sstevel@tonic-gate /*
33597c478bd9Sstevel@tonic-gate  * Called by rpcmod when the transport is (or should be) going away.  Informs
33607c478bd9Sstevel@tonic-gate  * all callers waiting for replies and marks the entry in the connection
33617c478bd9Sstevel@tonic-gate  * manager's list as unconnected, and either closing (close handshake in
33627c478bd9Sstevel@tonic-gate  * progress) or dead.
33637c478bd9Sstevel@tonic-gate  */
33647c478bd9Sstevel@tonic-gate void
clnt_dispatch_notifyall(queue_t * q,int32_t msg_type,int32_t reason)33657c478bd9Sstevel@tonic-gate clnt_dispatch_notifyall(queue_t *q, int32_t msg_type, int32_t reason)
33667c478bd9Sstevel@tonic-gate {
33677c478bd9Sstevel@tonic-gate 	calllist_t *e;
33687c478bd9Sstevel@tonic-gate 	call_table_t *ctp;
33697c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
33707c478bd9Sstevel@tonic-gate 	int have_connmgr_lock;
33717c478bd9Sstevel@tonic-gate 	int i;
33727c478bd9Sstevel@tonic-gate 
33737c478bd9Sstevel@tonic-gate 	ASSERT((q->q_flag & QREADR) == 0);
33747c478bd9Sstevel@tonic-gate 
33757c478bd9Sstevel@tonic-gate 	RPCLOG(1, "clnt_dispatch_notifyall on queue %p", (void *)q);
33767c478bd9Sstevel@tonic-gate 	RPCLOG(1, " received a notifcation prim type [%s]",
33777c478bd9Sstevel@tonic-gate 	    rpc_tpiprim2name(msg_type));
33787c478bd9Sstevel@tonic-gate 	RPCLOG(1, " and reason %d\n", reason);
33797c478bd9Sstevel@tonic-gate 
33807c478bd9Sstevel@tonic-gate 	/*
33817c478bd9Sstevel@tonic-gate 	 * Find the transport entry in the connection manager's list, close
33827c478bd9Sstevel@tonic-gate 	 * the transport and delete the entry.  In the case where rpcmod's
33837c478bd9Sstevel@tonic-gate 	 * idle timer goes off, it sends us a T_ORDREL_REQ, indicating we
33847c478bd9Sstevel@tonic-gate 	 * should gracefully close the connection.
33857c478bd9Sstevel@tonic-gate 	 */
33867c478bd9Sstevel@tonic-gate 	have_connmgr_lock = 1;
33877c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
33887c478bd9Sstevel@tonic-gate 	for (cm_entry = cm_hd; cm_entry; cm_entry = cm_entry->x_next) {
33897c478bd9Sstevel@tonic-gate 		ASSERT(cm_entry != cm_entry->x_next);
33907c478bd9Sstevel@tonic-gate 		if (cm_entry->x_wq == q) {
33917c478bd9Sstevel@tonic-gate 			ASSERT(MUTEX_HELD(&connmgr_lock));
33927c478bd9Sstevel@tonic-gate 			ASSERT(have_connmgr_lock == 1);
33937c478bd9Sstevel@tonic-gate 			switch (msg_type) {
33947c478bd9Sstevel@tonic-gate 			case T_ORDREL_REQ:
33957c478bd9Sstevel@tonic-gate 
33967c478bd9Sstevel@tonic-gate 				if (cm_entry->x_dead) {
33977c478bd9Sstevel@tonic-gate 					RPCLOG(1, "idle timeout on dead "
33987c478bd9Sstevel@tonic-gate 					    "connection: %p\n",
33997c478bd9Sstevel@tonic-gate 					    (void *)cm_entry);
34007c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
34017c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
34027c478bd9Sstevel@tonic-gate 					break;
34037c478bd9Sstevel@tonic-gate 				}
34047c478bd9Sstevel@tonic-gate 
34057c478bd9Sstevel@tonic-gate 				/*
34067c478bd9Sstevel@tonic-gate 				 * Only mark the connection as dead if it is
34077c478bd9Sstevel@tonic-gate 				 * connected and idle.
34087c478bd9Sstevel@tonic-gate 				 * An unconnected connection has probably
34097c478bd9Sstevel@tonic-gate 				 * gone idle because the server is down,
34107c478bd9Sstevel@tonic-gate 				 * and when it comes back up there will be
34117c478bd9Sstevel@tonic-gate 				 * retries that need to use that connection.
34127c478bd9Sstevel@tonic-gate 				 */
34137c478bd9Sstevel@tonic-gate 				if (cm_entry->x_connected ||
34147c478bd9Sstevel@tonic-gate 				    cm_entry->x_doomed) {
34157c478bd9Sstevel@tonic-gate 					if (cm_entry->x_ordrel) {
34168ffff9fdSgt29601 						if (cm_entry->x_closing ==
34178ffff9fdSgt29601 						    TRUE) {
34187c478bd9Sstevel@tonic-gate 							/*
34198ffff9fdSgt29601 							 * The connection is
34208ffff9fdSgt29601 							 * obviously wedged due
34218ffff9fdSgt29601 							 * to a bug or problem
34228ffff9fdSgt29601 							 * with the transport.
34238ffff9fdSgt29601 							 * Mark it as dead.
34248ffff9fdSgt29601 							 * Otherwise we can
34258ffff9fdSgt29601 							 * leak connections.
34267c478bd9Sstevel@tonic-gate 							 */
34277c478bd9Sstevel@tonic-gate 							cm_entry->x_dead = TRUE;
34288ffff9fdSgt29601 							mutex_exit(
34298ffff9fdSgt29601 							    &connmgr_lock);
34307c478bd9Sstevel@tonic-gate 							have_connmgr_lock = 0;
34318ffff9fdSgt29601 							if (clnt_stop_idle !=
34328ffff9fdSgt29601 							    NULL)
34337c478bd9Sstevel@tonic-gate 							(*clnt_stop_idle)(q);
34347c478bd9Sstevel@tonic-gate 							break;
34357c478bd9Sstevel@tonic-gate 						}
34367c478bd9Sstevel@tonic-gate 						cm_entry->x_closing = TRUE;
34377c478bd9Sstevel@tonic-gate 						connmgr_sndrel(cm_entry);
34387c478bd9Sstevel@tonic-gate 						have_connmgr_lock = 0;
34397c478bd9Sstevel@tonic-gate 					} else {
34407c478bd9Sstevel@tonic-gate 						cm_entry->x_dead = TRUE;
34417c478bd9Sstevel@tonic-gate 						mutex_exit(&connmgr_lock);
34427c478bd9Sstevel@tonic-gate 						have_connmgr_lock = 0;
34437c478bd9Sstevel@tonic-gate 						if (clnt_stop_idle != NULL)
34447c478bd9Sstevel@tonic-gate 							(*clnt_stop_idle)(q);
34457c478bd9Sstevel@tonic-gate 					}
34467c478bd9Sstevel@tonic-gate 				} else {
34477c478bd9Sstevel@tonic-gate 					/*
34487c478bd9Sstevel@tonic-gate 					 * We don't mark the connection
34497c478bd9Sstevel@tonic-gate 					 * as dead, but we turn off the
34507c478bd9Sstevel@tonic-gate 					 * idle timer.
34517c478bd9Sstevel@tonic-gate 					 */
34527c478bd9Sstevel@tonic-gate 					mutex_exit(&connmgr_lock);
34537c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
34547c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
34557c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
34567c478bd9Sstevel@tonic-gate 					RPCLOG(1, "clnt_dispatch_notifyall:"
34577c478bd9Sstevel@tonic-gate 					    " ignoring timeout from rpcmod"
34587c478bd9Sstevel@tonic-gate 					    " (q %p) because we are not "
34597c478bd9Sstevel@tonic-gate 					    " connected\n", (void *)q);
34607c478bd9Sstevel@tonic-gate 				}
34617c478bd9Sstevel@tonic-gate 				break;
34627c478bd9Sstevel@tonic-gate 			case T_ORDREL_IND:
34637c478bd9Sstevel@tonic-gate 				/*
34647c478bd9Sstevel@tonic-gate 				 * If this entry is marked closing, then we are
34657c478bd9Sstevel@tonic-gate 				 * completing a close handshake, and the
34667c478bd9Sstevel@tonic-gate 				 * connection is dead.  Otherwise, the server is
34677c478bd9Sstevel@tonic-gate 				 * trying to close. Since the server will not
34687c478bd9Sstevel@tonic-gate 				 * be sending any more RPC replies, we abort
34697c478bd9Sstevel@tonic-gate 				 * the connection, including flushing
34707c478bd9Sstevel@tonic-gate 				 * any RPC requests that are in-transit.
347130f84f37SDai Ngo 				 * In either case, mark the entry as dead so
347230f84f37SDai Ngo 				 * that it can be closed by the connection
347330f84f37SDai Ngo 				 * manager's garbage collector.
34747c478bd9Sstevel@tonic-gate 				 */
34757c478bd9Sstevel@tonic-gate 				cm_entry->x_dead = TRUE;
347630f84f37SDai Ngo 				if (cm_entry->x_closing) {
34777c478bd9Sstevel@tonic-gate 					mutex_exit(&connmgr_lock);
34787c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
34797c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
34807c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
34817c478bd9Sstevel@tonic-gate 				} else {
34827c478bd9Sstevel@tonic-gate 					/*
34837c478bd9Sstevel@tonic-gate 					 * if we're getting a disconnect
34847c478bd9Sstevel@tonic-gate 					 * before we've finished our
34857c478bd9Sstevel@tonic-gate 					 * connect attempt, mark it for
34867c478bd9Sstevel@tonic-gate 					 * later processing
34877c478bd9Sstevel@tonic-gate 					 */
34887c478bd9Sstevel@tonic-gate 					if (cm_entry->x_thread)
34897c478bd9Sstevel@tonic-gate 						cm_entry->x_early_disc = TRUE;
34907c478bd9Sstevel@tonic-gate 					else
34917c478bd9Sstevel@tonic-gate 						cm_entry->x_connected = FALSE;
34927c478bd9Sstevel@tonic-gate 					cm_entry->x_waitdis = TRUE;
34937c478bd9Sstevel@tonic-gate 					connmgr_snddis(cm_entry);
34947c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
34957c478bd9Sstevel@tonic-gate 				}
34967c478bd9Sstevel@tonic-gate 				break;
34977c478bd9Sstevel@tonic-gate 
34987c478bd9Sstevel@tonic-gate 			case T_ERROR_ACK:
34997c478bd9Sstevel@tonic-gate 			case T_OK_ACK:
35007c478bd9Sstevel@tonic-gate 				cm_entry->x_waitdis = FALSE;
35017c478bd9Sstevel@tonic-gate 				cv_signal(&cm_entry->x_dis_cv);
35027c478bd9Sstevel@tonic-gate 				mutex_exit(&connmgr_lock);
35037c478bd9Sstevel@tonic-gate 				return;
35047c478bd9Sstevel@tonic-gate 
35057c478bd9Sstevel@tonic-gate 			case T_DISCON_REQ:
35067c478bd9Sstevel@tonic-gate 				if (cm_entry->x_thread)
35077c478bd9Sstevel@tonic-gate 					cm_entry->x_early_disc = TRUE;
35087c478bd9Sstevel@tonic-gate 				else
35097c478bd9Sstevel@tonic-gate 					cm_entry->x_connected = FALSE;
35107c478bd9Sstevel@tonic-gate 				cm_entry->x_waitdis = TRUE;
35117c478bd9Sstevel@tonic-gate 
35127c478bd9Sstevel@tonic-gate 				connmgr_snddis(cm_entry);
35137c478bd9Sstevel@tonic-gate 				have_connmgr_lock = 0;
35147c478bd9Sstevel@tonic-gate 				break;
35157c478bd9Sstevel@tonic-gate 
35167c478bd9Sstevel@tonic-gate 			case T_DISCON_IND:
35177c478bd9Sstevel@tonic-gate 			default:
35187c478bd9Sstevel@tonic-gate 				/*
35197c478bd9Sstevel@tonic-gate 				 * if we're getting a disconnect before
35207c478bd9Sstevel@tonic-gate 				 * we've finished our connect attempt,
35217c478bd9Sstevel@tonic-gate 				 * mark it for later processing
35227c478bd9Sstevel@tonic-gate 				 */
35237c478bd9Sstevel@tonic-gate 				if (cm_entry->x_closing) {
35247c478bd9Sstevel@tonic-gate 					cm_entry->x_dead = TRUE;
35257c478bd9Sstevel@tonic-gate 					mutex_exit(&connmgr_lock);
35267c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
35277c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
35287c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
35297c478bd9Sstevel@tonic-gate 				} else {
35307c478bd9Sstevel@tonic-gate 					if (cm_entry->x_thread) {
35317c478bd9Sstevel@tonic-gate 						cm_entry->x_early_disc = TRUE;
35327c478bd9Sstevel@tonic-gate 					} else {
35337c478bd9Sstevel@tonic-gate 						cm_entry->x_dead = TRUE;
35347c478bd9Sstevel@tonic-gate 						cm_entry->x_connected = FALSE;
35357c478bd9Sstevel@tonic-gate 					}
35367c478bd9Sstevel@tonic-gate 				}
35377c478bd9Sstevel@tonic-gate 				break;
35387c478bd9Sstevel@tonic-gate 			}
35397c478bd9Sstevel@tonic-gate 			break;
35407c478bd9Sstevel@tonic-gate 		}
35417c478bd9Sstevel@tonic-gate 	}
35427c478bd9Sstevel@tonic-gate 
35437c478bd9Sstevel@tonic-gate 	if (have_connmgr_lock)
35447c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
35457c478bd9Sstevel@tonic-gate 
35467c478bd9Sstevel@tonic-gate 	if (msg_type == T_ERROR_ACK || msg_type == T_OK_ACK) {
35477c478bd9Sstevel@tonic-gate 		RPCLOG(1, "clnt_dispatch_notifyall: (wq %p) could not find "
35487c478bd9Sstevel@tonic-gate 		    "connmgr entry for discon ack\n", (void *)q);
35497c478bd9Sstevel@tonic-gate 		return;
35507c478bd9Sstevel@tonic-gate 	}
35517c478bd9Sstevel@tonic-gate 
35527c478bd9Sstevel@tonic-gate 	/*
35537c478bd9Sstevel@tonic-gate 	 * Then kick all the clnt_pending calls out of their wait.  There
35547c478bd9Sstevel@tonic-gate 	 * should be no clnt_pending calls in the case of rpcmod's idle
35557c478bd9Sstevel@tonic-gate 	 * timer firing.
35567c478bd9Sstevel@tonic-gate 	 */
35577c478bd9Sstevel@tonic-gate 	for (i = 0; i < clnt_cots_hash_size; i++) {
35587c478bd9Sstevel@tonic-gate 		ctp = &cots_call_ht[i];
35597c478bd9Sstevel@tonic-gate 		mutex_enter(&ctp->ct_lock);
35607c478bd9Sstevel@tonic-gate 		for (e = ctp->ct_call_next;
35617c478bd9Sstevel@tonic-gate 		    e != (calllist_t *)ctp;
35627c478bd9Sstevel@tonic-gate 		    e = e->call_next) {
35637c478bd9Sstevel@tonic-gate 			if (e->call_wq == q && e->call_notified == FALSE) {
35647c478bd9Sstevel@tonic-gate 				RPCLOG(1,
35657c478bd9Sstevel@tonic-gate 				    "clnt_dispatch_notifyall for queue %p ",
35667c478bd9Sstevel@tonic-gate 				    (void *)q);
35677c478bd9Sstevel@tonic-gate 				RPCLOG(1, "aborting clnt_pending call %p\n",
35687c478bd9Sstevel@tonic-gate 				    (void *)e);
35697c478bd9Sstevel@tonic-gate 
35707c478bd9Sstevel@tonic-gate 				if (msg_type == T_DISCON_IND)
35717c478bd9Sstevel@tonic-gate 					e->call_reason = reason;
35727c478bd9Sstevel@tonic-gate 				e->call_notified = TRUE;
35737c478bd9Sstevel@tonic-gate 				e->call_status = RPC_XPRTFAILED;
35747c478bd9Sstevel@tonic-gate 				cv_signal(&e->call_cv);
35757c478bd9Sstevel@tonic-gate 			}
35767c478bd9Sstevel@tonic-gate 		}
35777c478bd9Sstevel@tonic-gate 		mutex_exit(&ctp->ct_lock);
35787c478bd9Sstevel@tonic-gate 	}
35797c478bd9Sstevel@tonic-gate 
35807c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
35817c478bd9Sstevel@tonic-gate 	for (e = clnt_pending; e; e = e->call_next) {
35827c478bd9Sstevel@tonic-gate 		/*
35837c478bd9Sstevel@tonic-gate 		 * Only signal those RPC handles that haven't been
35847c478bd9Sstevel@tonic-gate 		 * signalled yet. Otherwise we can get a bogus call_reason.
35857c478bd9Sstevel@tonic-gate 		 * This can happen if thread A is making a call over a
35867c478bd9Sstevel@tonic-gate 		 * connection. If the server is killed, it will cause
35877c478bd9Sstevel@tonic-gate 		 * reset, and reason will default to EIO as a result of
35887c478bd9Sstevel@tonic-gate 		 * a T_ORDREL_IND. Thread B then attempts to recreate
35897c478bd9Sstevel@tonic-gate 		 * the connection but gets a T_DISCON_IND. If we set the
35907c478bd9Sstevel@tonic-gate 		 * call_reason code for all threads, then if thread A
35917c478bd9Sstevel@tonic-gate 		 * hasn't been dispatched yet, it will get the wrong
35927c478bd9Sstevel@tonic-gate 		 * reason. The bogus call_reason can make it harder to
35937c478bd9Sstevel@tonic-gate 		 * discriminate between calls that fail because the
35947c478bd9Sstevel@tonic-gate 		 * connection attempt failed versus those where the call
35957c478bd9Sstevel@tonic-gate 		 * may have been executed on the server.
35967c478bd9Sstevel@tonic-gate 		 */
35977c478bd9Sstevel@tonic-gate 		if (e->call_wq == q && e->call_notified == FALSE) {
35987c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_dispatch_notifyall for queue %p ",
35997c478bd9Sstevel@tonic-gate 			    (void *)q);
36007c478bd9Sstevel@tonic-gate 			RPCLOG(1, " aborting clnt_pending call %p\n",
36017c478bd9Sstevel@tonic-gate 			    (void *)e);
36027c478bd9Sstevel@tonic-gate 
36037c478bd9Sstevel@tonic-gate 			if (msg_type == T_DISCON_IND)
36047c478bd9Sstevel@tonic-gate 				e->call_reason = reason;
36057c478bd9Sstevel@tonic-gate 			e->call_notified = TRUE;
36067c478bd9Sstevel@tonic-gate 			/*
36077c478bd9Sstevel@tonic-gate 			 * Let the caller timeout, else he will retry
36087c478bd9Sstevel@tonic-gate 			 * immediately.
36097c478bd9Sstevel@tonic-gate 			 */
36107c478bd9Sstevel@tonic-gate 			e->call_status = RPC_XPRTFAILED;
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 			/*
36137c478bd9Sstevel@tonic-gate 			 * We used to just signal those threads
36147c478bd9Sstevel@tonic-gate 			 * waiting for a connection, (call_xid = 0).
36157c478bd9Sstevel@tonic-gate 			 * That meant that threads waiting for a response
36167c478bd9Sstevel@tonic-gate 			 * waited till their timeout expired. This
36177c478bd9Sstevel@tonic-gate 			 * could be a long time if they've specified a
36187c478bd9Sstevel@tonic-gate 			 * maximum timeout. (2^31 - 1). So we
36197c478bd9Sstevel@tonic-gate 			 * Signal all threads now.
36207c478bd9Sstevel@tonic-gate 			 */
36217c478bd9Sstevel@tonic-gate 			cv_signal(&e->call_cv);
36227c478bd9Sstevel@tonic-gate 		}
36237c478bd9Sstevel@tonic-gate 	}
36247c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
36257c478bd9Sstevel@tonic-gate }
36267c478bd9Sstevel@tonic-gate 
36277c478bd9Sstevel@tonic-gate 
36287c478bd9Sstevel@tonic-gate /*ARGSUSED*/
36297c478bd9Sstevel@tonic-gate /*
36307c478bd9Sstevel@tonic-gate  * after resuming a system that's been suspended for longer than the
36317c478bd9Sstevel@tonic-gate  * NFS server's idle timeout (svc_idle_timeout for Solaris 2), rfscall()
36327c478bd9Sstevel@tonic-gate  * generates "NFS server X not responding" and "NFS server X ok" messages;
36337c478bd9Sstevel@tonic-gate  * here we reset inet connections to cause a re-connect and avoid those
36347c478bd9Sstevel@tonic-gate  * NFS messages.  see 4045054
36357c478bd9Sstevel@tonic-gate  */
36367c478bd9Sstevel@tonic-gate boolean_t
connmgr_cpr_reset(void * arg,int code)36377c478bd9Sstevel@tonic-gate connmgr_cpr_reset(void *arg, int code)
36387c478bd9Sstevel@tonic-gate {
36397c478bd9Sstevel@tonic-gate 	struct cm_xprt *cxp;
36407c478bd9Sstevel@tonic-gate 
36417c478bd9Sstevel@tonic-gate 	if (code == CB_CODE_CPR_CHKPT)
36427c478bd9Sstevel@tonic-gate 		return (B_TRUE);
36437c478bd9Sstevel@tonic-gate 
36447c478bd9Sstevel@tonic-gate 	if (mutex_tryenter(&connmgr_lock) == 0)
36457c478bd9Sstevel@tonic-gate 		return (B_FALSE);
36467c478bd9Sstevel@tonic-gate 	for (cxp = cm_hd; cxp; cxp = cxp->x_next) {
36477c478bd9Sstevel@tonic-gate 		if ((cxp->x_family == AF_INET || cxp->x_family == AF_INET6) &&
36487c478bd9Sstevel@tonic-gate 		    cxp->x_connected == TRUE) {
36497c478bd9Sstevel@tonic-gate 			if (cxp->x_thread)
36507c478bd9Sstevel@tonic-gate 				cxp->x_early_disc = TRUE;
36517c478bd9Sstevel@tonic-gate 			else
36527c478bd9Sstevel@tonic-gate 				cxp->x_connected = FALSE;
36537c478bd9Sstevel@tonic-gate 			cxp->x_needdis = TRUE;
36547c478bd9Sstevel@tonic-gate 		}
36557c478bd9Sstevel@tonic-gate 	}
36567c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
36577c478bd9Sstevel@tonic-gate 	return (B_TRUE);
36587c478bd9Sstevel@tonic-gate }
36597c478bd9Sstevel@tonic-gate 
36607c478bd9Sstevel@tonic-gate void
clnt_cots_stats_init(zoneid_t zoneid,struct rpc_cots_client ** statsp)36617c478bd9Sstevel@tonic-gate clnt_cots_stats_init(zoneid_t zoneid, struct rpc_cots_client **statsp)
36627c478bd9Sstevel@tonic-gate {
36637c478bd9Sstevel@tonic-gate 
36647c478bd9Sstevel@tonic-gate 	*statsp = (struct rpc_cots_client *)rpcstat_zone_init_common(zoneid,
36657c478bd9Sstevel@tonic-gate 	    "unix", "rpc_cots_client", (const kstat_named_t *)&cots_rcstat_tmpl,
36667c478bd9Sstevel@tonic-gate 	    sizeof (cots_rcstat_tmpl));
36677c478bd9Sstevel@tonic-gate }
36687c478bd9Sstevel@tonic-gate 
36697c478bd9Sstevel@tonic-gate void
clnt_cots_stats_fini(zoneid_t zoneid,struct rpc_cots_client ** statsp)36707c478bd9Sstevel@tonic-gate clnt_cots_stats_fini(zoneid_t zoneid, struct rpc_cots_client **statsp)
36717c478bd9Sstevel@tonic-gate {
36727c478bd9Sstevel@tonic-gate 	rpcstat_zone_fini_common(zoneid, "unix", "rpc_cots_client");
36737c478bd9Sstevel@tonic-gate 	kmem_free(*statsp, sizeof (cots_rcstat_tmpl));
36747c478bd9Sstevel@tonic-gate }
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate void
clnt_cots_init(void)36777c478bd9Sstevel@tonic-gate clnt_cots_init(void)
36787c478bd9Sstevel@tonic-gate {
36797c478bd9Sstevel@tonic-gate 	mutex_init(&connmgr_lock, NULL, MUTEX_DEFAULT, NULL);
36807c478bd9Sstevel@tonic-gate 	mutex_init(&clnt_pending_lock, NULL, MUTEX_DEFAULT, NULL);
36817c478bd9Sstevel@tonic-gate 
36827c478bd9Sstevel@tonic-gate 	if (clnt_cots_hash_size < DEFAULT_MIN_HASH_SIZE)
36837c478bd9Sstevel@tonic-gate 		clnt_cots_hash_size = DEFAULT_MIN_HASH_SIZE;
36847c478bd9Sstevel@tonic-gate 
36857c478bd9Sstevel@tonic-gate 	cots_call_ht = call_table_init(clnt_cots_hash_size);
36867c478bd9Sstevel@tonic-gate 	zone_key_create(&zone_cots_key, NULL, NULL, clnt_zone_destroy);
36877c478bd9Sstevel@tonic-gate }
36887c478bd9Sstevel@tonic-gate 
36897c478bd9Sstevel@tonic-gate void
clnt_cots_fini(void)36907c478bd9Sstevel@tonic-gate clnt_cots_fini(void)
36917c478bd9Sstevel@tonic-gate {
36927c478bd9Sstevel@tonic-gate 	(void) zone_key_delete(zone_cots_key);
36937c478bd9Sstevel@tonic-gate }
36947c478bd9Sstevel@tonic-gate 
36957c478bd9Sstevel@tonic-gate /*
36967c478bd9Sstevel@tonic-gate  * Wait for TPI ack, returns success only if expected ack is received
36977c478bd9Sstevel@tonic-gate  * within timeout period.
36987c478bd9Sstevel@tonic-gate  */
36997c478bd9Sstevel@tonic-gate 
37007c478bd9Sstevel@tonic-gate static int
waitforack(calllist_t * e,t_scalar_t ack_prim,const struct timeval * waitp,bool_t nosignal)37017c478bd9Sstevel@tonic-gate waitforack(calllist_t *e, t_scalar_t ack_prim, const struct timeval *waitp,
37027c478bd9Sstevel@tonic-gate     bool_t nosignal)
37037c478bd9Sstevel@tonic-gate {
37047c478bd9Sstevel@tonic-gate 	union T_primitives *tpr;
37057c478bd9Sstevel@tonic-gate 	clock_t timout;
37067c478bd9Sstevel@tonic-gate 	int cv_stat = 1;
37077c478bd9Sstevel@tonic-gate 
37087c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&clnt_pending_lock));
37097c478bd9Sstevel@tonic-gate 	while (e->call_reply == NULL) {
37107c478bd9Sstevel@tonic-gate 		if (waitp != NULL) {
37117c478bd9Sstevel@tonic-gate 			timout = waitp->tv_sec * drv_usectohz(MICROSEC) +
3712d3d50737SRafael Vanoni 			    drv_usectohz(waitp->tv_usec);
37137c478bd9Sstevel@tonic-gate 			if (nosignal)
3714d3d50737SRafael Vanoni 				cv_stat = cv_reltimedwait(&e->call_cv,
3715d3d50737SRafael Vanoni 				    &clnt_pending_lock, timout, TR_CLOCK_TICK);
37167c478bd9Sstevel@tonic-gate 			else
3717d3d50737SRafael Vanoni 				cv_stat = cv_reltimedwait_sig(&e->call_cv,
3718d3d50737SRafael Vanoni 				    &clnt_pending_lock, timout, TR_CLOCK_TICK);
37197c478bd9Sstevel@tonic-gate 		} else {
37207c478bd9Sstevel@tonic-gate 			if (nosignal)
37217c478bd9Sstevel@tonic-gate 				cv_wait(&e->call_cv, &clnt_pending_lock);
37227c478bd9Sstevel@tonic-gate 			else
37237c478bd9Sstevel@tonic-gate 				cv_stat = cv_wait_sig(&e->call_cv,
37247c478bd9Sstevel@tonic-gate 				    &clnt_pending_lock);
37257c478bd9Sstevel@tonic-gate 		}
37267c478bd9Sstevel@tonic-gate 		if (cv_stat == -1)
37277c478bd9Sstevel@tonic-gate 			return (ETIME);
37287c478bd9Sstevel@tonic-gate 		if (cv_stat == 0)
37297c478bd9Sstevel@tonic-gate 			return (EINTR);
37306ec2b7e3Sgt29601 		/*
37316ec2b7e3Sgt29601 		 * if we received an error from the server and we know a reply
37326ec2b7e3Sgt29601 		 * is not going to be sent, do not wait for the full timeout,
37336ec2b7e3Sgt29601 		 * return now.
37346ec2b7e3Sgt29601 		 */
37356ec2b7e3Sgt29601 		if (e->call_status == RPC_XPRTFAILED)
37366ec2b7e3Sgt29601 			return (e->call_reason);
37377c478bd9Sstevel@tonic-gate 	}
37387c478bd9Sstevel@tonic-gate 	tpr = (union T_primitives *)e->call_reply->b_rptr;
37397c478bd9Sstevel@tonic-gate 	if (tpr->type == ack_prim)
37407c478bd9Sstevel@tonic-gate 		return (0); /* Success */
37417c478bd9Sstevel@tonic-gate 
37427c478bd9Sstevel@tonic-gate 	if (tpr->type == T_ERROR_ACK) {
37437c478bd9Sstevel@tonic-gate 		if (tpr->error_ack.TLI_error == TSYSERR)
37447c478bd9Sstevel@tonic-gate 			return (tpr->error_ack.UNIX_error);
37457c478bd9Sstevel@tonic-gate 		else
37467c478bd9Sstevel@tonic-gate 			return (t_tlitosyserr(tpr->error_ack.TLI_error));
37477c478bd9Sstevel@tonic-gate 	}
37487c478bd9Sstevel@tonic-gate 
37497c478bd9Sstevel@tonic-gate 	return (EPROTO); /* unknown or unexpected primitive */
37507c478bd9Sstevel@tonic-gate }
3751