xref: /illumos-gate/usr/src/uts/common/rpc/clnt_cots.c (revision 6f914afd)
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  */
21cf98b944SMarcel Telka 
22cf98b944SMarcel Telka /*
23c16316e1SMarcel Telka  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
2448bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
254b6bffb4SDan McDonald  * Copyright 2019 Joyent, Inc.
26cf98b944SMarcel Telka  */
27cf98b944SMarcel Telka 
287c478bd9Sstevel@tonic-gate /*
29de8c4a14SErik Nordmark  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
307c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
357c478bd9Sstevel@tonic-gate  *		All Rights Reserved
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate /*
397c478bd9Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley 4.3 BSD
407c478bd9Sstevel@tonic-gate  * under license from the Regents of the University of California.
417c478bd9Sstevel@tonic-gate  */
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate  * Implements a kernel based, client side RPC over Connection Oriented
467c478bd9Sstevel@tonic-gate  * Transports (COTS).
477c478bd9Sstevel@tonic-gate  */
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate /*
507c478bd9Sstevel@tonic-gate  * Much of this file has been re-written to let NFS work better over slow
517c478bd9Sstevel@tonic-gate  * transports. A description follows.
527c478bd9Sstevel@tonic-gate  *
537c478bd9Sstevel@tonic-gate  * One of the annoying things about kRPC/COTS is that it will temporarily
547c478bd9Sstevel@tonic-gate  * create more than one connection between a client and server. This
557c478bd9Sstevel@tonic-gate  * happens because when a connection is made, the end-points entry in the
567c478bd9Sstevel@tonic-gate  * linked list of connections (headed by cm_hd), is removed so that other
577c478bd9Sstevel@tonic-gate  * threads don't mess with it. Went ahead and bit the bullet by keeping
587c478bd9Sstevel@tonic-gate  * the endpoint on the connection list and introducing state bits,
597c478bd9Sstevel@tonic-gate  * condition variables etc. to the connection entry data structure (struct
607c478bd9Sstevel@tonic-gate  * cm_xprt).
617c478bd9Sstevel@tonic-gate  *
627c478bd9Sstevel@tonic-gate  * Here is a summary of the changes to cm-xprt:
637c478bd9Sstevel@tonic-gate  *
647c478bd9Sstevel@tonic-gate  *	x_ctime is the timestamp of when the endpoint was last
657c478bd9Sstevel@tonic-gate  *	connected or disconnected. If an end-point is ever disconnected
667c478bd9Sstevel@tonic-gate  *	or re-connected, then any outstanding RPC request is presumed
677c478bd9Sstevel@tonic-gate  *	lost, telling clnt_cots_kcallit that it needs to re-send the
687c478bd9Sstevel@tonic-gate  *	request, not just wait for the original request's reply to
697c478bd9Sstevel@tonic-gate  *	arrive.
707c478bd9Sstevel@tonic-gate  *
717c478bd9Sstevel@tonic-gate  *	x_thread flag which tells us if a thread is doing a connection attempt.
727c478bd9Sstevel@tonic-gate  *
737c478bd9Sstevel@tonic-gate  *	x_waitdis flag which tells us we are waiting a disconnect ACK.
747c478bd9Sstevel@tonic-gate  *
757c478bd9Sstevel@tonic-gate  *	x_needdis flag which tells us we need to send a T_DISCONN_REQ
767c478bd9Sstevel@tonic-gate  *	to kill the connection.
777c478bd9Sstevel@tonic-gate  *
787c478bd9Sstevel@tonic-gate  *	x_needrel flag which tells us we need to send a T_ORDREL_REQ to
797c478bd9Sstevel@tonic-gate  *	gracefully close the connection.
807c478bd9Sstevel@tonic-gate  *
817c478bd9Sstevel@tonic-gate  *	#defined bitmasks for the all the b_* bits so that more
827c478bd9Sstevel@tonic-gate  *	efficient (and at times less clumsy) masks can be used to
837c478bd9Sstevel@tonic-gate  *	manipulated state in cases where multiple bits have to
847c478bd9Sstevel@tonic-gate  *	set/cleared/checked in the same critical section.
857c478bd9Sstevel@tonic-gate  *
867c478bd9Sstevel@tonic-gate  *	x_conn_cv and x_dis-_cv are new condition variables to let
877c478bd9Sstevel@tonic-gate  *	threads knows when the connection attempt is done, and to let
887c478bd9Sstevel@tonic-gate  *	the connecting thread know when the disconnect handshake is
897c478bd9Sstevel@tonic-gate  *	done.
907c478bd9Sstevel@tonic-gate  *
917c478bd9Sstevel@tonic-gate  * Added the CONN_HOLD() macro so that all reference holds have the same
927c478bd9Sstevel@tonic-gate  * look and feel.
937c478bd9Sstevel@tonic-gate  *
947c478bd9Sstevel@tonic-gate  * In the private (cku_private) portion of the client handle,
957c478bd9Sstevel@tonic-gate  *
967c478bd9Sstevel@tonic-gate  *	cku_flags replaces the cku_sent a boolean. cku_flags keeps
977c478bd9Sstevel@tonic-gate  *	track of whether a request as been sent, and whether the
987c478bd9Sstevel@tonic-gate  *	client's handles call record is on the dispatch list (so that
997c478bd9Sstevel@tonic-gate  *	the reply can be matched by XID to the right client handle).
1007c478bd9Sstevel@tonic-gate  *	The idea of CKU_ONQUEUE is that we can exit clnt_cots_kcallit()
1017c478bd9Sstevel@tonic-gate  *	and still have the response find the right client handle so
1027c478bd9Sstevel@tonic-gate  *	that the retry of CLNT_CALL() gets the result. Testing, found
1037c478bd9Sstevel@tonic-gate  *	situations where if the timeout was increased, performance
1047c478bd9Sstevel@tonic-gate  *	degraded. This was due to us hitting a window where the thread
1057c478bd9Sstevel@tonic-gate  *	was back in rfscall() (probably printing server not responding)
1067c478bd9Sstevel@tonic-gate  *	while the response came back but no place to put it.
1077c478bd9Sstevel@tonic-gate  *
1087c478bd9Sstevel@tonic-gate  *	cku_ctime is just a cache of x_ctime. If they match,
1097c478bd9Sstevel@tonic-gate  *	clnt_cots_kcallit() won't to send a retry (unless the maximum
1107c478bd9Sstevel@tonic-gate  *	receive count limit as been reached). If the don't match, then
1117c478bd9Sstevel@tonic-gate  *	we assume the request has been lost, and a retry of the request
1127c478bd9Sstevel@tonic-gate  *	is needed.
1137c478bd9Sstevel@tonic-gate  *
1147c478bd9Sstevel@tonic-gate  *	cku_recv_attempts counts the number of receive count attempts
1157c478bd9Sstevel@tonic-gate  *	after one try is sent on the wire.
1167c478bd9Sstevel@tonic-gate  *
1177c478bd9Sstevel@tonic-gate  * Added the clnt_delay() routine so that interruptible and
1187c478bd9Sstevel@tonic-gate  * noninterruptible delays are possible.
1197c478bd9Sstevel@tonic-gate  *
1207c478bd9Sstevel@tonic-gate  * CLNT_MIN_TIMEOUT has been bumped to 10 seconds from 3. This is used to
1217c478bd9Sstevel@tonic-gate  * control how long the client delays before returned after getting
1227c478bd9Sstevel@tonic-gate  * ECONNREFUSED. At 3 seconds, 8 client threads per mount really does bash
1237c478bd9Sstevel@tonic-gate  * a server that may be booting and not yet started nfsd.
1247c478bd9Sstevel@tonic-gate  *
1257c478bd9Sstevel@tonic-gate  * CLNT_MAXRECV_WITHOUT_RETRY is a new macro (value of 3) (with a tunable)
1267c478bd9Sstevel@tonic-gate  * Why don't we just wait forever (receive an infinite # of times)?
1277c478bd9Sstevel@tonic-gate  * Because the server may have rebooted. More insidious is that some
1287c478bd9Sstevel@tonic-gate  * servers (ours) will drop NFS/TCP requests in some cases. This is bad,
1297c478bd9Sstevel@tonic-gate  * but it is a reality.
1307c478bd9Sstevel@tonic-gate  *
1317c478bd9Sstevel@tonic-gate  * The case of a server doing orderly release really messes up the
1327c478bd9Sstevel@tonic-gate  * client's recovery, especially if the server's TCP implementation is
1337c478bd9Sstevel@tonic-gate  * buggy.  It was found was that the kRPC/COTS client was breaking some
1347c478bd9Sstevel@tonic-gate  * TPI rules, such as not waiting for the acknowledgement of a
1357c478bd9Sstevel@tonic-gate  * T_DISCON_REQ (hence the added case statements T_ERROR_ACK, T_OK_ACK and
1367c478bd9Sstevel@tonic-gate  * T_DISCON_REQ in clnt_dispatch_notifyall()).
1377c478bd9Sstevel@tonic-gate  *
1387c478bd9Sstevel@tonic-gate  * One of things that we've seen is that a kRPC TCP endpoint goes into
1397c478bd9Sstevel@tonic-gate  * TIMEWAIT and a thus a reconnect takes a long time to satisfy because
1407c478bd9Sstevel@tonic-gate  * that the TIMEWAIT state takes a while to finish.  If a server sends a
1417c478bd9Sstevel@tonic-gate  * T_ORDREL_IND, there is little point in an RPC client doing a
1427c478bd9Sstevel@tonic-gate  * T_ORDREL_REQ, because the RPC request isn't going to make it (the
1437c478bd9Sstevel@tonic-gate  * server is saying that it won't accept any more data). So kRPC was
1447c478bd9Sstevel@tonic-gate  * changed to send a T_DISCON_REQ when we get a T_ORDREL_IND. So now the
1457c478bd9Sstevel@tonic-gate  * connection skips the TIMEWAIT state and goes straight to a bound state
1467c478bd9Sstevel@tonic-gate  * that kRPC can quickly switch to connected.
1477c478bd9Sstevel@tonic-gate  *
1487c478bd9Sstevel@tonic-gate  * Code that issues TPI request must use waitforack() to wait for the
1497c478bd9Sstevel@tonic-gate  * corresponding ack (assuming there is one) in any future modifications.
1507c478bd9Sstevel@tonic-gate  * This works around problems that may be introduced by breaking TPI rules
1517c478bd9Sstevel@tonic-gate  * (by submitting new calls before earlier requests have been acked) in the
1527c478bd9Sstevel@tonic-gate  * case of a signal or other early return.  waitforack() depends on
1537c478bd9Sstevel@tonic-gate  * clnt_dispatch_notifyconn() to issue the wakeup when the ack
1547c478bd9Sstevel@tonic-gate  * arrives, so adding new TPI calls may require corresponding changes
1557c478bd9Sstevel@tonic-gate  * to clnt_dispatch_notifyconn(). Presently, the timeout period is based on
1567c478bd9Sstevel@tonic-gate  * CLNT_MIN_TIMEOUT which is 10 seconds. If you modify this value, be sure
1577c478bd9Sstevel@tonic-gate  * not to set it too low or TPI ACKS will be lost.
1587c478bd9Sstevel@tonic-gate  */
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate #include <sys/param.h>
1617c478bd9Sstevel@tonic-gate #include <sys/types.h>
1627c478bd9Sstevel@tonic-gate #include <sys/user.h>
1637c478bd9Sstevel@tonic-gate #include <sys/systm.h>
1647c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
1657c478bd9Sstevel@tonic-gate #include <sys/proc.h>
1667c478bd9Sstevel@tonic-gate #include <sys/socket.h>
1677c478bd9Sstevel@tonic-gate #include <sys/file.h>
1687c478bd9Sstevel@tonic-gate #include <sys/stream.h>
1697c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
1707c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
1717c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
1727c478bd9Sstevel@tonic-gate #include <sys/timod.h>
1737c478bd9Sstevel@tonic-gate #include <sys/tiuser.h>
1747c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
1757c478bd9Sstevel@tonic-gate #include <sys/t_kuser.h>
1767c478bd9Sstevel@tonic-gate #include <sys/fcntl.h>
1777c478bd9Sstevel@tonic-gate #include <sys/errno.h>
1787c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
1797c478bd9Sstevel@tonic-gate #include <sys/debug.h>
1807c478bd9Sstevel@tonic-gate #include <sys/systm.h>
1817c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
1827c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
1837c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
1847c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
1857c478bd9Sstevel@tonic-gate #include <sys/time.h>
1867c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
1877c478bd9Sstevel@tonic-gate #include <sys/callb.h>
1887c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
1897c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
190125a8fd9SSiddheshwar Mahesh #include <sys/sdt.h>
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate #include <netinet/in.h>
1937c478bd9Sstevel@tonic-gate #include <netinet/tcp.h>
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate #include <rpc/types.h>
1967c478bd9Sstevel@tonic-gate #include <rpc/xdr.h>
1977c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
1987c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
1997c478bd9Sstevel@tonic-gate #include <rpc/rpc_msg.h>
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate #define	COTS_DEFAULT_ALLOCSIZE	2048
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate #define	WIRE_HDR_SIZE	20	/* serialized call header, sans proc number */
2047c478bd9Sstevel@tonic-gate #define	MSG_OFFSET	128	/* offset of call into the mblk */
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate const char *kinet_ntop6(uchar_t *, char *, size_t);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate static int	clnt_cots_ksettimers(CLIENT *, struct rpc_timers *,
2097c478bd9Sstevel@tonic-gate     struct rpc_timers *, int, void(*)(int, int, caddr_t), caddr_t, uint32_t);
2107c478bd9Sstevel@tonic-gate static enum clnt_stat	clnt_cots_kcallit(CLIENT *, rpcproc_t, xdrproc_t,
2117c478bd9Sstevel@tonic-gate     caddr_t, xdrproc_t, caddr_t, struct timeval);
2127c478bd9Sstevel@tonic-gate static void	clnt_cots_kabort(CLIENT *);
2137c478bd9Sstevel@tonic-gate static void	clnt_cots_kerror(CLIENT *, struct rpc_err *);
2147c478bd9Sstevel@tonic-gate static bool_t	clnt_cots_kfreeres(CLIENT *, xdrproc_t, caddr_t);
2157c478bd9Sstevel@tonic-gate static void	clnt_cots_kdestroy(CLIENT *);
2167c478bd9Sstevel@tonic-gate static bool_t	clnt_cots_kcontrol(CLIENT *, int, char *);
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate /* List of transports managed by the connection manager. */
2207c478bd9Sstevel@tonic-gate struct cm_xprt {
2217c478bd9Sstevel@tonic-gate 	TIUSER		*x_tiptr;	/* transport handle */
2227c478bd9Sstevel@tonic-gate 	queue_t		*x_wq;		/* send queue */
2237c478bd9Sstevel@tonic-gate 	clock_t		x_time;		/* last time we handed this xprt out */
2247c478bd9Sstevel@tonic-gate 	clock_t		x_ctime;	/* time we went to CONNECTED */
2257c478bd9Sstevel@tonic-gate 	int		x_tidu_size;    /* TIDU size of this transport */
2267c478bd9Sstevel@tonic-gate 	union {
2277c478bd9Sstevel@tonic-gate 	    struct {
2287c478bd9Sstevel@tonic-gate 		unsigned int
2297c478bd9Sstevel@tonic-gate #ifdef	_BIT_FIELDS_HTOL
2307c478bd9Sstevel@tonic-gate 		b_closing:	1,	/* we've sent a ord rel on this conn */
2317c478bd9Sstevel@tonic-gate 		b_dead:		1,	/* transport is closed or disconn */
2327c478bd9Sstevel@tonic-gate 		b_doomed:	1,	/* too many conns, let this go idle */
2337c478bd9Sstevel@tonic-gate 		b_connected:	1,	/* this connection is connected */
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 		b_ordrel:	1,	/* do an orderly release? */
2367c478bd9Sstevel@tonic-gate 		b_thread:	1,	/* thread doing connect */
2377c478bd9Sstevel@tonic-gate 		b_waitdis:	1,	/* waiting for disconnect ACK */
2387c478bd9Sstevel@tonic-gate 		b_needdis:	1,	/* need T_DISCON_REQ */
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 		b_needrel:	1,	/* need T_ORDREL_REQ */
2417c478bd9Sstevel@tonic-gate 		b_early_disc:	1,	/* got a T_ORDREL_IND or T_DISCON_IND */
2427c478bd9Sstevel@tonic-gate 					/* disconnect during connect */
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 		b_pad:		22;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate #endif
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate #ifdef	_BIT_FIELDS_LTOH
2497c478bd9Sstevel@tonic-gate 		b_pad:		22,
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 		b_early_disc:	1,	/* got a T_ORDREL_IND or T_DISCON_IND */
2527c478bd9Sstevel@tonic-gate 					/* disconnect during connect */
2537c478bd9Sstevel@tonic-gate 		b_needrel:	1,	/* need T_ORDREL_REQ */
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 		b_needdis:	1,	/* need T_DISCON_REQ */
2567c478bd9Sstevel@tonic-gate 		b_waitdis:	1,	/* waiting for disconnect ACK */
2577c478bd9Sstevel@tonic-gate 		b_thread:	1,	/* thread doing connect */
2587c478bd9Sstevel@tonic-gate 		b_ordrel:	1,	/* do an orderly release? */
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 		b_connected:	1,	/* this connection is connected */
2617c478bd9Sstevel@tonic-gate 		b_doomed:	1,	/* too many conns, let this go idle */
2627c478bd9Sstevel@tonic-gate 		b_dead:		1,	/* transport is closed or disconn */
2637c478bd9Sstevel@tonic-gate 		b_closing:	1;	/* we've sent a ord rel on this conn */
2647c478bd9Sstevel@tonic-gate #endif
2657c478bd9Sstevel@tonic-gate 	    } bit;	    unsigned int word;
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate #define	x_closing	x_state.bit.b_closing
2687c478bd9Sstevel@tonic-gate #define	x_dead		x_state.bit.b_dead
2697c478bd9Sstevel@tonic-gate #define	x_doomed	x_state.bit.b_doomed
2707c478bd9Sstevel@tonic-gate #define	x_connected	x_state.bit.b_connected
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate #define	x_ordrel	x_state.bit.b_ordrel
2737c478bd9Sstevel@tonic-gate #define	x_thread	x_state.bit.b_thread
2747c478bd9Sstevel@tonic-gate #define	x_waitdis	x_state.bit.b_waitdis
2757c478bd9Sstevel@tonic-gate #define	x_needdis	x_state.bit.b_needdis
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate #define	x_needrel	x_state.bit.b_needrel
2787c478bd9Sstevel@tonic-gate #define	x_early_disc    x_state.bit.b_early_disc
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate #define	x_state_flags	x_state.word
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate #define	X_CLOSING	0x80000000
2837c478bd9Sstevel@tonic-gate #define	X_DEAD		0x40000000
2847c478bd9Sstevel@tonic-gate #define	X_DOOMED	0x20000000
2857c478bd9Sstevel@tonic-gate #define	X_CONNECTED	0x10000000
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate #define	X_ORDREL	0x08000000
2887c478bd9Sstevel@tonic-gate #define	X_THREAD	0x04000000
2897c478bd9Sstevel@tonic-gate #define	X_WAITDIS	0x02000000
2907c478bd9Sstevel@tonic-gate #define	X_NEEDDIS	0x01000000
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate #define	X_NEEDREL	0x00800000
2937c478bd9Sstevel@tonic-gate #define	X_EARLYDISC	0x00400000
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate #define	X_BADSTATES	(X_CLOSING | X_DEAD | X_DOOMED)
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	}		x_state;
2987c478bd9Sstevel@tonic-gate 	int		x_ref;		/* number of users of this xprt */
2997c478bd9Sstevel@tonic-gate 	int		x_family;	/* address family of transport */
3007c478bd9Sstevel@tonic-gate 	dev_t		x_rdev;		/* device number of transport */
3017c478bd9Sstevel@tonic-gate 	struct cm_xprt	*x_next;
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	struct netbuf	x_server;	/* destination address */
3047c478bd9Sstevel@tonic-gate 	struct netbuf	x_src;		/* src address (for retries) */
3057c478bd9Sstevel@tonic-gate 	kmutex_t	x_lock;		/* lock on this entry */
3067c478bd9Sstevel@tonic-gate 	kcondvar_t	x_cv;		/* to signal when can be closed */
3077c478bd9Sstevel@tonic-gate 	kcondvar_t	x_conn_cv;	/* to signal when connection attempt */
3087c478bd9Sstevel@tonic-gate 					/* is complete */
3097c478bd9Sstevel@tonic-gate 	kstat_t		*x_ksp;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	kcondvar_t	x_dis_cv;	/* to signal when disconnect attempt */
3127c478bd9Sstevel@tonic-gate 					/* is complete */
3137c478bd9Sstevel@tonic-gate 	zoneid_t	x_zoneid;	/* zone this xprt belongs to */
3147c478bd9Sstevel@tonic-gate };
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate typedef struct cm_kstat_xprt {
3177c478bd9Sstevel@tonic-gate 	kstat_named_t	x_wq;
3187c478bd9Sstevel@tonic-gate 	kstat_named_t	x_server;
3197c478bd9Sstevel@tonic-gate 	kstat_named_t	x_family;
3207c478bd9Sstevel@tonic-gate 	kstat_named_t	x_rdev;
3217c478bd9Sstevel@tonic-gate 	kstat_named_t	x_time;
3227c478bd9Sstevel@tonic-gate 	kstat_named_t	x_state;
3237c478bd9Sstevel@tonic-gate 	kstat_named_t	x_ref;
3247c478bd9Sstevel@tonic-gate 	kstat_named_t	x_port;
3257c478bd9Sstevel@tonic-gate } cm_kstat_xprt_t;
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate static cm_kstat_xprt_t cm_kstat_template = {
3287c478bd9Sstevel@tonic-gate 	{ "write_queue", KSTAT_DATA_UINT32 },
3297c478bd9Sstevel@tonic-gate 	{ "server",	KSTAT_DATA_STRING },
3307c478bd9Sstevel@tonic-gate 	{ "addr_family", KSTAT_DATA_UINT32 },
3317c478bd9Sstevel@tonic-gate 	{ "device",	KSTAT_DATA_UINT32 },
3327c478bd9Sstevel@tonic-gate 	{ "time_stamp",	KSTAT_DATA_UINT32 },
3337c478bd9Sstevel@tonic-gate 	{ "status",	KSTAT_DATA_UINT32 },
3347c478bd9Sstevel@tonic-gate 	{ "ref_count",	KSTAT_DATA_INT32 },
3357c478bd9Sstevel@tonic-gate 	{ "port",	KSTAT_DATA_UINT32 },
3367c478bd9Sstevel@tonic-gate };
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate /*
3397c478bd9Sstevel@tonic-gate  * The inverse of this is connmgr_release().
3407c478bd9Sstevel@tonic-gate  */
3417c478bd9Sstevel@tonic-gate #define	CONN_HOLD(Cm_entry)	{\
3427c478bd9Sstevel@tonic-gate 	mutex_enter(&(Cm_entry)->x_lock);	\
3437c478bd9Sstevel@tonic-gate 	(Cm_entry)->x_ref++;	\
3447c478bd9Sstevel@tonic-gate 	mutex_exit(&(Cm_entry)->x_lock);	\
3457c478bd9Sstevel@tonic-gate }
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate /*
3497c478bd9Sstevel@tonic-gate  * Private data per rpc handle.  This structure is allocated by
3507c478bd9Sstevel@tonic-gate  * clnt_cots_kcreate, and freed by clnt_cots_kdestroy.
3517c478bd9Sstevel@tonic-gate  */
3527c478bd9Sstevel@tonic-gate typedef struct cku_private_s {
3537c478bd9Sstevel@tonic-gate 	CLIENT			cku_client;	/* client handle */
3547c478bd9Sstevel@tonic-gate 	calllist_t		cku_call;	/* for dispatching calls */
3557c478bd9Sstevel@tonic-gate 	struct rpc_err		cku_err;	/* error status */
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	struct netbuf		cku_srcaddr;	/* source address for retries */
3587c478bd9Sstevel@tonic-gate 	int			cku_addrfmly;  /* for binding port */
3597c478bd9Sstevel@tonic-gate 	struct netbuf		cku_addr;	/* remote address */
3607c478bd9Sstevel@tonic-gate 	dev_t			cku_device;	/* device to use */
3617c478bd9Sstevel@tonic-gate 	uint_t			cku_flags;
3627c478bd9Sstevel@tonic-gate #define	CKU_ONQUEUE		0x1
3637c478bd9Sstevel@tonic-gate #define	CKU_SENT		0x2
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	bool_t			cku_progress;	/* for CLSET_PROGRESS */
3667c478bd9Sstevel@tonic-gate 	uint32_t		cku_xid;	/* current XID */
3677c478bd9Sstevel@tonic-gate 	clock_t			cku_ctime;	/* time stamp of when */
3687c478bd9Sstevel@tonic-gate 						/* connection was created */
3697c478bd9Sstevel@tonic-gate 	uint_t			cku_recv_attempts;
3707c478bd9Sstevel@tonic-gate 	XDR			cku_outxdr;	/* xdr routine for output */
3717c478bd9Sstevel@tonic-gate 	XDR			cku_inxdr;	/* xdr routine for input */
3727c478bd9Sstevel@tonic-gate 	char			cku_rpchdr[WIRE_HDR_SIZE + 4];
3737c478bd9Sstevel@tonic-gate 						/* pre-serialized rpc header */
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	uint_t			cku_outbuflen;	/* default output mblk length */
3767c478bd9Sstevel@tonic-gate 	struct cred		*cku_cred;	/* credentials */
3777c478bd9Sstevel@tonic-gate 	bool_t			cku_nodelayonerr;
3787c478bd9Sstevel@tonic-gate 						/* for CLSET_NODELAYONERR */
3797c478bd9Sstevel@tonic-gate 	int			cku_useresvport; /* Use reserved port */
3807c478bd9Sstevel@tonic-gate 	struct rpc_cots_client	*cku_stats;	/* stats for zone */
3817c478bd9Sstevel@tonic-gate } cku_private_t;
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate static struct cm_xprt *connmgr_wrapconnect(struct cm_xprt *,
3847c478bd9Sstevel@tonic-gate 	const struct timeval *, struct netbuf *, int, struct netbuf *,
385de8c4a14SErik Nordmark 	struct rpc_err *, bool_t, bool_t, cred_t *);
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate static bool_t	connmgr_connect(struct cm_xprt *, queue_t *, struct netbuf *,
3887c478bd9Sstevel@tonic-gate 				int, calllist_t *, int *, bool_t reconnect,
389de8c4a14SErik Nordmark 				const struct timeval *, bool_t, cred_t *);
3907c478bd9Sstevel@tonic-gate 
3912081ac19SDai Ngo static void	*connmgr_opt_getoff(mblk_t *mp, t_uscalar_t offset,
3922081ac19SDai Ngo 				t_uscalar_t length, uint_t align_size);
3932081ac19SDai Ngo static bool_t	connmgr_setbufsz(calllist_t *e, queue_t *wq, cred_t *cr);
3942081ac19SDai Ngo static bool_t	connmgr_getopt_int(queue_t *wq, int level, int name, int *val,
3952081ac19SDai Ngo 				calllist_t *e, cred_t *cr);
3962081ac19SDai Ngo static bool_t	connmgr_setopt_int(queue_t *wq, int level, int name, int val,
3972081ac19SDai Ngo 				calllist_t *e, cred_t *cr);
398de8c4a14SErik Nordmark static bool_t	connmgr_setopt(queue_t *, int, int, calllist_t *, cred_t *cr);
3997c478bd9Sstevel@tonic-gate static void	connmgr_sndrel(struct cm_xprt *);
4007c478bd9Sstevel@tonic-gate static void	connmgr_snddis(struct cm_xprt *);
4017c478bd9Sstevel@tonic-gate static void	connmgr_close(struct cm_xprt *);
4027c478bd9Sstevel@tonic-gate static void	connmgr_release(struct cm_xprt *);
4037c478bd9Sstevel@tonic-gate static struct cm_xprt *connmgr_wrapget(struct netbuf *, const struct timeval *,
4047c478bd9Sstevel@tonic-gate 	cku_private_t *);
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate static struct cm_xprt *connmgr_get(struct netbuf *, const struct timeval *,
4077c478bd9Sstevel@tonic-gate 	struct netbuf *, int, struct netbuf *, struct rpc_err *, dev_t,
408de8c4a14SErik Nordmark 	bool_t, int, cred_t *);
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate static void connmgr_cancelconn(struct cm_xprt *);
4117c478bd9Sstevel@tonic-gate static enum clnt_stat connmgr_cwait(struct cm_xprt *, const struct timeval *,
4127c478bd9Sstevel@tonic-gate 	bool_t);
4137c478bd9Sstevel@tonic-gate static void connmgr_dis_and_wait(struct cm_xprt *);
4147c478bd9Sstevel@tonic-gate 
415125a8fd9SSiddheshwar Mahesh static int	clnt_dispatch_send(queue_t *, mblk_t *, calllist_t *, uint_t,
4167c478bd9Sstevel@tonic-gate 					uint_t);
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate static int clnt_delay(clock_t, bool_t);
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate static int waitforack(calllist_t *, t_scalar_t, const struct timeval *, bool_t);
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate /*
4237c478bd9Sstevel@tonic-gate  * Operations vector for TCP/IP based RPC
4247c478bd9Sstevel@tonic-gate  */
4257c478bd9Sstevel@tonic-gate static struct clnt_ops tcp_ops = {
4267c478bd9Sstevel@tonic-gate 	clnt_cots_kcallit,	/* do rpc call */
4277c478bd9Sstevel@tonic-gate 	clnt_cots_kabort,	/* abort call */
4287c478bd9Sstevel@tonic-gate 	clnt_cots_kerror,	/* return error status */
4297c478bd9Sstevel@tonic-gate 	clnt_cots_kfreeres,	/* free results */
4307c478bd9Sstevel@tonic-gate 	clnt_cots_kdestroy,	/* destroy rpc handle */
4317c478bd9Sstevel@tonic-gate 	clnt_cots_kcontrol,	/* the ioctl() of rpc */
4327c478bd9Sstevel@tonic-gate 	clnt_cots_ksettimers,	/* set retry timers */
4337c478bd9Sstevel@tonic-gate };
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate static int rpc_kstat_instance = 0;  /* keeps the current instance */
4367c478bd9Sstevel@tonic-gate 				/* number for the next kstat_create */
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate static struct cm_xprt *cm_hd = NULL;
4397c478bd9Sstevel@tonic-gate static kmutex_t connmgr_lock;	/* for connection mngr's list of transports */
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate extern kmutex_t clnt_max_msg_lock;
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate static calllist_t *clnt_pending = NULL;
4447c478bd9Sstevel@tonic-gate extern kmutex_t clnt_pending_lock;
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate static int clnt_cots_hash_size = DEFAULT_HASH_SIZE;
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate static call_table_t *cots_call_ht;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate static const struct rpc_cots_client {
4517c478bd9Sstevel@tonic-gate 	kstat_named_t	rccalls;
4527c478bd9Sstevel@tonic-gate 	kstat_named_t	rcbadcalls;
4537c478bd9Sstevel@tonic-gate 	kstat_named_t	rcbadxids;
4547c478bd9Sstevel@tonic-gate 	kstat_named_t	rctimeouts;
4557c478bd9Sstevel@tonic-gate 	kstat_named_t	rcnewcreds;
4567c478bd9Sstevel@tonic-gate 	kstat_named_t	rcbadverfs;
4577c478bd9Sstevel@tonic-gate 	kstat_named_t	rctimers;
4587c478bd9Sstevel@tonic-gate 	kstat_named_t	rccantconn;
4597c478bd9Sstevel@tonic-gate 	kstat_named_t	rcnomem;
4607c478bd9Sstevel@tonic-gate 	kstat_named_t	rcintrs;
4617c478bd9Sstevel@tonic-gate } cots_rcstat_tmpl = {
4627c478bd9Sstevel@tonic-gate 	{ "calls",	KSTAT_DATA_UINT64 },
4637c478bd9Sstevel@tonic-gate 	{ "badcalls",	KSTAT_DATA_UINT64 },
4647c478bd9Sstevel@tonic-gate 	{ "badxids",	KSTAT_DATA_UINT64 },
4657c478bd9Sstevel@tonic-gate 	{ "timeouts",	KSTAT_DATA_UINT64 },
4667c478bd9Sstevel@tonic-gate 	{ "newcreds",	KSTAT_DATA_UINT64 },
4677c478bd9Sstevel@tonic-gate 	{ "badverfs",	KSTAT_DATA_UINT64 },
4687c478bd9Sstevel@tonic-gate 	{ "timers",	KSTAT_DATA_UINT64 },
4697c478bd9Sstevel@tonic-gate 	{ "cantconn",	KSTAT_DATA_UINT64 },
4707c478bd9Sstevel@tonic-gate 	{ "nomem",	KSTAT_DATA_UINT64 },
4717c478bd9Sstevel@tonic-gate 	{ "interrupts", KSTAT_DATA_UINT64 }
4727c478bd9Sstevel@tonic-gate };
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate #define	COTSRCSTAT_INCR(p, x)	\
4751a5e258fSJosef 'Jeff' Sipek 	atomic_inc_64(&(p)->x.value.ui64)
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate #define	CLNT_MAX_CONNS	1	/* concurrent connections between clnt/srvr */
47881dbf0b5SDai Ngo int clnt_max_conns = CLNT_MAX_CONNS;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate #define	CLNT_MIN_TIMEOUT	10	/* seconds to wait after we get a */
4817c478bd9Sstevel@tonic-gate 					/* connection reset */
4827c478bd9Sstevel@tonic-gate #define	CLNT_MIN_CONNTIMEOUT	5	/* seconds to wait for a connection */
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 
48581dbf0b5SDai Ngo int clnt_cots_min_tout = CLNT_MIN_TIMEOUT;
48681dbf0b5SDai Ngo int clnt_cots_min_conntout = CLNT_MIN_CONNTIMEOUT;
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate /*
4897c478bd9Sstevel@tonic-gate  * Limit the number of times we will attempt to receive a reply without
4907c478bd9Sstevel@tonic-gate  * re-sending a response.
4917c478bd9Sstevel@tonic-gate  */
4927c478bd9Sstevel@tonic-gate #define	CLNT_MAXRECV_WITHOUT_RETRY	3
49381dbf0b5SDai Ngo uint_t clnt_cots_maxrecv	= CLNT_MAXRECV_WITHOUT_RETRY;
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate uint_t *clnt_max_msg_sizep;
4967c478bd9Sstevel@tonic-gate void (*clnt_stop_idle)(queue_t *wq);
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate #define	ptoh(p)		(&((p)->cku_client))
4997c478bd9Sstevel@tonic-gate #define	htop(h)		((cku_private_t *)((h)->cl_private))
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate /*
5027c478bd9Sstevel@tonic-gate  * Times to retry
5037c478bd9Sstevel@tonic-gate  */
5047c478bd9Sstevel@tonic-gate #define	REFRESHES	2	/* authentication refreshes */
5057c478bd9Sstevel@tonic-gate 
5065bd9f8f1Srg137905 /*
5075bd9f8f1Srg137905  * The following is used to determine the global default behavior for
5085bd9f8f1Srg137905  * COTS when binding to a local port.
5095bd9f8f1Srg137905  *
5105bd9f8f1Srg137905  * If the value is set to 1 the default will be to select a reserved
5115bd9f8f1Srg137905  * (aka privileged) port, if the value is zero the default will be to
5125bd9f8f1Srg137905  * use non-reserved ports.  Users of kRPC may override this by using
5135bd9f8f1Srg137905  * CLNT_CONTROL() and CLSET_BINDRESVPORT.
5145bd9f8f1Srg137905  */
51581dbf0b5SDai Ngo int clnt_cots_do_bindresvport = 1;
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate static zone_key_t zone_cots_key;
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate /*
5202081ac19SDai Ngo  * Defaults TCP send and receive buffer size for RPC connections.
5212081ac19SDai Ngo  * These values can be tuned by /etc/system.
5222081ac19SDai Ngo  */
5232081ac19SDai Ngo int rpc_send_bufsz = 1024*1024;
5242081ac19SDai Ngo int rpc_recv_bufsz = 1024*1024;
5252081ac19SDai Ngo /*
5262081ac19SDai Ngo  * To use system-wide default for TCP send and receive buffer size,
5272081ac19SDai Ngo  * use /etc/system to set rpc_default_tcp_bufsz to 1:
5282081ac19SDai Ngo  *
5292081ac19SDai Ngo  * set rpcmod:rpc_default_tcp_bufsz=1
5302081ac19SDai Ngo  */
5312081ac19SDai Ngo int rpc_default_tcp_bufsz = 0;
5322081ac19SDai Ngo 
5332081ac19SDai Ngo /*
5347c478bd9Sstevel@tonic-gate  * We need to do this after all kernel threads in the zone have exited.
5357c478bd9Sstevel@tonic-gate  */
5367c478bd9Sstevel@tonic-gate /* ARGSUSED */
5377c478bd9Sstevel@tonic-gate static void
clnt_zone_destroy(zoneid_t zoneid,void * unused)5387c478bd9Sstevel@tonic-gate clnt_zone_destroy(zoneid_t zoneid, void *unused)
5397c478bd9Sstevel@tonic-gate {
5407c478bd9Sstevel@tonic-gate 	struct cm_xprt **cmp;
5417c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
5427c478bd9Sstevel@tonic-gate 	struct cm_xprt *freelist = NULL;
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
5457c478bd9Sstevel@tonic-gate 	cmp = &cm_hd;
5467c478bd9Sstevel@tonic-gate 	while ((cm_entry = *cmp) != NULL) {
5477c478bd9Sstevel@tonic-gate 		if (cm_entry->x_zoneid == zoneid) {
5487c478bd9Sstevel@tonic-gate 			*cmp = cm_entry->x_next;
5497c478bd9Sstevel@tonic-gate 			cm_entry->x_next = freelist;
5507c478bd9Sstevel@tonic-gate 			freelist = cm_entry;
5517c478bd9Sstevel@tonic-gate 		} else {
5527c478bd9Sstevel@tonic-gate 			cmp = &cm_entry->x_next;
5537c478bd9Sstevel@tonic-gate 		}
5547c478bd9Sstevel@tonic-gate 	}
5557c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
5567c478bd9Sstevel@tonic-gate 	while ((cm_entry = freelist) != NULL) {
5577c478bd9Sstevel@tonic-gate 		freelist = cm_entry->x_next;
5587c478bd9Sstevel@tonic-gate 		connmgr_close(cm_entry);
5597c478bd9Sstevel@tonic-gate 	}
5607c478bd9Sstevel@tonic-gate }
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@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)5637c478bd9Sstevel@tonic-gate clnt_cots_kcreate(dev_t dev, struct netbuf *addr, int family, rpcprog_t prog,
5647c478bd9Sstevel@tonic-gate     rpcvers_t vers, uint_t max_msgsize, cred_t *cred, CLIENT **ncl)
5657c478bd9Sstevel@tonic-gate {
5667c478bd9Sstevel@tonic-gate 	CLIENT *h;
5677c478bd9Sstevel@tonic-gate 	cku_private_t *p;
5687c478bd9Sstevel@tonic-gate 	struct rpc_msg call_msg;
5697c478bd9Sstevel@tonic-gate 	struct rpcstat *rpcstat;
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_cots_kcreate: prog %u\n", prog);
5727c478bd9Sstevel@tonic-gate 
573108322fbScarlsonj 	rpcstat = zone_getspecific(rpcstat_zone_key, rpc_zone());
5747c478bd9Sstevel@tonic-gate 	ASSERT(rpcstat != NULL);
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	/* Allocate and intialize the client handle. */
5777c478bd9Sstevel@tonic-gate 	p = kmem_zalloc(sizeof (*p), KM_SLEEP);
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	h = ptoh(p);
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 	h->cl_private = (caddr_t)p;
5827c478bd9Sstevel@tonic-gate 	h->cl_auth = authkern_create();
5837c478bd9Sstevel@tonic-gate 	h->cl_ops = &tcp_ops;
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	cv_init(&p->cku_call.call_cv, NULL, CV_DEFAULT, NULL);
5867c478bd9Sstevel@tonic-gate 	mutex_init(&p->cku_call.call_lock, NULL, MUTEX_DEFAULT, NULL);
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/*
5897c478bd9Sstevel@tonic-gate 	 * If the current sanity check size in rpcmod is smaller
5907c478bd9Sstevel@tonic-gate 	 * than the size needed, then increase the sanity check.
5917c478bd9Sstevel@tonic-gate 	 */
5927c478bd9Sstevel@tonic-gate 	if (max_msgsize != 0 && clnt_max_msg_sizep != NULL &&
5937c478bd9Sstevel@tonic-gate 	    max_msgsize > *clnt_max_msg_sizep) {
5947c478bd9Sstevel@tonic-gate 		mutex_enter(&clnt_max_msg_lock);
5957c478bd9Sstevel@tonic-gate 		if (max_msgsize > *clnt_max_msg_sizep)
5967c478bd9Sstevel@tonic-gate 			*clnt_max_msg_sizep = max_msgsize;
5977c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_max_msg_lock);
5987c478bd9Sstevel@tonic-gate 	}
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	p->cku_outbuflen = COTS_DEFAULT_ALLOCSIZE;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	/* Preserialize the call message header */
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	call_msg.rm_xid = 0;
6057c478bd9Sstevel@tonic-gate 	call_msg.rm_direction = CALL;
6067c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
6077c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_prog = prog;
6087c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_vers = vers;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	xdrmem_create(&p->cku_outxdr, p->cku_rpchdr, WIRE_HDR_SIZE, XDR_ENCODE);
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	if (!xdr_callhdr(&p->cku_outxdr, &call_msg)) {
613cf98b944SMarcel Telka 		XDR_DESTROY(&p->cku_outxdr);
6147c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcreate - Fatal header serialization "
6157c478bd9Sstevel@tonic-gate 		    "error\n");
6167c478bd9Sstevel@tonic-gate 		auth_destroy(h->cl_auth);
6177c478bd9Sstevel@tonic-gate 		kmem_free(p, sizeof (cku_private_t));
6187c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcreate: create failed error EINVAL\n");
6197c478bd9Sstevel@tonic-gate 		return (EINVAL);		/* XXX */
6207c478bd9Sstevel@tonic-gate 	}
621cf98b944SMarcel Telka 	XDR_DESTROY(&p->cku_outxdr);
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	/*
6247c478bd9Sstevel@tonic-gate 	 * The zalloc initialized the fields below.
6257c478bd9Sstevel@tonic-gate 	 * p->cku_xid = 0;
6267c478bd9Sstevel@tonic-gate 	 * p->cku_flags = 0;
627f67d64d9SMarcel Telka 	 * p->cku_srcaddr.buf = NULL;
6287c478bd9Sstevel@tonic-gate 	 * p->cku_srcaddr.len = 0;
6297c478bd9Sstevel@tonic-gate 	 * p->cku_srcaddr.maxlen = 0;
6307c478bd9Sstevel@tonic-gate 	 */
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 	p->cku_cred = cred;
6337c478bd9Sstevel@tonic-gate 	p->cku_device = dev;
6347c478bd9Sstevel@tonic-gate 	p->cku_addrfmly = family;
6357c478bd9Sstevel@tonic-gate 	p->cku_addr.buf = kmem_zalloc(addr->maxlen, KM_SLEEP);
6367c478bd9Sstevel@tonic-gate 	p->cku_addr.maxlen = addr->maxlen;
6377c478bd9Sstevel@tonic-gate 	p->cku_addr.len = addr->len;
6387c478bd9Sstevel@tonic-gate 	bcopy(addr->buf, p->cku_addr.buf, addr->len);
6397c478bd9Sstevel@tonic-gate 	p->cku_stats = rpcstat->rpc_cots_client;
6407c478bd9Sstevel@tonic-gate 	p->cku_useresvport = -1; /* value is has not been set */
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate 	*ncl = h;
6437c478bd9Sstevel@tonic-gate 	return (0);
6447c478bd9Sstevel@tonic-gate }
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6477c478bd9Sstevel@tonic-gate static void
clnt_cots_kabort(CLIENT * h)6487c478bd9Sstevel@tonic-gate clnt_cots_kabort(CLIENT *h)
6497c478bd9Sstevel@tonic-gate {
6507c478bd9Sstevel@tonic-gate }
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate /*
6537c478bd9Sstevel@tonic-gate  * Return error info on this handle.
6547c478bd9Sstevel@tonic-gate  */
6557c478bd9Sstevel@tonic-gate static void
clnt_cots_kerror(CLIENT * h,struct rpc_err * err)6567c478bd9Sstevel@tonic-gate clnt_cots_kerror(CLIENT *h, struct rpc_err *err)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
6597c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	*err = p->cku_err;
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate 
664cf98b944SMarcel Telka /*ARGSUSED*/
6657c478bd9Sstevel@tonic-gate static bool_t
clnt_cots_kfreeres(CLIENT * h,xdrproc_t xdr_res,caddr_t res_ptr)6667c478bd9Sstevel@tonic-gate clnt_cots_kfreeres(CLIENT *h, xdrproc_t xdr_res, caddr_t res_ptr)
6677c478bd9Sstevel@tonic-gate {
668cf98b944SMarcel Telka 	xdr_free(xdr_res, res_ptr);
6697c478bd9Sstevel@tonic-gate 
670cf98b944SMarcel Telka 	return (TRUE);
6717c478bd9Sstevel@tonic-gate }
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate static bool_t
clnt_cots_kcontrol(CLIENT * h,int cmd,char * arg)6747c478bd9Sstevel@tonic-gate clnt_cots_kcontrol(CLIENT *h, int cmd, char *arg)
6757c478bd9Sstevel@tonic-gate {
6767c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	switch (cmd) {
6797c478bd9Sstevel@tonic-gate 	case CLSET_PROGRESS:
6807c478bd9Sstevel@tonic-gate 		p->cku_progress = TRUE;
6817c478bd9Sstevel@tonic-gate 		return (TRUE);
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	case CLSET_XID:
6847c478bd9Sstevel@tonic-gate 		if (arg == NULL)
6857c478bd9Sstevel@tonic-gate 			return (FALSE);
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 		p->cku_xid = *((uint32_t *)arg);
6887c478bd9Sstevel@tonic-gate 		return (TRUE);
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	case CLGET_XID:
6917c478bd9Sstevel@tonic-gate 		if (arg == NULL)
6927c478bd9Sstevel@tonic-gate 			return (FALSE);
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 		*((uint32_t *)arg) = p->cku_xid;
6957c478bd9Sstevel@tonic-gate 		return (TRUE);
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	case CLSET_NODELAYONERR:
6987c478bd9Sstevel@tonic-gate 		if (arg == NULL)
6997c478bd9Sstevel@tonic-gate 			return (FALSE);
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 		if (*((bool_t *)arg) == TRUE) {
7027c478bd9Sstevel@tonic-gate 			p->cku_nodelayonerr = TRUE;
7037c478bd9Sstevel@tonic-gate 			return (TRUE);
7047c478bd9Sstevel@tonic-gate 		}
7057c478bd9Sstevel@tonic-gate 		if (*((bool_t *)arg) == FALSE) {
7067c478bd9Sstevel@tonic-gate 			p->cku_nodelayonerr = FALSE;
7077c478bd9Sstevel@tonic-gate 			return (TRUE);
7087c478bd9Sstevel@tonic-gate 		}
7097c478bd9Sstevel@tonic-gate 		return (FALSE);
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	case CLGET_NODELAYONERR:
7127c478bd9Sstevel@tonic-gate 		if (arg == NULL)
7137c478bd9Sstevel@tonic-gate 			return (FALSE);
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 		*((bool_t *)arg) = p->cku_nodelayonerr;
7167c478bd9Sstevel@tonic-gate 		return (TRUE);
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 	case CLSET_BINDRESVPORT:
7197c478bd9Sstevel@tonic-gate 		if (arg == NULL)
7207c478bd9Sstevel@tonic-gate 			return (FALSE);
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 		if (*(int *)arg != 1 && *(int *)arg != 0)
7237c478bd9Sstevel@tonic-gate 			return (FALSE);
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 		p->cku_useresvport = *(int *)arg;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 		return (TRUE);
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	case CLGET_BINDRESVPORT:
7307c478bd9Sstevel@tonic-gate 		if (arg == NULL)
7317c478bd9Sstevel@tonic-gate 			return (FALSE);
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 		*(int *)arg = p->cku_useresvport;
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 		return (TRUE);
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate 	default:
7387c478bd9Sstevel@tonic-gate 		return (FALSE);
7397c478bd9Sstevel@tonic-gate 	}
7407c478bd9Sstevel@tonic-gate }
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate /*
7437c478bd9Sstevel@tonic-gate  * Destroy rpc handle.  Frees the space used for output buffer,
7447c478bd9Sstevel@tonic-gate  * private data, and handle structure.
7457c478bd9Sstevel@tonic-gate  */
7467c478bd9Sstevel@tonic-gate static void
clnt_cots_kdestroy(CLIENT * h)7477c478bd9Sstevel@tonic-gate clnt_cots_kdestroy(CLIENT *h)
7487c478bd9Sstevel@tonic-gate {
7497c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
7507c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
7517c478bd9Sstevel@tonic-gate 	calllist_t *call = &p->cku_call;
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_cots_kdestroy h: %p\n", (void *)h);
7547c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_cots_kdestroy h: xid=0x%x\n", p->cku_xid);
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 	if (p->cku_flags & CKU_ONQUEUE) {
7577c478bd9Sstevel@tonic-gate 		RPCLOG(64, "clnt_cots_kdestroy h: removing call for xid 0x%x "
7587c478bd9Sstevel@tonic-gate 		    "from dispatch list\n", p->cku_xid);
7597c478bd9Sstevel@tonic-gate 		call_table_remove(call);
7607c478bd9Sstevel@tonic-gate 	}
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	if (call->call_reply)
7637c478bd9Sstevel@tonic-gate 		freemsg(call->call_reply);
7647c478bd9Sstevel@tonic-gate 	cv_destroy(&call->call_cv);
7657c478bd9Sstevel@tonic-gate 	mutex_destroy(&call->call_lock);
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	kmem_free(p->cku_srcaddr.buf, p->cku_srcaddr.maxlen);
7687c478bd9Sstevel@tonic-gate 	kmem_free(p->cku_addr.buf, p->cku_addr.maxlen);
7697c478bd9Sstevel@tonic-gate 	kmem_free(p, sizeof (*p));
7707c478bd9Sstevel@tonic-gate }
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate static int clnt_cots_pulls;
7737c478bd9Sstevel@tonic-gate #define	RM_HDR_SIZE	4	/* record mark header size */
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate /*
7767c478bd9Sstevel@tonic-gate  * Call remote procedure.
7777c478bd9Sstevel@tonic-gate  */
7787c478bd9Sstevel@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)7797c478bd9Sstevel@tonic-gate clnt_cots_kcallit(CLIENT *h, rpcproc_t procnum, xdrproc_t xdr_args,
7807c478bd9Sstevel@tonic-gate     caddr_t argsp, xdrproc_t xdr_results, caddr_t resultsp, struct timeval wait)
7817c478bd9Sstevel@tonic-gate {
7827c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
7837c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
7847c478bd9Sstevel@tonic-gate 	calllist_t *call = &p->cku_call;
7857c478bd9Sstevel@tonic-gate 	XDR *xdrs;
7867c478bd9Sstevel@tonic-gate 	struct rpc_msg reply_msg;
7877c478bd9Sstevel@tonic-gate 	mblk_t *mp;
7887c478bd9Sstevel@tonic-gate #ifdef	RPCDEBUG
7897c478bd9Sstevel@tonic-gate 	clock_t time_sent;
7907c478bd9Sstevel@tonic-gate #endif
7917c478bd9Sstevel@tonic-gate 	struct netbuf *retryaddr;
7927c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry = NULL;
7937c478bd9Sstevel@tonic-gate 	queue_t *wq;
794e280ed37SDai Ngo 	int len, waitsecs, max_waitsecs;
7957c478bd9Sstevel@tonic-gate 	int mpsize;
7967c478bd9Sstevel@tonic-gate 	int refreshes = REFRESHES;
7977c478bd9Sstevel@tonic-gate 	int interrupted;
7987c478bd9Sstevel@tonic-gate 	int tidu_size;
7997c478bd9Sstevel@tonic-gate 	enum clnt_stat status;
8007c478bd9Sstevel@tonic-gate 	struct timeval cwait;
8017c478bd9Sstevel@tonic-gate 	bool_t delay_first = FALSE;
802d3d50737SRafael Vanoni 	clock_t ticks, now;
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit, procnum %u\n", procnum);
8057c478bd9Sstevel@tonic-gate 	COTSRCSTAT_INCR(p->cku_stats, rccalls);
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit: wait.tv_sec: %ld\n", wait.tv_sec);
8087c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit: wait.tv_usec: %ld\n", wait.tv_usec);
8097c478bd9Sstevel@tonic-gate 	/*
8107c478bd9Sstevel@tonic-gate 	 * Bug ID 1240234:
8117c478bd9Sstevel@tonic-gate 	 * Look out for zero length timeouts. We don't want to
8127c478bd9Sstevel@tonic-gate 	 * wait zero seconds for a connection to be established.
8137c478bd9Sstevel@tonic-gate 	 */
8147c478bd9Sstevel@tonic-gate 	if (wait.tv_sec < clnt_cots_min_conntout) {
8157c478bd9Sstevel@tonic-gate 		cwait.tv_sec = clnt_cots_min_conntout;
8167c478bd9Sstevel@tonic-gate 		cwait.tv_usec = 0;
8177c478bd9Sstevel@tonic-gate 		RPCLOG(8, "clnt_cots_kcallit: wait.tv_sec (%ld) too low,",
8187c478bd9Sstevel@tonic-gate 		    wait.tv_sec);
8197c478bd9Sstevel@tonic-gate 		RPCLOG(8, " setting to: %d\n", clnt_cots_min_conntout);
8207c478bd9Sstevel@tonic-gate 	} else {
8217c478bd9Sstevel@tonic-gate 		cwait = wait;
8227c478bd9Sstevel@tonic-gate 	}
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate call_again:
8257c478bd9Sstevel@tonic-gate 	if (cm_entry) {
8267c478bd9Sstevel@tonic-gate 		connmgr_release(cm_entry);
8277c478bd9Sstevel@tonic-gate 		cm_entry = NULL;
8287c478bd9Sstevel@tonic-gate 	}
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	mp = NULL;
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	/*
8337c478bd9Sstevel@tonic-gate 	 * If the call is not a retry, allocate a new xid and cache it
8347c478bd9Sstevel@tonic-gate 	 * for future retries.
8357c478bd9Sstevel@tonic-gate 	 * Bug ID 1246045:
8367c478bd9Sstevel@tonic-gate 	 * Treat call as a retry for purposes of binding the source
8377c478bd9Sstevel@tonic-gate 	 * port only if we actually attempted to send anything on
8387c478bd9Sstevel@tonic-gate 	 * the previous call.
8397c478bd9Sstevel@tonic-gate 	 */
8407c478bd9Sstevel@tonic-gate 	if (p->cku_xid == 0) {
8417c478bd9Sstevel@tonic-gate 		p->cku_xid = alloc_xid();
8428ffff9fdSgt29601 		call->call_zoneid = rpc_zoneid();
8438ffff9fdSgt29601 
8447c478bd9Sstevel@tonic-gate 		/*
8457c478bd9Sstevel@tonic-gate 		 * We need to ASSERT here that our xid != 0 because this
8467c478bd9Sstevel@tonic-gate 		 * determines whether or not our call record gets placed on
8477c478bd9Sstevel@tonic-gate 		 * the hash table or the linked list.  By design, we mandate
8487c478bd9Sstevel@tonic-gate 		 * that RPC calls over cots must have xid's != 0, so we can
8497c478bd9Sstevel@tonic-gate 		 * ensure proper management of the hash table.
8507c478bd9Sstevel@tonic-gate 		 */
8517c478bd9Sstevel@tonic-gate 		ASSERT(p->cku_xid != 0);
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 		retryaddr = NULL;
8547c478bd9Sstevel@tonic-gate 		p->cku_flags &= ~CKU_SENT;
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 		if (p->cku_flags & CKU_ONQUEUE) {
8577c478bd9Sstevel@tonic-gate 			RPCLOG(8, "clnt_cots_kcallit: new call, dequeuing old"
8587c478bd9Sstevel@tonic-gate 			    " one (%p)\n", (void *)call);
8597c478bd9Sstevel@tonic-gate 			call_table_remove(call);
8607c478bd9Sstevel@tonic-gate 			p->cku_flags &= ~CKU_ONQUEUE;
8617c478bd9Sstevel@tonic-gate 			RPCLOG(64, "clnt_cots_kcallit: removing call from "
8627c478bd9Sstevel@tonic-gate 			    "dispatch list because xid was zero (now 0x%x)\n",
8637c478bd9Sstevel@tonic-gate 			    p->cku_xid);
8647c478bd9Sstevel@tonic-gate 		}
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 		if (call->call_reply != NULL) {
8677c478bd9Sstevel@tonic-gate 			freemsg(call->call_reply);
8687c478bd9Sstevel@tonic-gate 			call->call_reply = NULL;
8697c478bd9Sstevel@tonic-gate 		}
8707c478bd9Sstevel@tonic-gate 	} else if (p->cku_srcaddr.buf == NULL || p->cku_srcaddr.len == 0) {
8717c478bd9Sstevel@tonic-gate 		retryaddr = NULL;
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	} else if (p->cku_flags & CKU_SENT) {
8747c478bd9Sstevel@tonic-gate 		retryaddr = &p->cku_srcaddr;
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	} else {
8777c478bd9Sstevel@tonic-gate 		/*
8787c478bd9Sstevel@tonic-gate 		 * Bug ID 1246045: Nothing was sent, so set retryaddr to
8797c478bd9Sstevel@tonic-gate 		 * NULL and let connmgr_get() bind to any source port it
8807c478bd9Sstevel@tonic-gate 		 * can get.
8817c478bd9Sstevel@tonic-gate 		 */
8827c478bd9Sstevel@tonic-gate 		retryaddr = NULL;
8837c478bd9Sstevel@tonic-gate 	}
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate 	RPCLOG(64, "clnt_cots_kcallit: xid = 0x%x", p->cku_xid);
8867c478bd9Sstevel@tonic-gate 	RPCLOG(64, " flags = 0x%x\n", p->cku_flags);
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 	p->cku_err.re_status = RPC_TIMEDOUT;
8897c478bd9Sstevel@tonic-gate 	p->cku_err.re_errno = p->cku_err.re_terrno = 0;
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 	cm_entry = connmgr_wrapget(retryaddr, &cwait, p);
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	if (cm_entry == NULL) {
8947c478bd9Sstevel@tonic-gate 		RPCLOG(1, "clnt_cots_kcallit: can't connect status %s\n",
8957c478bd9Sstevel@tonic-gate 		    clnt_sperrno(p->cku_err.re_status));
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 		/*
8987c478bd9Sstevel@tonic-gate 		 * The reasons why we fail to create a connection are
8997c478bd9Sstevel@tonic-gate 		 * varied. In most cases we don't want the caller to
9007c478bd9Sstevel@tonic-gate 		 * immediately retry. This could have one or more
9017c478bd9Sstevel@tonic-gate 		 * bad effects. This includes flooding the net with
9027c478bd9Sstevel@tonic-gate 		 * connect requests to ports with no listener; a hard
9037c478bd9Sstevel@tonic-gate 		 * kernel loop due to all the "reserved" TCP ports being
9047c478bd9Sstevel@tonic-gate 		 * in use.
9057c478bd9Sstevel@tonic-gate 		 */
9067c478bd9Sstevel@tonic-gate 		delay_first = TRUE;
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 		/*
9097c478bd9Sstevel@tonic-gate 		 * Even if we end up returning EINTR, we still count a
9107c478bd9Sstevel@tonic-gate 		 * a "can't connect", because the connection manager
9117c478bd9Sstevel@tonic-gate 		 * might have been committed to waiting for or timing out on
9127c478bd9Sstevel@tonic-gate 		 * a connection.
9137c478bd9Sstevel@tonic-gate 		 */
9147c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(p->cku_stats, rccantconn);
9157c478bd9Sstevel@tonic-gate 		switch (p->cku_err.re_status) {
9167c478bd9Sstevel@tonic-gate 		case RPC_INTR:
9177c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EINTR;
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 			/*
9207c478bd9Sstevel@tonic-gate 			 * No need to delay because a UNIX signal(2)
9217c478bd9Sstevel@tonic-gate 			 * interrupted us. The caller likely won't
9227c478bd9Sstevel@tonic-gate 			 * retry the CLNT_CALL() and even if it does,
9237c478bd9Sstevel@tonic-gate 			 * we assume the caller knows what it is doing.
9247c478bd9Sstevel@tonic-gate 			 */
9257c478bd9Sstevel@tonic-gate 			delay_first = FALSE;
9267c478bd9Sstevel@tonic-gate 			break;
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 		case RPC_TIMEDOUT:
9297c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = ETIMEDOUT;
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 			/*
9327c478bd9Sstevel@tonic-gate 			 * No need to delay because timed out already
9337c478bd9Sstevel@tonic-gate 			 * on the connection request and assume that the
9347c478bd9Sstevel@tonic-gate 			 * transport time out is longer than our minimum
9357c478bd9Sstevel@tonic-gate 			 * timeout, or least not too much smaller.
9367c478bd9Sstevel@tonic-gate 			 */
9377c478bd9Sstevel@tonic-gate 			delay_first = FALSE;
9387c478bd9Sstevel@tonic-gate 			break;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 		case RPC_SYSTEMERROR:
9417c478bd9Sstevel@tonic-gate 		case RPC_TLIERROR:
9427c478bd9Sstevel@tonic-gate 			/*
9437c478bd9Sstevel@tonic-gate 			 * We want to delay here because a transient
9447c478bd9Sstevel@tonic-gate 			 * system error has a better chance of going away
9457c478bd9Sstevel@tonic-gate 			 * if we delay a bit. If it's not transient, then
9467c478bd9Sstevel@tonic-gate 			 * we don't want end up in a hard kernel loop
9477c478bd9Sstevel@tonic-gate 			 * due to retries.
9487c478bd9Sstevel@tonic-gate 			 */
9497c478bd9Sstevel@tonic-gate 			ASSERT(p->cku_err.re_errno != 0);
9507c478bd9Sstevel@tonic-gate 			break;
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 		case RPC_CANTCONNECT:
9547c478bd9Sstevel@tonic-gate 			/*
9557c478bd9Sstevel@tonic-gate 			 * RPC_CANTCONNECT is set on T_ERROR_ACK which
9567c478bd9Sstevel@tonic-gate 			 * implies some error down in the TCP layer or
9577c478bd9Sstevel@tonic-gate 			 * below. If cku_nodelayonerror is set then we
9587c478bd9Sstevel@tonic-gate 			 * assume the caller knows not to try too hard.
9597c478bd9Sstevel@tonic-gate 			 */
9607c478bd9Sstevel@tonic-gate 			RPCLOG0(8, "clnt_cots_kcallit: connection failed,");
9617c478bd9Sstevel@tonic-gate 			RPCLOG0(8, " re_status=RPC_CANTCONNECT,");
9627c478bd9Sstevel@tonic-gate 			RPCLOG(8, " re_errno=%d,", p->cku_err.re_errno);
9637c478bd9Sstevel@tonic-gate 			RPCLOG(8, " cku_nodelayonerr=%d", p->cku_nodelayonerr);
9647c478bd9Sstevel@tonic-gate 			if (p->cku_nodelayonerr == TRUE)
9657c478bd9Sstevel@tonic-gate 				delay_first = FALSE;
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 			break;
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 		case RPC_XPRTFAILED:
9727c478bd9Sstevel@tonic-gate 			/*
9737c478bd9Sstevel@tonic-gate 			 * We want to delay here because we likely
9747c478bd9Sstevel@tonic-gate 			 * got a refused connection.
9757c478bd9Sstevel@tonic-gate 			 */
976af41c4bfSvv149972 			if (p->cku_err.re_errno == 0)
977af41c4bfSvv149972 				p->cku_err.re_errno = EIO;
9787c478bd9Sstevel@tonic-gate 
979af41c4bfSvv149972 			RPCLOG(1, "clnt_cots_kcallit: transport failed: %d\n",
980af41c4bfSvv149972 			    p->cku_err.re_errno);
981af41c4bfSvv149972 
982af41c4bfSvv149972 			break;
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 		default:
9857c478bd9Sstevel@tonic-gate 			/*
9867c478bd9Sstevel@tonic-gate 			 * We delay here because it is better to err
9877c478bd9Sstevel@tonic-gate 			 * on the side of caution. If we got here then
9887c478bd9Sstevel@tonic-gate 			 * status could have been RPC_SUCCESS, but we
9897c478bd9Sstevel@tonic-gate 			 * know that we did not get a connection, so
9907c478bd9Sstevel@tonic-gate 			 * force the rpc status to RPC_CANTCONNECT.
9917c478bd9Sstevel@tonic-gate 			 */
9927c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_CANTCONNECT;
9937c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
9947c478bd9Sstevel@tonic-gate 			break;
9957c478bd9Sstevel@tonic-gate 		}
9967c478bd9Sstevel@tonic-gate 		if (delay_first == TRUE)
9977c478bd9Sstevel@tonic-gate 			ticks = clnt_cots_min_tout * drv_usectohz(1000000);
9987c478bd9Sstevel@tonic-gate 		goto cots_done;
9997c478bd9Sstevel@tonic-gate 	}
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 	/*
10027c478bd9Sstevel@tonic-gate 	 * If we've never sent any request on this connection (send count
10037c478bd9Sstevel@tonic-gate 	 * is zero, or the connection has been reset), cache the
10047c478bd9Sstevel@tonic-gate 	 * the connection's create time and send a request (possibly a retry)
10057c478bd9Sstevel@tonic-gate 	 */
10067c478bd9Sstevel@tonic-gate 	if ((p->cku_flags & CKU_SENT) == 0 ||
10077c478bd9Sstevel@tonic-gate 	    p->cku_ctime != cm_entry->x_ctime) {
10087c478bd9Sstevel@tonic-gate 		p->cku_ctime = cm_entry->x_ctime;
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate 	} else if ((p->cku_flags & CKU_SENT) && (p->cku_flags & CKU_ONQUEUE) &&
10117c478bd9Sstevel@tonic-gate 	    (call->call_reply != NULL ||
10127c478bd9Sstevel@tonic-gate 	    p->cku_recv_attempts < clnt_cots_maxrecv)) {
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 		/*
10157c478bd9Sstevel@tonic-gate 		 * If we've sent a request and our call is on the dispatch
10167c478bd9Sstevel@tonic-gate 		 * queue and we haven't made too many receive attempts, then
10177c478bd9Sstevel@tonic-gate 		 * don't re-send, just receive.
10187c478bd9Sstevel@tonic-gate 		 */
10197c478bd9Sstevel@tonic-gate 		p->cku_recv_attempts++;
10207c478bd9Sstevel@tonic-gate 		goto read_again;
10217c478bd9Sstevel@tonic-gate 	}
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	/*
10247c478bd9Sstevel@tonic-gate 	 * Now we create the RPC request in a STREAMS message.  We have to do
10257c478bd9Sstevel@tonic-gate 	 * this after the call to connmgr_get so that we have the correct
10267c478bd9Sstevel@tonic-gate 	 * TIDU size for the transport.
10277c478bd9Sstevel@tonic-gate 	 */
10287c478bd9Sstevel@tonic-gate 	tidu_size = cm_entry->x_tidu_size;
10297c478bd9Sstevel@tonic-gate 	len = MSG_OFFSET + MAX(tidu_size, RM_HDR_SIZE + WIRE_HDR_SIZE);
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 	while ((mp = allocb(len, BPRI_MED)) == NULL) {
10327c478bd9Sstevel@tonic-gate 		if (strwaitbuf(len, BPRI_MED)) {
10337c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_SYSTEMERROR;
10347c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = ENOSR;
10357c478bd9Sstevel@tonic-gate 			COTSRCSTAT_INCR(p->cku_stats, rcnomem);
10367c478bd9Sstevel@tonic-gate 			goto cots_done;
10377c478bd9Sstevel@tonic-gate 		}
10387c478bd9Sstevel@tonic-gate 	}
10397c478bd9Sstevel@tonic-gate 	xdrs = &p->cku_outxdr;
10407c478bd9Sstevel@tonic-gate 	xdrmblk_init(xdrs, mp, XDR_ENCODE, tidu_size);
10417c478bd9Sstevel@tonic-gate 	mpsize = MBLKSIZE(mp);
10427c478bd9Sstevel@tonic-gate 	ASSERT(mpsize >= len);
10437c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_rptr == mp->b_datap->db_base);
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate 	/*
10467c478bd9Sstevel@tonic-gate 	 * If the size of mblk is not appreciably larger than what we
10477c478bd9Sstevel@tonic-gate 	 * asked, then resize the mblk to exactly len bytes. The reason for
10487c478bd9Sstevel@tonic-gate 	 * this: suppose len is 1600 bytes, the tidu is 1460 bytes
10497c478bd9Sstevel@tonic-gate 	 * (from TCP over ethernet), and the arguments to the RPC require
10507c478bd9Sstevel@tonic-gate 	 * 2800 bytes. Ideally we want the protocol to render two
10517c478bd9Sstevel@tonic-gate 	 * ~1400 byte segments over the wire. However if allocb() gives us a 2k
10527c478bd9Sstevel@tonic-gate 	 * mblk, and we allocate a second mblk for the remainder, the protocol
10537c478bd9Sstevel@tonic-gate 	 * module may generate 3 segments over the wire:
10547c478bd9Sstevel@tonic-gate 	 * 1460 bytes for the first, 448 (2048 - 1600) for the second, and
10557c478bd9Sstevel@tonic-gate 	 * 892 for the third. If we "waste" 448 bytes in the first mblk,
10567c478bd9Sstevel@tonic-gate 	 * the XDR encoding will generate two ~1400 byte mblks, and the
10577c478bd9Sstevel@tonic-gate 	 * protocol module is more likely to produce properly sized segments.
10587c478bd9Sstevel@tonic-gate 	 */
10597c478bd9Sstevel@tonic-gate 	if ((mpsize >> 1) <= len)
10607c478bd9Sstevel@tonic-gate 		mp->b_rptr += (mpsize - len);
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	/*
10637c478bd9Sstevel@tonic-gate 	 * Adjust b_rptr to reserve space for the non-data protocol headers
10647c478bd9Sstevel@tonic-gate 	 * any downstream modules might like to add, and for the
10657c478bd9Sstevel@tonic-gate 	 * record marking header.
10667c478bd9Sstevel@tonic-gate 	 */
10677c478bd9Sstevel@tonic-gate 	mp->b_rptr += (MSG_OFFSET + RM_HDR_SIZE);
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate 	if (h->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
10707c478bd9Sstevel@tonic-gate 		/* Copy in the preserialized RPC header information. */
10717c478bd9Sstevel@tonic-gate 		bcopy(p->cku_rpchdr, mp->b_rptr, WIRE_HDR_SIZE);
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 		/* Use XDR_SETPOS() to set the b_wptr to past the RPC header. */
10747c478bd9Sstevel@tonic-gate 		XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base +
10757c478bd9Sstevel@tonic-gate 		    WIRE_HDR_SIZE));
10767c478bd9Sstevel@tonic-gate 
10777c478bd9Sstevel@tonic-gate 		ASSERT((mp->b_wptr - mp->b_rptr) == WIRE_HDR_SIZE);
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 		/* Serialize the procedure number and the arguments. */
10807c478bd9Sstevel@tonic-gate 		if ((!XDR_PUTINT32(xdrs, (int32_t *)&procnum)) ||
10817c478bd9Sstevel@tonic-gate 		    (!AUTH_MARSHALL(h->cl_auth, xdrs, p->cku_cred)) ||
10827c478bd9Sstevel@tonic-gate 		    (!(*xdr_args)(xdrs, argsp))) {
1083cf98b944SMarcel Telka 			XDR_DESTROY(xdrs);
10847c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_CANTENCODEARGS;
10857c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
10867c478bd9Sstevel@tonic-gate 			goto cots_done;
10877c478bd9Sstevel@tonic-gate 		}
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 		(*(uint32_t *)(mp->b_rptr)) = p->cku_xid;
10907c478bd9Sstevel@tonic-gate 	} else {
10917c478bd9Sstevel@tonic-gate 		uint32_t *uproc = (uint32_t *)&p->cku_rpchdr[WIRE_HDR_SIZE];
10927c478bd9Sstevel@tonic-gate 		IXDR_PUT_U_INT32(uproc, procnum);
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 		(*(uint32_t *)(&p->cku_rpchdr[0])) = p->cku_xid;
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate 		/* Use XDR_SETPOS() to set the b_wptr. */
10977c478bd9Sstevel@tonic-gate 		XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base));
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 		/* Serialize the procedure number and the arguments. */
11007c478bd9Sstevel@tonic-gate 		if (!AUTH_WRAP(h->cl_auth, p->cku_rpchdr, WIRE_HDR_SIZE+4,
11017c478bd9Sstevel@tonic-gate 		    xdrs, xdr_args, argsp)) {
1102cf98b944SMarcel Telka 			XDR_DESTROY(xdrs);
11037c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_CANTENCODEARGS;
11047c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
11057c478bd9Sstevel@tonic-gate 			goto cots_done;
11067c478bd9Sstevel@tonic-gate 		}
11077c478bd9Sstevel@tonic-gate 	}
11087c478bd9Sstevel@tonic-gate 
1109cf98b944SMarcel Telka 	XDR_DESTROY(xdrs);
1110cf98b944SMarcel Telka 
11117c478bd9Sstevel@tonic-gate 	RPCLOG(2, "clnt_cots_kcallit: connected, sending call, tidu_size %d\n",
11127c478bd9Sstevel@tonic-gate 	    tidu_size);
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 	wq = cm_entry->x_wq;
1115e280ed37SDai Ngo 	waitsecs = 0;
1116e280ed37SDai Ngo 
1117e280ed37SDai Ngo dispatch_again:
1118125a8fd9SSiddheshwar Mahesh 	status = clnt_dispatch_send(wq, mp, call, p->cku_xid,
11197c478bd9Sstevel@tonic-gate 	    (p->cku_flags & CKU_ONQUEUE));
11207c478bd9Sstevel@tonic-gate 
1121e280ed37SDai Ngo 	if ((status == RPC_CANTSEND) && (call->call_reason == ENOBUFS)) {
1122125a8fd9SSiddheshwar Mahesh 		/*
1123e280ed37SDai Ngo 		 * QFULL condition, allow some time for queue to drain
1124e280ed37SDai Ngo 		 * and try again. Give up after waiting for all timeout
1125e280ed37SDai Ngo 		 * specified for the call, or zone is going away.
1126125a8fd9SSiddheshwar Mahesh 		 */
1127e280ed37SDai Ngo 		max_waitsecs = wait.tv_sec ? wait.tv_sec : clnt_cots_min_tout;
1128e280ed37SDai Ngo 		if ((waitsecs++ < max_waitsecs) &&
1129e280ed37SDai Ngo 		    !(zone_status_get(curproc->p_zone) >=
1130e280ed37SDai Ngo 		    ZONE_IS_SHUTTING_DOWN)) {
1131e280ed37SDai Ngo 
1132e280ed37SDai Ngo 			/* wait 1 sec for queue to drain */
1133e280ed37SDai Ngo 			if (clnt_delay(drv_usectohz(1000000),
1134e280ed37SDai Ngo 			    h->cl_nosignal) == EINTR) {
1135e280ed37SDai Ngo 				p->cku_err.re_errno = EINTR;
1136e280ed37SDai Ngo 				p->cku_err.re_status = RPC_INTR;
1137125a8fd9SSiddheshwar Mahesh 
1138125a8fd9SSiddheshwar Mahesh 				goto cots_done;
1139125a8fd9SSiddheshwar Mahesh 			}
1140125a8fd9SSiddheshwar Mahesh 
1141e280ed37SDai Ngo 			/* and try again */
1142e280ed37SDai Ngo 			goto dispatch_again;
1143e280ed37SDai Ngo 		}
1144e280ed37SDai Ngo 		p->cku_err.re_status = status;
1145e280ed37SDai Ngo 		p->cku_err.re_errno = call->call_reason;
1146e280ed37SDai Ngo 		DTRACE_PROBE(krpc__e__clntcots__kcallit__cantsend);
1147e280ed37SDai Ngo 
1148e280ed37SDai Ngo 		goto cots_done;
1149e280ed37SDai Ngo 	}
1150e280ed37SDai Ngo 
1151e280ed37SDai Ngo 	if (waitsecs) {
1152e280ed37SDai Ngo 		/* adjust timeout to account for time wait to send */
1153e280ed37SDai Ngo 		wait.tv_sec -= waitsecs;
1154e280ed37SDai Ngo 		if (wait.tv_sec < 0) {
1155e280ed37SDai Ngo 			/* pick up reply on next retry */
1156e280ed37SDai Ngo 			wait.tv_sec = 0;
1157e280ed37SDai Ngo 		}
1158e280ed37SDai Ngo 		DTRACE_PROBE2(clnt_cots__sendwait, CLIENT *, h,
1159e280ed37SDai Ngo 		    int, waitsecs);
1160e280ed37SDai Ngo 	}
1161e280ed37SDai Ngo 
11627c478bd9Sstevel@tonic-gate 	RPCLOG(64, "clnt_cots_kcallit: sent call for xid 0x%x\n",
11637c478bd9Sstevel@tonic-gate 	    (uint_t)p->cku_xid);
11647c478bd9Sstevel@tonic-gate 	p->cku_flags = (CKU_ONQUEUE|CKU_SENT);
11657c478bd9Sstevel@tonic-gate 	p->cku_recv_attempts = 1;
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate #ifdef	RPCDEBUG
1168d3d50737SRafael Vanoni 	time_sent = ddi_get_lbolt();
11697c478bd9Sstevel@tonic-gate #endif
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 	/*
11727c478bd9Sstevel@tonic-gate 	 * Wait for a reply or a timeout.  If there is no error or timeout,
11737c478bd9Sstevel@tonic-gate 	 * (both indicated by call_status), call->call_reply will contain
11747c478bd9Sstevel@tonic-gate 	 * the RPC reply message.
11757c478bd9Sstevel@tonic-gate 	 */
11767c478bd9Sstevel@tonic-gate read_again:
11777c478bd9Sstevel@tonic-gate 	mutex_enter(&call->call_lock);
11787c478bd9Sstevel@tonic-gate 	interrupted = 0;
11797c478bd9Sstevel@tonic-gate 	if (call->call_status == RPC_TIMEDOUT) {
11807c478bd9Sstevel@tonic-gate 		/*
11817c478bd9Sstevel@tonic-gate 		 * Indicate that the lwp is not to be stopped while waiting
11827c478bd9Sstevel@tonic-gate 		 * for this network traffic.  This is to avoid deadlock while
11837c478bd9Sstevel@tonic-gate 		 * debugging a process via /proc and also to avoid recursive
11847c478bd9Sstevel@tonic-gate 		 * mutex_enter()s due to NFS page faults while stopping
11857c478bd9Sstevel@tonic-gate 		 * (NFS holds locks when it calls here).
11867c478bd9Sstevel@tonic-gate 		 */
11877c478bd9Sstevel@tonic-gate 		clock_t cv_wait_ret;
11887c478bd9Sstevel@tonic-gate 		clock_t timout;
11897c478bd9Sstevel@tonic-gate 		clock_t oldlbolt;
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 		klwp_t *lwp = ttolwp(curthread);
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 		if (lwp != NULL)
11947c478bd9Sstevel@tonic-gate 			lwp->lwp_nostop++;
11957c478bd9Sstevel@tonic-gate 
1196d3d50737SRafael Vanoni 		oldlbolt = ddi_get_lbolt();
11977c478bd9Sstevel@tonic-gate 		timout = wait.tv_sec * drv_usectohz(1000000) +
11987c478bd9Sstevel@tonic-gate 		    drv_usectohz(wait.tv_usec) + oldlbolt;
11997c478bd9Sstevel@tonic-gate 		/*
12007c478bd9Sstevel@tonic-gate 		 * Iterate until the call_status is changed to something
12017c478bd9Sstevel@tonic-gate 		 * other that RPC_TIMEDOUT, or if cv_timedwait_sig() returns
12027c478bd9Sstevel@tonic-gate 		 * something <=0 zero. The latter means that we timed
12037c478bd9Sstevel@tonic-gate 		 * out.
12047c478bd9Sstevel@tonic-gate 		 */
12057c478bd9Sstevel@tonic-gate 		if (h->cl_nosignal)
12067c478bd9Sstevel@tonic-gate 			while ((cv_wait_ret = cv_timedwait(&call->call_cv,
12077c478bd9Sstevel@tonic-gate 			    &call->call_lock, timout)) > 0 &&
12088ffff9fdSgt29601 			    call->call_status == RPC_TIMEDOUT)
12098ffff9fdSgt29601 				;
12107c478bd9Sstevel@tonic-gate 		else
12117c478bd9Sstevel@tonic-gate 			while ((cv_wait_ret = cv_timedwait_sig(
12127c478bd9Sstevel@tonic-gate 			    &call->call_cv,
12137c478bd9Sstevel@tonic-gate 			    &call->call_lock, timout)) > 0 &&
12148ffff9fdSgt29601 			    call->call_status == RPC_TIMEDOUT)
12158ffff9fdSgt29601 				;
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate 		switch (cv_wait_ret) {
12187c478bd9Sstevel@tonic-gate 		case 0:
12197c478bd9Sstevel@tonic-gate 			/*
12207c478bd9Sstevel@tonic-gate 			 * If we got out of the above loop with
12217c478bd9Sstevel@tonic-gate 			 * cv_timedwait_sig() returning 0, then we were
12227c478bd9Sstevel@tonic-gate 			 * interrupted regardless what call_status is.
12237c478bd9Sstevel@tonic-gate 			 */
12247c478bd9Sstevel@tonic-gate 			interrupted = 1;
12257c478bd9Sstevel@tonic-gate 			break;
12267c478bd9Sstevel@tonic-gate 		case -1:
12277c478bd9Sstevel@tonic-gate 			/* cv_timedwait_sig() timed out */
12287c478bd9Sstevel@tonic-gate 			break;
12297c478bd9Sstevel@tonic-gate 		default:
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 			/*
12327c478bd9Sstevel@tonic-gate 			 * We were cv_signaled(). If we didn't
12337c478bd9Sstevel@tonic-gate 			 * get a successful call_status and returned
12347c478bd9Sstevel@tonic-gate 			 * before time expired, delay up to clnt_cots_min_tout
12357c478bd9Sstevel@tonic-gate 			 * seconds so that the caller doesn't immediately
12367c478bd9Sstevel@tonic-gate 			 * try to call us again and thus force the
12377c478bd9Sstevel@tonic-gate 			 * same condition that got us here (such
12387c478bd9Sstevel@tonic-gate 			 * as a RPC_XPRTFAILED due to the server not
12397c478bd9Sstevel@tonic-gate 			 * listening on the end-point.
12407c478bd9Sstevel@tonic-gate 			 */
12417c478bd9Sstevel@tonic-gate 			if (call->call_status != RPC_SUCCESS) {
12427c478bd9Sstevel@tonic-gate 				clock_t curlbolt;
12437c478bd9Sstevel@tonic-gate 				clock_t diff;
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 				curlbolt = ddi_get_lbolt();
12467c478bd9Sstevel@tonic-gate 				ticks = clnt_cots_min_tout *
12477c478bd9Sstevel@tonic-gate 				    drv_usectohz(1000000);
12487c478bd9Sstevel@tonic-gate 				diff = curlbolt - oldlbolt;
12497c478bd9Sstevel@tonic-gate 				if (diff < ticks) {
12507c478bd9Sstevel@tonic-gate 					delay_first = TRUE;
12517c478bd9Sstevel@tonic-gate 					if (diff > 0)
12527c478bd9Sstevel@tonic-gate 						ticks -= diff;
12537c478bd9Sstevel@tonic-gate 				}
12547c478bd9Sstevel@tonic-gate 			}
12557c478bd9Sstevel@tonic-gate 			break;
12567c478bd9Sstevel@tonic-gate 		}
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 		if (lwp != NULL)
12597c478bd9Sstevel@tonic-gate 			lwp->lwp_nostop--;
12607c478bd9Sstevel@tonic-gate 	}
12617c478bd9Sstevel@tonic-gate 	/*
12627c478bd9Sstevel@tonic-gate 	 * Get the reply message, if any.  This will be freed at the end
12637c478bd9Sstevel@tonic-gate 	 * whether or not an error occurred.
12647c478bd9Sstevel@tonic-gate 	 */
12657c478bd9Sstevel@tonic-gate 	mp = call->call_reply;
12667c478bd9Sstevel@tonic-gate 	call->call_reply = NULL;
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate 	/*
12697c478bd9Sstevel@tonic-gate 	 * call_err is the error info when the call is on dispatch queue.
12707c478bd9Sstevel@tonic-gate 	 * cku_err is the error info returned to the caller.
12717c478bd9Sstevel@tonic-gate 	 * Sync cku_err with call_err for local message processing.
12727c478bd9Sstevel@tonic-gate 	 */
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 	status = call->call_status;
12757c478bd9Sstevel@tonic-gate 	p->cku_err = call->call_err;
12767c478bd9Sstevel@tonic-gate 	mutex_exit(&call->call_lock);
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 	if (status != RPC_SUCCESS) {
12797c478bd9Sstevel@tonic-gate 		switch (status) {
12807c478bd9Sstevel@tonic-gate 		case RPC_TIMEDOUT:
1281d3d50737SRafael Vanoni 			now = ddi_get_lbolt();
12827c478bd9Sstevel@tonic-gate 			if (interrupted) {
12837c478bd9Sstevel@tonic-gate 				COTSRCSTAT_INCR(p->cku_stats, rcintrs);
12847c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_INTR;
12857c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EINTR;
12867c478bd9Sstevel@tonic-gate 				RPCLOG(1, "clnt_cots_kcallit: xid 0x%x",
12877c478bd9Sstevel@tonic-gate 				    p->cku_xid);
1288d3d50737SRafael Vanoni 				RPCLOG(1, "signal interrupted at %ld", now);
12897c478bd9Sstevel@tonic-gate 				RPCLOG(1, ", was sent at %ld\n", time_sent);
12907c478bd9Sstevel@tonic-gate 			} else {
12917c478bd9Sstevel@tonic-gate 				COTSRCSTAT_INCR(p->cku_stats, rctimeouts);
12927c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = ETIMEDOUT;
12937c478bd9Sstevel@tonic-gate 				RPCLOG(1, "clnt_cots_kcallit: timed out at %ld",
1294d3d50737SRafael Vanoni 				    now);
12957c478bd9Sstevel@tonic-gate 				RPCLOG(1, ", was sent at %ld\n", time_sent);
12967c478bd9Sstevel@tonic-gate 			}
12977c478bd9Sstevel@tonic-gate 			break;
12987c478bd9Sstevel@tonic-gate 
12997c478bd9Sstevel@tonic-gate 		case RPC_XPRTFAILED:
13007c478bd9Sstevel@tonic-gate 			if (p->cku_err.re_errno == 0)
13017c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcallit: transport failed: %d\n",
13047c478bd9Sstevel@tonic-gate 			    p->cku_err.re_errno);
13057c478bd9Sstevel@tonic-gate 			break;
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 		case RPC_SYSTEMERROR:
13087c478bd9Sstevel@tonic-gate 			ASSERT(p->cku_err.re_errno);
13097c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcallit: system error: %d\n",
13107c478bd9Sstevel@tonic-gate 			    p->cku_err.re_errno);
13117c478bd9Sstevel@tonic-gate 			break;
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 		default:
13147c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_SYSTEMERROR;
13157c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EIO;
13167c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcallit: error: %s\n",
13177c478bd9Sstevel@tonic-gate 			    clnt_sperrno(status));
13187c478bd9Sstevel@tonic-gate 			break;
13197c478bd9Sstevel@tonic-gate 		}
13207c478bd9Sstevel@tonic-gate 		if (p->cku_err.re_status != RPC_TIMEDOUT) {
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate 			if (p->cku_flags & CKU_ONQUEUE) {
13237c478bd9Sstevel@tonic-gate 				call_table_remove(call);
13247c478bd9Sstevel@tonic-gate 				p->cku_flags &= ~CKU_ONQUEUE;
13257c478bd9Sstevel@tonic-gate 			}
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 			RPCLOG(64, "clnt_cots_kcallit: non TIMEOUT so xid 0x%x "
13287c478bd9Sstevel@tonic-gate 			    "taken off dispatch list\n", p->cku_xid);
13297c478bd9Sstevel@tonic-gate 			if (call->call_reply) {
13307c478bd9Sstevel@tonic-gate 				freemsg(call->call_reply);
13317c478bd9Sstevel@tonic-gate 				call->call_reply = NULL;
13327c478bd9Sstevel@tonic-gate 			}
13337c478bd9Sstevel@tonic-gate 		} else if (wait.tv_sec != 0) {
13347c478bd9Sstevel@tonic-gate 			/*
13357c478bd9Sstevel@tonic-gate 			 * We've sent the request over TCP and so we have
13367c478bd9Sstevel@tonic-gate 			 * every reason to believe it will get
13377c478bd9Sstevel@tonic-gate 			 * delivered. In which case returning a timeout is not
13387c478bd9Sstevel@tonic-gate 			 * appropriate.
13397c478bd9Sstevel@tonic-gate 			 */
13407c478bd9Sstevel@tonic-gate 			if (p->cku_progress == TRUE &&
13417c478bd9Sstevel@tonic-gate 			    p->cku_recv_attempts < clnt_cots_maxrecv) {
13427c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_INPROGRESS;
13437c478bd9Sstevel@tonic-gate 			}
13447c478bd9Sstevel@tonic-gate 		}
13457c478bd9Sstevel@tonic-gate 		goto cots_done;
13467c478bd9Sstevel@tonic-gate 	}
13477c478bd9Sstevel@tonic-gate 
13487c478bd9Sstevel@tonic-gate 	xdrs = &p->cku_inxdr;
13497c478bd9Sstevel@tonic-gate 	xdrmblk_init(xdrs, mp, XDR_DECODE, 0);
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 	reply_msg.rm_direction = REPLY;
13527c478bd9Sstevel@tonic-gate 	reply_msg.rm_reply.rp_stat = MSG_ACCEPTED;
13537c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_stat = SUCCESS;
13547c478bd9Sstevel@tonic-gate 
13557c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_verf = _null_auth;
13567c478bd9Sstevel@tonic-gate 	/*
13577c478bd9Sstevel@tonic-gate 	 *  xdr_results will be done in AUTH_UNWRAP.
13587c478bd9Sstevel@tonic-gate 	 */
13597c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_results.where = NULL;
13607c478bd9Sstevel@tonic-gate 	reply_msg.acpted_rply.ar_results.proc = xdr_void;
13617c478bd9Sstevel@tonic-gate 
13627c478bd9Sstevel@tonic-gate 	if (xdr_replymsg(xdrs, &reply_msg)) {
13637c478bd9Sstevel@tonic-gate 		enum clnt_stat re_status;
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate 		_seterr_reply(&reply_msg, &p->cku_err);
13667c478bd9Sstevel@tonic-gate 
13677c478bd9Sstevel@tonic-gate 		re_status = p->cku_err.re_status;
13687c478bd9Sstevel@tonic-gate 		if (re_status == RPC_SUCCESS) {
13697c478bd9Sstevel@tonic-gate 			/*
13707c478bd9Sstevel@tonic-gate 			 * Reply is good, check auth.
13717c478bd9Sstevel@tonic-gate 			 */
13727c478bd9Sstevel@tonic-gate 			if (!AUTH_VALIDATE(h->cl_auth,
13737c478bd9Sstevel@tonic-gate 			    &reply_msg.acpted_rply.ar_verf)) {
13747c478bd9Sstevel@tonic-gate 				COTSRCSTAT_INCR(p->cku_stats, rcbadverfs);
13757c478bd9Sstevel@tonic-gate 				RPCLOG0(1, "clnt_cots_kcallit: validation "
13767c478bd9Sstevel@tonic-gate 				    "failure\n");
13777c478bd9Sstevel@tonic-gate 				freemsg(mp);
13787c478bd9Sstevel@tonic-gate 				(void) xdr_rpc_free_verifier(xdrs, &reply_msg);
1379cf98b944SMarcel Telka 				XDR_DESTROY(xdrs);
13807c478bd9Sstevel@tonic-gate 				mutex_enter(&call->call_lock);
13817c478bd9Sstevel@tonic-gate 				if (call->call_reply == NULL)
13827c478bd9Sstevel@tonic-gate 					call->call_status = RPC_TIMEDOUT;
13837c478bd9Sstevel@tonic-gate 				mutex_exit(&call->call_lock);
13847c478bd9Sstevel@tonic-gate 				goto read_again;
13857c478bd9Sstevel@tonic-gate 			} else if (!AUTH_UNWRAP(h->cl_auth, xdrs,
13867c478bd9Sstevel@tonic-gate 			    xdr_results, resultsp)) {
13877c478bd9Sstevel@tonic-gate 				RPCLOG0(1, "clnt_cots_kcallit: validation "
13887c478bd9Sstevel@tonic-gate 				    "failure (unwrap)\n");
13897c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_CANTDECODERES;
13907c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
13917c478bd9Sstevel@tonic-gate 			}
13927c478bd9Sstevel@tonic-gate 		} else {
13937c478bd9Sstevel@tonic-gate 			/* set errno in case we can't recover */
13947c478bd9Sstevel@tonic-gate 			if (re_status != RPC_VERSMISMATCH &&
13957c478bd9Sstevel@tonic-gate 			    re_status != RPC_AUTHERROR &&
13967c478bd9Sstevel@tonic-gate 			    re_status != RPC_PROGVERSMISMATCH)
13977c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
13987c478bd9Sstevel@tonic-gate 
13997c478bd9Sstevel@tonic-gate 			if (re_status == RPC_AUTHERROR) {
14007c478bd9Sstevel@tonic-gate 				/*
1401a33e37fdSrg137905 				 * Maybe our credential need to be refreshed
14027c478bd9Sstevel@tonic-gate 				 */
1403a85a6121Srg137905 				if (cm_entry) {
1404a85a6121Srg137905 					/*
1405a85a6121Srg137905 					 * There is the potential that the
1406a85a6121Srg137905 					 * cm_entry has/will be marked dead,
1407a33e37fdSrg137905 					 * so drop the connection altogether,
1408a33e37fdSrg137905 					 * force REFRESH to establish new
1409a33e37fdSrg137905 					 * connection.
1410a85a6121Srg137905 					 */
1411a33e37fdSrg137905 					connmgr_cancelconn(cm_entry);
1412a85a6121Srg137905 					cm_entry = NULL;
1413a85a6121Srg137905 				}
1414a85a6121Srg137905 
1415a85a6121Srg137905 				(void) xdr_rpc_free_verifier(xdrs,
1416a85a6121Srg137905 				    &reply_msg);
1417cf98b944SMarcel Telka 				XDR_DESTROY(xdrs);
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 				if (p->cku_flags & CKU_ONQUEUE) {
14207c478bd9Sstevel@tonic-gate 					call_table_remove(call);
14217c478bd9Sstevel@tonic-gate 					p->cku_flags &= ~CKU_ONQUEUE;
14227c478bd9Sstevel@tonic-gate 				}
1423a85a6121Srg137905 				RPCLOG(64,
1424a85a6121Srg137905 				    "clnt_cots_kcallit: AUTH_ERROR, xid"
1425a85a6121Srg137905 				    " 0x%x removed off dispatch list\n",
14267c478bd9Sstevel@tonic-gate 				    p->cku_xid);
14277c478bd9Sstevel@tonic-gate 				if (call->call_reply) {
14287c478bd9Sstevel@tonic-gate 					freemsg(call->call_reply);
14297c478bd9Sstevel@tonic-gate 					call->call_reply = NULL;
14307c478bd9Sstevel@tonic-gate 				}
1431a85a6121Srg137905 
143228a15eaaSMarcel Telka 				if ((refreshes > 0) &&
143328a15eaaSMarcel Telka 				    AUTH_REFRESH(h->cl_auth, &reply_msg,
143428a15eaaSMarcel Telka 				    p->cku_cred)) {
143528a15eaaSMarcel Telka 					refreshes--;
143628a15eaaSMarcel Telka 					freemsg(mp);
143728a15eaaSMarcel Telka 					mp = NULL;
143828a15eaaSMarcel Telka 
1439a85a6121Srg137905 					COTSRCSTAT_INCR(p->cku_stats,
1440a85a6121Srg137905 					    rcbadcalls);
1441a85a6121Srg137905 					COTSRCSTAT_INCR(p->cku_stats,
1442a85a6121Srg137905 					    rcnewcreds);
14437c478bd9Sstevel@tonic-gate 					goto call_again;
1444a33e37fdSrg137905 				}
1445a33e37fdSrg137905 
14467c478bd9Sstevel@tonic-gate 				/*
14477c478bd9Sstevel@tonic-gate 				 * We have used the client handle to
14487c478bd9Sstevel@tonic-gate 				 * do an AUTH_REFRESH and the RPC status may
14497c478bd9Sstevel@tonic-gate 				 * be set to RPC_SUCCESS; Let's make sure to
14507c478bd9Sstevel@tonic-gate 				 * set it to RPC_AUTHERROR.
14517c478bd9Sstevel@tonic-gate 				 */
14527c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_AUTHERROR;
1453a33e37fdSrg137905 
14547c478bd9Sstevel@tonic-gate 				/*
14557c478bd9Sstevel@tonic-gate 				 * Map recoverable and unrecoverable
14567c478bd9Sstevel@tonic-gate 				 * authentication errors to appropriate errno
14577c478bd9Sstevel@tonic-gate 				 */
14587c478bd9Sstevel@tonic-gate 				switch (p->cku_err.re_why) {
145978598ee3Snd150628 				case AUTH_TOOWEAK:
146078598ee3Snd150628 					/*
1461a85a6121Srg137905 					 * This could be a failure where the
1462a85a6121Srg137905 					 * server requires use of a reserved
1463a85a6121Srg137905 					 * port,  check and optionally set the
1464a85a6121Srg137905 					 * client handle useresvport trying
1465a85a6121Srg137905 					 * one more time. Next go round we
1466a85a6121Srg137905 					 * fall out with the tooweak error.
146778598ee3Snd150628 					 */
146878598ee3Snd150628 					if (p->cku_useresvport != 1) {
146978598ee3Snd150628 						p->cku_useresvport = 1;
147078598ee3Snd150628 						p->cku_xid = 0;
147178598ee3Snd150628 						freemsg(mp);
147228a15eaaSMarcel Telka 						mp = NULL;
147378598ee3Snd150628 						goto call_again;
147478598ee3Snd150628 					}
147578598ee3Snd150628 					/* FALLTHRU */
14767c478bd9Sstevel@tonic-gate 				case AUTH_BADCRED:
14777c478bd9Sstevel@tonic-gate 				case AUTH_BADVERF:
14787c478bd9Sstevel@tonic-gate 				case AUTH_INVALIDRESP:
14797c478bd9Sstevel@tonic-gate 				case AUTH_FAILED:
14807c478bd9Sstevel@tonic-gate 				case RPCSEC_GSS_NOCRED:
14817c478bd9Sstevel@tonic-gate 				case RPCSEC_GSS_FAILED:
14827c478bd9Sstevel@tonic-gate 						p->cku_err.re_errno = EACCES;
14837c478bd9Sstevel@tonic-gate 						break;
14847c478bd9Sstevel@tonic-gate 				case AUTH_REJECTEDCRED:
14857c478bd9Sstevel@tonic-gate 				case AUTH_REJECTEDVERF:
14867c478bd9Sstevel@tonic-gate 				default:	p->cku_err.re_errno = EIO;
14877c478bd9Sstevel@tonic-gate 						break;
14887c478bd9Sstevel@tonic-gate 				}
14897c478bd9Sstevel@tonic-gate 				RPCLOG(1, "clnt_cots_kcallit : authentication"
14907c478bd9Sstevel@tonic-gate 				    " failed with RPC_AUTHERROR of type %d\n",
14917c478bd9Sstevel@tonic-gate 				    (int)p->cku_err.re_why);
149228a15eaaSMarcel Telka 				goto cots_done;
14937c478bd9Sstevel@tonic-gate 			}
14947c478bd9Sstevel@tonic-gate 		}
14957c478bd9Sstevel@tonic-gate 	} else {
14967c478bd9Sstevel@tonic-gate 		/* reply didn't decode properly. */
14977c478bd9Sstevel@tonic-gate 		p->cku_err.re_status = RPC_CANTDECODERES;
14987c478bd9Sstevel@tonic-gate 		p->cku_err.re_errno = EIO;
14997c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcallit: decode failure\n");
15007c478bd9Sstevel@tonic-gate 	}
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate 	(void) xdr_rpc_free_verifier(xdrs, &reply_msg);
1503cf98b944SMarcel Telka 	XDR_DESTROY(xdrs);
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate 	if (p->cku_flags & CKU_ONQUEUE) {
15067c478bd9Sstevel@tonic-gate 		call_table_remove(call);
15077c478bd9Sstevel@tonic-gate 		p->cku_flags &= ~CKU_ONQUEUE;
15087c478bd9Sstevel@tonic-gate 	}
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate 	RPCLOG(64, "clnt_cots_kcallit: xid 0x%x taken off dispatch list",
15117c478bd9Sstevel@tonic-gate 	    p->cku_xid);
15127c478bd9Sstevel@tonic-gate 	RPCLOG(64, " status is %s\n", clnt_sperrno(p->cku_err.re_status));
15137c478bd9Sstevel@tonic-gate cots_done:
15147c478bd9Sstevel@tonic-gate 	if (cm_entry)
15157c478bd9Sstevel@tonic-gate 		connmgr_release(cm_entry);
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 	if (mp != NULL)
15187c478bd9Sstevel@tonic-gate 		freemsg(mp);
15197c478bd9Sstevel@tonic-gate 	if ((p->cku_flags & CKU_ONQUEUE) == 0 && call->call_reply) {
15207c478bd9Sstevel@tonic-gate 		freemsg(call->call_reply);
15217c478bd9Sstevel@tonic-gate 		call->call_reply = NULL;
15227c478bd9Sstevel@tonic-gate 	}
15237c478bd9Sstevel@tonic-gate 	if (p->cku_err.re_status != RPC_SUCCESS) {
15247c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "clnt_cots_kcallit: tail-end failure\n");
15257c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(p->cku_stats, rcbadcalls);
15267c478bd9Sstevel@tonic-gate 	}
15277c478bd9Sstevel@tonic-gate 
15287c478bd9Sstevel@tonic-gate 	/*
15297c478bd9Sstevel@tonic-gate 	 * No point in delaying if the zone is going away.
15307c478bd9Sstevel@tonic-gate 	 */
15317c478bd9Sstevel@tonic-gate 	if (delay_first == TRUE &&
15327c478bd9Sstevel@tonic-gate 	    !(zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)) {
15337c478bd9Sstevel@tonic-gate 		if (clnt_delay(ticks, h->cl_nosignal) == EINTR) {
15347c478bd9Sstevel@tonic-gate 			p->cku_err.re_errno = EINTR;
15357c478bd9Sstevel@tonic-gate 			p->cku_err.re_status = RPC_INTR;
15367c478bd9Sstevel@tonic-gate 		}
15377c478bd9Sstevel@tonic-gate 	}
15387c478bd9Sstevel@tonic-gate 	return (p->cku_err.re_status);
15397c478bd9Sstevel@tonic-gate }
15407c478bd9Sstevel@tonic-gate 
15417c478bd9Sstevel@tonic-gate /*
15427c478bd9Sstevel@tonic-gate  * Kinit routine for cots.  This sets up the correct operations in
15437c478bd9Sstevel@tonic-gate  * the client handle, as the handle may have previously been a clts
15447c478bd9Sstevel@tonic-gate  * handle, and clears the xid field so there is no way a new call
15457c478bd9Sstevel@tonic-gate  * could be mistaken for a retry.  It also sets in the handle the
15467c478bd9Sstevel@tonic-gate  * information that is passed at create/kinit time but needed at
15477c478bd9Sstevel@tonic-gate  * call time, as cots creates the transport at call time - device,
15487c478bd9Sstevel@tonic-gate  * address of the server, protocol family.
15497c478bd9Sstevel@tonic-gate  */
15507c478bd9Sstevel@tonic-gate void
clnt_cots_kinit(CLIENT * h,dev_t dev,int family,struct netbuf * addr,int max_msgsize,cred_t * cred)15517c478bd9Sstevel@tonic-gate clnt_cots_kinit(CLIENT *h, dev_t dev, int family, struct netbuf *addr,
15527c478bd9Sstevel@tonic-gate     int max_msgsize, cred_t *cred)
15537c478bd9Sstevel@tonic-gate {
15547c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
15557c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
15567c478bd9Sstevel@tonic-gate 	calllist_t *call = &p->cku_call;
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	h->cl_ops = &tcp_ops;
15597c478bd9Sstevel@tonic-gate 	if (p->cku_flags & CKU_ONQUEUE) {
15607c478bd9Sstevel@tonic-gate 		call_table_remove(call);
15617c478bd9Sstevel@tonic-gate 		p->cku_flags &= ~CKU_ONQUEUE;
15627c478bd9Sstevel@tonic-gate 		RPCLOG(64, "clnt_cots_kinit: removing call for xid 0x%x from"
15637c478bd9Sstevel@tonic-gate 		    " dispatch list\n", p->cku_xid);
15647c478bd9Sstevel@tonic-gate 	}
15657c478bd9Sstevel@tonic-gate 
15667c478bd9Sstevel@tonic-gate 	if (call->call_reply != NULL) {
15677c478bd9Sstevel@tonic-gate 		freemsg(call->call_reply);
15687c478bd9Sstevel@tonic-gate 		call->call_reply = NULL;
15697c478bd9Sstevel@tonic-gate 	}
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 	call->call_bucket = NULL;
15727c478bd9Sstevel@tonic-gate 	call->call_hash = 0;
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 	/*
15757c478bd9Sstevel@tonic-gate 	 * We don't clear cku_flags here, because clnt_cots_kcallit()
15767c478bd9Sstevel@tonic-gate 	 * takes care of handling the cku_flags reset.
15777c478bd9Sstevel@tonic-gate 	 */
15787c478bd9Sstevel@tonic-gate 	p->cku_xid = 0;
15797c478bd9Sstevel@tonic-gate 	p->cku_device = dev;
15807c478bd9Sstevel@tonic-gate 	p->cku_addrfmly = family;
15817c478bd9Sstevel@tonic-gate 	p->cku_cred = cred;
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 	if (p->cku_addr.maxlen < addr->len) {
15847c478bd9Sstevel@tonic-gate 		kmem_free(p->cku_addr.buf, p->cku_addr.maxlen);
15857c478bd9Sstevel@tonic-gate 		p->cku_addr.buf = kmem_zalloc(addr->maxlen, KM_SLEEP);
15867c478bd9Sstevel@tonic-gate 		p->cku_addr.maxlen = addr->maxlen;
15877c478bd9Sstevel@tonic-gate 	}
15887c478bd9Sstevel@tonic-gate 
15897c478bd9Sstevel@tonic-gate 	p->cku_addr.len = addr->len;
15907c478bd9Sstevel@tonic-gate 	bcopy(addr->buf, p->cku_addr.buf, addr->len);
15917c478bd9Sstevel@tonic-gate 
15927c478bd9Sstevel@tonic-gate 	/*
15937c478bd9Sstevel@tonic-gate 	 * If the current sanity check size in rpcmod is smaller
15947c478bd9Sstevel@tonic-gate 	 * than the size needed, then increase the sanity check.
15957c478bd9Sstevel@tonic-gate 	 */
15967c478bd9Sstevel@tonic-gate 	if (max_msgsize != 0 && clnt_max_msg_sizep != NULL &&
15977c478bd9Sstevel@tonic-gate 	    max_msgsize > *clnt_max_msg_sizep) {
15987c478bd9Sstevel@tonic-gate 		mutex_enter(&clnt_max_msg_lock);
15997c478bd9Sstevel@tonic-gate 		if (max_msgsize > *clnt_max_msg_sizep)
16007c478bd9Sstevel@tonic-gate 			*clnt_max_msg_sizep = max_msgsize;
16017c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_max_msg_lock);
16027c478bd9Sstevel@tonic-gate 	}
16037c478bd9Sstevel@tonic-gate }
16047c478bd9Sstevel@tonic-gate 
16057c478bd9Sstevel@tonic-gate /*
16067c478bd9Sstevel@tonic-gate  * ksettimers is a no-op for cots, with the exception of setting the xid.
16077c478bd9Sstevel@tonic-gate  */
16087c478bd9Sstevel@tonic-gate /* ARGSUSED */
16097c478bd9Sstevel@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)16107c478bd9Sstevel@tonic-gate clnt_cots_ksettimers(CLIENT *h, struct rpc_timers *t, struct rpc_timers *all,
1611c16316e1SMarcel Telka     int minimum, void (*feedback)(int, int, caddr_t), caddr_t arg, uint32_t xid)
16127c478bd9Sstevel@tonic-gate {
16137c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
16147c478bd9Sstevel@tonic-gate 	cku_private_t *p = htop(h);
16157c478bd9Sstevel@tonic-gate 
16167c478bd9Sstevel@tonic-gate 	if (xid)
16177c478bd9Sstevel@tonic-gate 		p->cku_xid = xid;
16187c478bd9Sstevel@tonic-gate 	COTSRCSTAT_INCR(p->cku_stats, rctimers);
16197c478bd9Sstevel@tonic-gate 	return (0);
16207c478bd9Sstevel@tonic-gate }
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate extern void rpc_poptimod(struct vnode *);
16237c478bd9Sstevel@tonic-gate extern int kstr_push(struct vnode *, char *);
16247c478bd9Sstevel@tonic-gate 
16257c478bd9Sstevel@tonic-gate int
conn_kstat_update(kstat_t * ksp,int rw)16267c478bd9Sstevel@tonic-gate conn_kstat_update(kstat_t *ksp, int rw)
16277c478bd9Sstevel@tonic-gate {
16287c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
16297c478bd9Sstevel@tonic-gate 	struct cm_kstat_xprt *cm_ksp_data;
16307c478bd9Sstevel@tonic-gate 	uchar_t *b;
16317c478bd9Sstevel@tonic-gate 	char *fbuf;
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
16347c478bd9Sstevel@tonic-gate 		return (EACCES);
16357c478bd9Sstevel@tonic-gate 	if (ksp == NULL || ksp->ks_private == NULL)
16367c478bd9Sstevel@tonic-gate 		return (EIO);
16377c478bd9Sstevel@tonic-gate 	cm_entry  = (struct cm_xprt *)ksp->ks_private;
16387c478bd9Sstevel@tonic-gate 	cm_ksp_data = (struct cm_kstat_xprt *)ksp->ks_data;
16397c478bd9Sstevel@tonic-gate 
16407c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_wq.value.ui32 = (uint32_t)(uintptr_t)cm_entry->x_wq;
16417c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_family.value.ui32 = cm_entry->x_family;
16427c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_rdev.value.ui32 = (uint32_t)cm_entry->x_rdev;
16437c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_time.value.ui32 = cm_entry->x_time;
16447c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_ref.value.ui32 = cm_entry->x_ref;
16457c478bd9Sstevel@tonic-gate 	cm_ksp_data->x_state.value.ui32 = cm_entry->x_state_flags;
16467c478bd9Sstevel@tonic-gate 
16477c478bd9Sstevel@tonic-gate 	if (cm_entry->x_server.buf) {
1648a1b5e537Sbmc 		fbuf = cm_ksp_data->x_server.value.str.addr.ptr;
16497c478bd9Sstevel@tonic-gate 		if (cm_entry->x_family == AF_INET &&
16507c478bd9Sstevel@tonic-gate 		    cm_entry->x_server.len ==
16517c478bd9Sstevel@tonic-gate 		    sizeof (struct sockaddr_in)) {
16527c478bd9Sstevel@tonic-gate 			struct sockaddr_in  *sa;
16537c478bd9Sstevel@tonic-gate 			sa = (struct sockaddr_in *)
16547c478bd9Sstevel@tonic-gate 				cm_entry->x_server.buf;
16557c478bd9Sstevel@tonic-gate 			b = (uchar_t *)&sa->sin_addr;
16567c478bd9Sstevel@tonic-gate 			(void) sprintf(fbuf,
1657c16316e1SMarcel Telka 			    "%d.%d.%d.%d", b[0] & 0xFF, b[1] & 0xFF,
16587c478bd9Sstevel@tonic-gate 			    b[2] & 0xFF, b[3] & 0xFF);
1659c16316e1SMarcel Telka 			cm_ksp_data->x_port.value.ui32 = ntohs(sa->sin_port);
16607c478bd9Sstevel@tonic-gate 		} else if (cm_entry->x_family == AF_INET6 &&
16617c478bd9Sstevel@tonic-gate 				cm_entry->x_server.len >=
16627c478bd9Sstevel@tonic-gate 				sizeof (struct sockaddr_in6)) {
16637c478bd9Sstevel@tonic-gate 			/* extract server IP address & port */
16647c478bd9Sstevel@tonic-gate 			struct sockaddr_in6 *sin6;
16657c478bd9Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)cm_entry->x_server.buf;
16667c478bd9Sstevel@tonic-gate 			(void) kinet_ntop6((uchar_t *)&sin6->sin6_addr, fbuf,
16677c478bd9Sstevel@tonic-gate 				INET6_ADDRSTRLEN);
1668c16316e1SMarcel Telka 			cm_ksp_data->x_port.value.ui32 = ntohs(sin6->sin6_port);
16697c478bd9Sstevel@tonic-gate 		} else {
16707c478bd9Sstevel@tonic-gate 			struct sockaddr_in  *sa;
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 			sa = (struct sockaddr_in *)cm_entry->x_server.buf;
16737c478bd9Sstevel@tonic-gate 			b = (uchar_t *)&sa->sin_addr;
16747c478bd9Sstevel@tonic-gate 			(void) sprintf(fbuf,
1675c16316e1SMarcel Telka 			    "%d.%d.%d.%d", b[0] & 0xFF, b[1] & 0xFF,
16767c478bd9Sstevel@tonic-gate 			    b[2] & 0xFF, b[3] & 0xFF);
16777c478bd9Sstevel@tonic-gate 		}
16787c478bd9Sstevel@tonic-gate 		KSTAT_NAMED_STR_BUFLEN(&cm_ksp_data->x_server) =
16797c478bd9Sstevel@tonic-gate 		    strlen(fbuf) + 1;
16807c478bd9Sstevel@tonic-gate 	}
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 	return (0);
16837c478bd9Sstevel@tonic-gate }
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate /*
16877c478bd9Sstevel@tonic-gate  * We want a version of delay which is interruptible by a UNIX signal
16887c478bd9Sstevel@tonic-gate  * Return EINTR if an interrupt occured.
16897c478bd9Sstevel@tonic-gate  */
16907c478bd9Sstevel@tonic-gate static int
clnt_delay(clock_t ticks,bool_t nosignal)16917c478bd9Sstevel@tonic-gate clnt_delay(clock_t ticks, bool_t nosignal)
16927c478bd9Sstevel@tonic-gate {
16937c478bd9Sstevel@tonic-gate 	if (nosignal == TRUE) {
16947c478bd9Sstevel@tonic-gate 		delay(ticks);
16957c478bd9Sstevel@tonic-gate 		return (0);
16967c478bd9Sstevel@tonic-gate 	}
16977c478bd9Sstevel@tonic-gate 	return (delay_sig(ticks));
16987c478bd9Sstevel@tonic-gate }
16997c478bd9Sstevel@tonic-gate 
17007c478bd9Sstevel@tonic-gate /*
17017c478bd9Sstevel@tonic-gate  * Wait for a connection until a timeout, or until we are
17027c478bd9Sstevel@tonic-gate  * signalled that there has been a connection state change.
17037c478bd9Sstevel@tonic-gate  */
17047c478bd9Sstevel@tonic-gate static enum clnt_stat
connmgr_cwait(struct cm_xprt * cm_entry,const struct timeval * waitp,bool_t nosignal)17057c478bd9Sstevel@tonic-gate connmgr_cwait(struct cm_xprt *cm_entry, const struct timeval *waitp,
17067c478bd9Sstevel@tonic-gate     bool_t nosignal)
17077c478bd9Sstevel@tonic-gate {
17087c478bd9Sstevel@tonic-gate 	bool_t interrupted;
17097c478bd9Sstevel@tonic-gate 	clock_t timout, cv_stat;
17107c478bd9Sstevel@tonic-gate 	enum clnt_stat clstat;
17117c478bd9Sstevel@tonic-gate 	unsigned int old_state;
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
17147c478bd9Sstevel@tonic-gate 	/*
17157c478bd9Sstevel@tonic-gate 	 * We wait for the transport connection to be made, or an
17167c478bd9Sstevel@tonic-gate 	 * indication that it could not be made.
17177c478bd9Sstevel@tonic-gate 	 */
17187c478bd9Sstevel@tonic-gate 	clstat = RPC_TIMEDOUT;
17197c478bd9Sstevel@tonic-gate 	interrupted = FALSE;
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	old_state = cm_entry->x_state_flags;
17227c478bd9Sstevel@tonic-gate 	/*
17237c478bd9Sstevel@tonic-gate 	 * Now loop until cv_timedwait{_sig} returns because of
17247c478bd9Sstevel@tonic-gate 	 * a signal(0) or timeout(-1) or cv_signal(>0). But it may be
17257c478bd9Sstevel@tonic-gate 	 * cv_signalled for various other reasons too. So loop
17267c478bd9Sstevel@tonic-gate 	 * until there is a state change on the connection.
17277c478bd9Sstevel@tonic-gate 	 */
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate 	timout = waitp->tv_sec * drv_usectohz(1000000) +
1730d3d50737SRafael Vanoni 	    drv_usectohz(waitp->tv_usec) + ddi_get_lbolt();
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 	if (nosignal) {
17337c478bd9Sstevel@tonic-gate 		while ((cv_stat = cv_timedwait(&cm_entry->x_conn_cv,
17347c478bd9Sstevel@tonic-gate 		    &connmgr_lock, timout)) > 0 &&
17357c478bd9Sstevel@tonic-gate 		    cm_entry->x_state_flags == old_state)
17367c478bd9Sstevel@tonic-gate 			;
17377c478bd9Sstevel@tonic-gate 	} else {
17387c478bd9Sstevel@tonic-gate 		while ((cv_stat = cv_timedwait_sig(&cm_entry->x_conn_cv,
17397c478bd9Sstevel@tonic-gate 		    &connmgr_lock, timout)) > 0 &&
17407c478bd9Sstevel@tonic-gate 		    cm_entry->x_state_flags == old_state)
17417c478bd9Sstevel@tonic-gate 			;
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 		if (cv_stat == 0) /* got intr signal? */
17447c478bd9Sstevel@tonic-gate 			interrupted = TRUE;
17457c478bd9Sstevel@tonic-gate 	}
17467c478bd9Sstevel@tonic-gate 
17477c478bd9Sstevel@tonic-gate 	if ((cm_entry->x_state_flags & (X_BADSTATES|X_CONNECTED)) ==
17487c478bd9Sstevel@tonic-gate 	    X_CONNECTED) {
17497c478bd9Sstevel@tonic-gate 		clstat = RPC_SUCCESS;
17507c478bd9Sstevel@tonic-gate 	} else {
17517c478bd9Sstevel@tonic-gate 		if (interrupted == TRUE)
17527c478bd9Sstevel@tonic-gate 			clstat = RPC_INTR;
17537c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_cwait: can't connect, error: %s\n",
17547c478bd9Sstevel@tonic-gate 		    clnt_sperrno(clstat));
17557c478bd9Sstevel@tonic-gate 	}
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 	return (clstat);
17587c478bd9Sstevel@tonic-gate }
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate /*
17617c478bd9Sstevel@tonic-gate  * Primary interface for how RPC grabs a connection.
17627c478bd9Sstevel@tonic-gate  */
17637c478bd9Sstevel@tonic-gate static struct cm_xprt *
connmgr_wrapget(struct netbuf * retryaddr,const struct timeval * waitp,cku_private_t * p)17647c478bd9Sstevel@tonic-gate connmgr_wrapget(
17657c478bd9Sstevel@tonic-gate 	struct netbuf *retryaddr,
17667c478bd9Sstevel@tonic-gate 	const struct timeval *waitp,
17677c478bd9Sstevel@tonic-gate 	cku_private_t *p)
17687c478bd9Sstevel@tonic-gate {
17697c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 	cm_entry = connmgr_get(retryaddr, waitp, &p->cku_addr, p->cku_addrfmly,
17727c478bd9Sstevel@tonic-gate 	    &p->cku_srcaddr, &p->cku_err, p->cku_device,
1773de8c4a14SErik Nordmark 	    p->cku_client.cl_nosignal, p->cku_useresvport, p->cku_cred);
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	if (cm_entry == NULL) {
17767c478bd9Sstevel@tonic-gate 		/*
17777c478bd9Sstevel@tonic-gate 		 * Re-map the call status to RPC_INTR if the err code is
17787c478bd9Sstevel@tonic-gate 		 * EINTR. This can happen if calls status is RPC_TLIERROR.
17797c478bd9Sstevel@tonic-gate 		 * However, don't re-map if signalling has been turned off.
17807c478bd9Sstevel@tonic-gate 		 * XXX Really need to create a separate thread whenever
17817c478bd9Sstevel@tonic-gate 		 * there isn't an existing connection.
17827c478bd9Sstevel@tonic-gate 		 */
17837c478bd9Sstevel@tonic-gate 		if (p->cku_err.re_errno == EINTR) {
17847c478bd9Sstevel@tonic-gate 			if (p->cku_client.cl_nosignal == TRUE)
17857c478bd9Sstevel@tonic-gate 				p->cku_err.re_errno = EIO;
17867c478bd9Sstevel@tonic-gate 			else
17877c478bd9Sstevel@tonic-gate 				p->cku_err.re_status = RPC_INTR;
17887c478bd9Sstevel@tonic-gate 		}
17897c478bd9Sstevel@tonic-gate 	}
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 	return (cm_entry);
17927c478bd9Sstevel@tonic-gate }
17937c478bd9Sstevel@tonic-gate 
17947c478bd9Sstevel@tonic-gate /*
17957c478bd9Sstevel@tonic-gate  * Obtains a transport to the server specified in addr.  If a suitable transport
17967c478bd9Sstevel@tonic-gate  * does not already exist in the list of cached transports, a new connection
17977c478bd9Sstevel@tonic-gate  * is created, connected, and added to the list. The connection is for sending
17987c478bd9Sstevel@tonic-gate  * only - the reply message may come back on another transport connection.
179981dbf0b5SDai Ngo  *
180081dbf0b5SDai Ngo  * To implement round-robin load balancing with multiple client connections,
180181dbf0b5SDai Ngo  * the last entry on the list is always selected. Once the entry is selected
180281dbf0b5SDai Ngo  * it's re-inserted to the head of the list.
18037c478bd9Sstevel@tonic-gate  */
18047c478bd9Sstevel@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)18057c478bd9Sstevel@tonic-gate connmgr_get(
18067c478bd9Sstevel@tonic-gate 	struct netbuf	*retryaddr,
18077c478bd9Sstevel@tonic-gate 	const struct timeval	*waitp,	/* changed to a ptr to converse stack */
18087c478bd9Sstevel@tonic-gate 	struct netbuf	*destaddr,
18097c478bd9Sstevel@tonic-gate 	int		addrfmly,
18107c478bd9Sstevel@tonic-gate 	struct netbuf	*srcaddr,
18117c478bd9Sstevel@tonic-gate 	struct rpc_err	*rpcerr,
18127c478bd9Sstevel@tonic-gate 	dev_t		device,
18137c478bd9Sstevel@tonic-gate 	bool_t		nosignal,
1814de8c4a14SErik Nordmark 	int		useresvport,
1815de8c4a14SErik Nordmark 	cred_t		*cr)
18167c478bd9Sstevel@tonic-gate {
18177c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
18187c478bd9Sstevel@tonic-gate 	struct cm_xprt *lru_entry;
181981dbf0b5SDai Ngo 	struct cm_xprt **cmp, **prev;
18207c478bd9Sstevel@tonic-gate 	queue_t *wq;
18217c478bd9Sstevel@tonic-gate 	TIUSER *tiptr;
18227c478bd9Sstevel@tonic-gate 	int i;
18237c478bd9Sstevel@tonic-gate 	int retval;
18247c478bd9Sstevel@tonic-gate 	int tidu_size;
18257c478bd9Sstevel@tonic-gate 	bool_t	connected;
1826108322fbScarlsonj 	zoneid_t zoneid = rpc_zoneid();
18277c478bd9Sstevel@tonic-gate 
18287c478bd9Sstevel@tonic-gate 	/*
18297c478bd9Sstevel@tonic-gate 	 * If the call is not a retry, look for a transport entry that
18307c478bd9Sstevel@tonic-gate 	 * goes to the server of interest.
18317c478bd9Sstevel@tonic-gate 	 */
18327c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
18337c478bd9Sstevel@tonic-gate 
18347c478bd9Sstevel@tonic-gate 	if (retryaddr == NULL) {
18357c478bd9Sstevel@tonic-gate use_new_conn:
18367c478bd9Sstevel@tonic-gate 		i = 0;
18377c478bd9Sstevel@tonic-gate 		cm_entry = lru_entry = NULL;
18387c478bd9Sstevel@tonic-gate 
183981dbf0b5SDai Ngo 		prev = cmp = &cm_hd;
18407c478bd9Sstevel@tonic-gate 		while ((cm_entry = *cmp) != NULL) {
18417c478bd9Sstevel@tonic-gate 			ASSERT(cm_entry != cm_entry->x_next);
18427c478bd9Sstevel@tonic-gate 			/*
18437c478bd9Sstevel@tonic-gate 			 * Garbage collect conections that are marked
18447c478bd9Sstevel@tonic-gate 			 * for needs disconnect.
18457c478bd9Sstevel@tonic-gate 			 */
18467c478bd9Sstevel@tonic-gate 			if (cm_entry->x_needdis) {
1847418d27f3Sshepler 				CONN_HOLD(cm_entry);
18487c478bd9Sstevel@tonic-gate 				connmgr_dis_and_wait(cm_entry);
1849418d27f3Sshepler 				connmgr_release(cm_entry);
18507c478bd9Sstevel@tonic-gate 				/*
18517c478bd9Sstevel@tonic-gate 				 * connmgr_lock could have been
18527c478bd9Sstevel@tonic-gate 				 * dropped for the disconnect
18537c478bd9Sstevel@tonic-gate 				 * processing so start over.
18547c478bd9Sstevel@tonic-gate 				 */
18557c478bd9Sstevel@tonic-gate 				goto use_new_conn;
18567c478bd9Sstevel@tonic-gate 			}
18577c478bd9Sstevel@tonic-gate 
18587c478bd9Sstevel@tonic-gate 			/*
18597c478bd9Sstevel@tonic-gate 			 * Garbage collect the dead connections that have
18607c478bd9Sstevel@tonic-gate 			 * no threads working on them.
18617c478bd9Sstevel@tonic-gate 			 */
18627c478bd9Sstevel@tonic-gate 			if ((cm_entry->x_state_flags & (X_DEAD|X_THREAD)) ==
18637c478bd9Sstevel@tonic-gate 			    X_DEAD) {
18648f7c43eaSmaheshvs 				mutex_enter(&cm_entry->x_lock);
18658f7c43eaSmaheshvs 				if (cm_entry->x_ref != 0) {
18668f7c43eaSmaheshvs 					/*
18678f7c43eaSmaheshvs 					 * Currently in use.
18688f7c43eaSmaheshvs 					 * Cleanup later.
18698f7c43eaSmaheshvs 					 */
18708f7c43eaSmaheshvs 					cmp = &cm_entry->x_next;
18718f7c43eaSmaheshvs 					mutex_exit(&cm_entry->x_lock);
18728f7c43eaSmaheshvs 					continue;
18738f7c43eaSmaheshvs 				}
18748f7c43eaSmaheshvs 				mutex_exit(&cm_entry->x_lock);
18757c478bd9Sstevel@tonic-gate 				*cmp = cm_entry->x_next;
18767c478bd9Sstevel@tonic-gate 				mutex_exit(&connmgr_lock);
18777c478bd9Sstevel@tonic-gate 				connmgr_close(cm_entry);
18787c478bd9Sstevel@tonic-gate 				mutex_enter(&connmgr_lock);
18797c478bd9Sstevel@tonic-gate 				goto use_new_conn;
18807c478bd9Sstevel@tonic-gate 			}
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate 			if ((cm_entry->x_state_flags & X_BADSTATES) == 0 &&
18847c478bd9Sstevel@tonic-gate 			    cm_entry->x_zoneid == zoneid &&
18857c478bd9Sstevel@tonic-gate 			    cm_entry->x_rdev == device &&
18867c478bd9Sstevel@tonic-gate 			    destaddr->len == cm_entry->x_server.len &&
18877c478bd9Sstevel@tonic-gate 			    bcmp(destaddr->buf, cm_entry->x_server.buf,
18887c478bd9Sstevel@tonic-gate 			    destaddr->len) == 0) {
18897c478bd9Sstevel@tonic-gate 				/*
18907c478bd9Sstevel@tonic-gate 				 * If the matching entry isn't connected,
18917c478bd9Sstevel@tonic-gate 				 * attempt to reconnect it.
18927c478bd9Sstevel@tonic-gate 				 */
18937c478bd9Sstevel@tonic-gate 				if (cm_entry->x_connected == FALSE) {
18947c478bd9Sstevel@tonic-gate 					/*
18957c478bd9Sstevel@tonic-gate 					 * We don't go through trying
18967c478bd9Sstevel@tonic-gate 					 * to find the least recently
18977c478bd9Sstevel@tonic-gate 					 * used connected because
18987c478bd9Sstevel@tonic-gate 					 * connmgr_reconnect() briefly
18997c478bd9Sstevel@tonic-gate 					 * dropped the connmgr_lock,
19007c478bd9Sstevel@tonic-gate 					 * allowing a window for our
19017c478bd9Sstevel@tonic-gate 					 * accounting to be messed up.
19027c478bd9Sstevel@tonic-gate 					 * In any case, a re-connected
19037c478bd9Sstevel@tonic-gate 					 * connection is as good as
19047c478bd9Sstevel@tonic-gate 					 * a LRU connection.
19057c478bd9Sstevel@tonic-gate 					 */
19067c478bd9Sstevel@tonic-gate 					return (connmgr_wrapconnect(cm_entry,
19077c478bd9Sstevel@tonic-gate 					    waitp, destaddr, addrfmly, srcaddr,
1908de8c4a14SErik Nordmark 					    rpcerr, TRUE, nosignal, cr));
19097c478bd9Sstevel@tonic-gate 				}
19107c478bd9Sstevel@tonic-gate 				i++;
191181dbf0b5SDai Ngo 
191281dbf0b5SDai Ngo 				/* keep track of the last entry */
19137c478bd9Sstevel@tonic-gate 				lru_entry = cm_entry;
191481dbf0b5SDai Ngo 				prev = cmp;
19157c478bd9Sstevel@tonic-gate 			}
19167c478bd9Sstevel@tonic-gate 			cmp = &cm_entry->x_next;
19177c478bd9Sstevel@tonic-gate 		}
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 		if (i > clnt_max_conns) {
19207c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_get: too many conns, dooming entry"
19217c478bd9Sstevel@tonic-gate 			    " %p\n", (void *)lru_entry->x_tiptr);
19227c478bd9Sstevel@tonic-gate 			lru_entry->x_doomed = TRUE;
19237c478bd9Sstevel@tonic-gate 			goto use_new_conn;
19247c478bd9Sstevel@tonic-gate 		}
19257c478bd9Sstevel@tonic-gate 
19267c478bd9Sstevel@tonic-gate 		/*
19277c478bd9Sstevel@tonic-gate 		 * If we are at the maximum number of connections to
19287c478bd9Sstevel@tonic-gate 		 * the server, hand back the least recently used one.
19297c478bd9Sstevel@tonic-gate 		 */
19307c478bd9Sstevel@tonic-gate 		if (i == clnt_max_conns) {
19317c478bd9Sstevel@tonic-gate 			/*
19327c478bd9Sstevel@tonic-gate 			 * Copy into the handle the source address of
19337c478bd9Sstevel@tonic-gate 			 * the connection, which we will use in case of
19347c478bd9Sstevel@tonic-gate 			 * a later retry.
19357c478bd9Sstevel@tonic-gate 			 */
19367c478bd9Sstevel@tonic-gate 			if (srcaddr->len != lru_entry->x_src.len) {
1937f67d64d9SMarcel Telka 				kmem_free(srcaddr->buf, srcaddr->maxlen);
19384b6bffb4SDan McDonald 				ASSERT(lru_entry->x_src.len != 0);
19394b6bffb4SDan McDonald 				srcaddr->buf = kmem_alloc(
19407c478bd9Sstevel@tonic-gate 				    lru_entry->x_src.len, KM_SLEEP);
19417c478bd9Sstevel@tonic-gate 				srcaddr->maxlen = srcaddr->len =
19427c478bd9Sstevel@tonic-gate 				    lru_entry->x_src.len;
19437c478bd9Sstevel@tonic-gate 			}
19447c478bd9Sstevel@tonic-gate 			bcopy(lru_entry->x_src.buf, srcaddr->buf, srcaddr->len);
19457c478bd9Sstevel@tonic-gate 			RPCLOG(2, "connmgr_get: call going out on %p\n",
19467c478bd9Sstevel@tonic-gate 			    (void *)lru_entry);
1947d3d50737SRafael Vanoni 			lru_entry->x_time = ddi_get_lbolt();
19487c478bd9Sstevel@tonic-gate 			CONN_HOLD(lru_entry);
194981dbf0b5SDai Ngo 
195081dbf0b5SDai Ngo 			if ((i > 1) && (prev != &cm_hd)) {
195181dbf0b5SDai Ngo 				/*
195281dbf0b5SDai Ngo 				 * remove and re-insert entry at head of list.
195381dbf0b5SDai Ngo 				 */
195481dbf0b5SDai Ngo 				*prev = lru_entry->x_next;
195581dbf0b5SDai Ngo 				lru_entry->x_next = cm_hd;
195681dbf0b5SDai Ngo 				cm_hd = lru_entry;
195781dbf0b5SDai Ngo 			}
195881dbf0b5SDai Ngo 
19597c478bd9Sstevel@tonic-gate 			mutex_exit(&connmgr_lock);
19607c478bd9Sstevel@tonic-gate 			return (lru_entry);
19617c478bd9Sstevel@tonic-gate 		}
19627c478bd9Sstevel@tonic-gate 
19637c478bd9Sstevel@tonic-gate 	} else {
19647c478bd9Sstevel@tonic-gate 		/*
19657c478bd9Sstevel@tonic-gate 		 * This is the retry case (retryaddr != NULL).  Retries must
19667c478bd9Sstevel@tonic-gate 		 * be sent on the same source port as the original call.
19677c478bd9Sstevel@tonic-gate 		 */
19687c478bd9Sstevel@tonic-gate 
19697c478bd9Sstevel@tonic-gate 		/*
19707c478bd9Sstevel@tonic-gate 		 * Walk the list looking for a connection with a source address
19717c478bd9Sstevel@tonic-gate 		 * that matches the retry address.
19727c478bd9Sstevel@tonic-gate 		 */
19733807480aSGerald Thornbrugh start_retry_loop:
19747c478bd9Sstevel@tonic-gate 		cmp = &cm_hd;
19757c478bd9Sstevel@tonic-gate 		while ((cm_entry = *cmp) != NULL) {
19767c478bd9Sstevel@tonic-gate 			ASSERT(cm_entry != cm_entry->x_next);
19773807480aSGerald Thornbrugh 
19783807480aSGerald Thornbrugh 			/*
19793807480aSGerald Thornbrugh 			 * determine if this connection matches the passed
19803807480aSGerald Thornbrugh 			 * in retry address.  If it does not match, advance
19813807480aSGerald Thornbrugh 			 * to the next element on the list.
19823807480aSGerald Thornbrugh 			 */
19837c478bd9Sstevel@tonic-gate 			if (zoneid != cm_entry->x_zoneid ||
19847c478bd9Sstevel@tonic-gate 			    device != cm_entry->x_rdev ||
19857c478bd9Sstevel@tonic-gate 			    retryaddr->len != cm_entry->x_src.len ||
19867c478bd9Sstevel@tonic-gate 			    bcmp(retryaddr->buf, cm_entry->x_src.buf,
19877c478bd9Sstevel@tonic-gate 			    retryaddr->len) != 0) {
19887c478bd9Sstevel@tonic-gate 				cmp = &cm_entry->x_next;
19897c478bd9Sstevel@tonic-gate 				continue;
19907c478bd9Sstevel@tonic-gate 			}
19913807480aSGerald Thornbrugh 			/*
19923807480aSGerald Thornbrugh 			 * Garbage collect conections that are marked
19933807480aSGerald Thornbrugh 			 * for needs disconnect.
19943807480aSGerald Thornbrugh 			 */
19953807480aSGerald Thornbrugh 			if (cm_entry->x_needdis) {
19963807480aSGerald Thornbrugh 				CONN_HOLD(cm_entry);
19973807480aSGerald Thornbrugh 				connmgr_dis_and_wait(cm_entry);
19983807480aSGerald Thornbrugh 				connmgr_release(cm_entry);
19993807480aSGerald Thornbrugh 				/*
20003807480aSGerald Thornbrugh 				 * connmgr_lock could have been
20013807480aSGerald Thornbrugh 				 * dropped for the disconnect
20023807480aSGerald Thornbrugh 				 * processing so start over.
20033807480aSGerald Thornbrugh 				 */
20043807480aSGerald Thornbrugh 				goto start_retry_loop;
20053807480aSGerald Thornbrugh 			}
20063807480aSGerald Thornbrugh 			/*
20073807480aSGerald Thornbrugh 			 * Garbage collect the dead connections that have
20083807480aSGerald Thornbrugh 			 * no threads working on them.
20093807480aSGerald Thornbrugh 			 */
20103807480aSGerald Thornbrugh 			if ((cm_entry->x_state_flags & (X_DEAD|X_THREAD)) ==
20113807480aSGerald Thornbrugh 			    X_DEAD) {
20123807480aSGerald Thornbrugh 				mutex_enter(&cm_entry->x_lock);
20133807480aSGerald Thornbrugh 				if (cm_entry->x_ref != 0) {
20143807480aSGerald Thornbrugh 					/*
20153807480aSGerald Thornbrugh 					 * Currently in use.
20163807480aSGerald Thornbrugh 					 * Cleanup later.
20173807480aSGerald Thornbrugh 					 */
20183807480aSGerald Thornbrugh 					cmp = &cm_entry->x_next;
20193807480aSGerald Thornbrugh 					mutex_exit(&cm_entry->x_lock);
20203807480aSGerald Thornbrugh 					continue;
20213807480aSGerald Thornbrugh 				}
20223807480aSGerald Thornbrugh 				mutex_exit(&cm_entry->x_lock);
20233807480aSGerald Thornbrugh 				*cmp = cm_entry->x_next;
20243807480aSGerald Thornbrugh 				mutex_exit(&connmgr_lock);
20253807480aSGerald Thornbrugh 				connmgr_close(cm_entry);
20263807480aSGerald Thornbrugh 				mutex_enter(&connmgr_lock);
20273807480aSGerald Thornbrugh 				goto start_retry_loop;
20283807480aSGerald Thornbrugh 			}
20297c478bd9Sstevel@tonic-gate 
20307c478bd9Sstevel@tonic-gate 			/*
20317c478bd9Sstevel@tonic-gate 			 * Sanity check: if the connection with our source
20327c478bd9Sstevel@tonic-gate 			 * port is going to some other server, something went
20337c478bd9Sstevel@tonic-gate 			 * wrong, as we never delete connections (i.e. release
20347c478bd9Sstevel@tonic-gate 			 * ports) unless they have been idle.  In this case,
20357c478bd9Sstevel@tonic-gate 			 * it is probably better to send the call out using
20367c478bd9Sstevel@tonic-gate 			 * a new source address than to fail it altogether,
20377c478bd9Sstevel@tonic-gate 			 * since that port may never be released.
20387c478bd9Sstevel@tonic-gate 			 */
20397c478bd9Sstevel@tonic-gate 			if (destaddr->len != cm_entry->x_server.len ||
20407c478bd9Sstevel@tonic-gate 			    bcmp(destaddr->buf, cm_entry->x_server.buf,
20417c478bd9Sstevel@tonic-gate 			    destaddr->len) != 0) {
20427c478bd9Sstevel@tonic-gate 				RPCLOG(1, "connmgr_get: tiptr %p"
20437c478bd9Sstevel@tonic-gate 				    " is going to a different server"
20447c478bd9Sstevel@tonic-gate 				    " with the port that belongs"
20457c478bd9Sstevel@tonic-gate 				    " to us!\n", (void *)cm_entry->x_tiptr);
20467c478bd9Sstevel@tonic-gate 				retryaddr = NULL;
20477c478bd9Sstevel@tonic-gate 				goto use_new_conn;
20487c478bd9Sstevel@tonic-gate 			}
20497c478bd9Sstevel@tonic-gate 
20507c478bd9Sstevel@tonic-gate 			/*
20517c478bd9Sstevel@tonic-gate 			 * If the connection of interest is not connected and we
20527c478bd9Sstevel@tonic-gate 			 * can't reconnect it, then the server is probably
20537c478bd9Sstevel@tonic-gate 			 * still down.  Return NULL to the caller and let it
20547c478bd9Sstevel@tonic-gate 			 * retry later if it wants to.  We have a delay so the
20557c478bd9Sstevel@tonic-gate 			 * machine doesn't go into a tight retry loop.  If the
20567c478bd9Sstevel@tonic-gate 			 * entry was already connected, or the reconnected was
20577c478bd9Sstevel@tonic-gate 			 * successful, return this entry.
20587c478bd9Sstevel@tonic-gate 			 */
20597c478bd9Sstevel@tonic-gate 			if (cm_entry->x_connected == FALSE) {
20607c478bd9Sstevel@tonic-gate 				return (connmgr_wrapconnect(cm_entry,
20617c478bd9Sstevel@tonic-gate 				    waitp, destaddr, addrfmly, NULL,
2062de8c4a14SErik Nordmark 				    rpcerr, TRUE, nosignal, cr));
20637c478bd9Sstevel@tonic-gate 			} else {
20647c478bd9Sstevel@tonic-gate 				CONN_HOLD(cm_entry);
20657c478bd9Sstevel@tonic-gate 
2066d3d50737SRafael Vanoni 				cm_entry->x_time = ddi_get_lbolt();
20677c478bd9Sstevel@tonic-gate 				mutex_exit(&connmgr_lock);
20687c478bd9Sstevel@tonic-gate 				RPCLOG(2, "connmgr_get: found old "
20697c478bd9Sstevel@tonic-gate 				    "transport %p for retry\n",
20707c478bd9Sstevel@tonic-gate 				    (void *)cm_entry);
20717c478bd9Sstevel@tonic-gate 				return (cm_entry);
20727c478bd9Sstevel@tonic-gate 			}
20737c478bd9Sstevel@tonic-gate 		}
20747c478bd9Sstevel@tonic-gate 
20757c478bd9Sstevel@tonic-gate 		/*
20767c478bd9Sstevel@tonic-gate 		 * We cannot find an entry in the list for this retry.
20777c478bd9Sstevel@tonic-gate 		 * Either the entry has been removed temporarily to be
20787c478bd9Sstevel@tonic-gate 		 * reconnected by another thread, or the original call
20797c478bd9Sstevel@tonic-gate 		 * got a port but never got connected,
20807c478bd9Sstevel@tonic-gate 		 * and hence the transport never got put in the
20817c478bd9Sstevel@tonic-gate 		 * list.  Fall through to the "create new connection" code -
20827c478bd9Sstevel@tonic-gate 		 * the former case will fail there trying to rebind the port,
20837c478bd9Sstevel@tonic-gate 		 * and the later case (and any other pathological cases) will
20847c478bd9Sstevel@tonic-gate 		 * rebind and reconnect and not hang the client machine.
20857c478bd9Sstevel@tonic-gate 		 */
20867c478bd9Sstevel@tonic-gate 		RPCLOG0(8, "connmgr_get: no entry in list for retry\n");
20877c478bd9Sstevel@tonic-gate 	}
20887c478bd9Sstevel@tonic-gate 	/*
20897c478bd9Sstevel@tonic-gate 	 * Set up a transport entry in the connection manager's list.
20907c478bd9Sstevel@tonic-gate 	 */
20917c478bd9Sstevel@tonic-gate 	cm_entry = (struct cm_xprt *)
20927c478bd9Sstevel@tonic-gate 	    kmem_zalloc(sizeof (struct cm_xprt), KM_SLEEP);
20937c478bd9Sstevel@tonic-gate 
20944b6bffb4SDan McDonald 	cm_entry->x_server.buf = kmem_alloc(destaddr->len, KM_SLEEP);
20957c478bd9Sstevel@tonic-gate 	bcopy(destaddr->buf, cm_entry->x_server.buf, destaddr->len);
20967c478bd9Sstevel@tonic-gate 	cm_entry->x_server.len = cm_entry->x_server.maxlen = destaddr->len;
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate 	cm_entry->x_state_flags = X_THREAD;
20997c478bd9Sstevel@tonic-gate 	cm_entry->x_ref = 1;
21007c478bd9Sstevel@tonic-gate 	cm_entry->x_family = addrfmly;
21017c478bd9Sstevel@tonic-gate 	cm_entry->x_rdev = device;
21027c478bd9Sstevel@tonic-gate 	cm_entry->x_zoneid = zoneid;
21037c478bd9Sstevel@tonic-gate 	mutex_init(&cm_entry->x_lock, NULL, MUTEX_DEFAULT, NULL);
21047c478bd9Sstevel@tonic-gate 	cv_init(&cm_entry->x_cv, NULL, CV_DEFAULT, NULL);
21057c478bd9Sstevel@tonic-gate 	cv_init(&cm_entry->x_conn_cv, NULL, CV_DEFAULT, NULL);
21067c478bd9Sstevel@tonic-gate 	cv_init(&cm_entry->x_dis_cv, NULL, CV_DEFAULT, NULL);
21077c478bd9Sstevel@tonic-gate 
21087c478bd9Sstevel@tonic-gate 	/*
21097c478bd9Sstevel@tonic-gate 	 * Note that we add this partially initialized entry to the
21107c478bd9Sstevel@tonic-gate 	 * connection list. This is so that we don't have connections to
21117c478bd9Sstevel@tonic-gate 	 * the same server.
21127c478bd9Sstevel@tonic-gate 	 *
21137c478bd9Sstevel@tonic-gate 	 * Note that x_src is not initialized at this point. This is because
21147c478bd9Sstevel@tonic-gate 	 * retryaddr might be NULL in which case x_src is whatever
21157c478bd9Sstevel@tonic-gate 	 * t_kbind/bindresvport gives us. If another thread wants a
21167c478bd9Sstevel@tonic-gate 	 * connection to the same server, seemingly we have an issue, but we
21177c478bd9Sstevel@tonic-gate 	 * don't. If the other thread comes in with retryaddr == NULL, then it
21187c478bd9Sstevel@tonic-gate 	 * will never look at x_src, and it will end up waiting in
21197c478bd9Sstevel@tonic-gate 	 * connmgr_cwait() for the first thread to finish the connection
21207c478bd9Sstevel@tonic-gate 	 * attempt. If the other thread comes in with retryaddr != NULL, then
21217c478bd9Sstevel@tonic-gate 	 * that means there was a request sent on a connection, in which case
21227c478bd9Sstevel@tonic-gate 	 * the the connection should already exist. Thus the first thread
21237c478bd9Sstevel@tonic-gate 	 * never gets here ... it finds the connection it its server in the
21247c478bd9Sstevel@tonic-gate 	 * connection list.
21257c478bd9Sstevel@tonic-gate 	 *
21267c478bd9Sstevel@tonic-gate 	 * But even if theory is wrong, in the retryaddr != NULL case, the 2nd
21277c478bd9Sstevel@tonic-gate 	 * thread will skip us because x_src.len == 0.
21287c478bd9Sstevel@tonic-gate 	 */
21297c478bd9Sstevel@tonic-gate 	cm_entry->x_next = cm_hd;
21307c478bd9Sstevel@tonic-gate 	cm_hd = cm_entry;
21317c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
21327c478bd9Sstevel@tonic-gate 
21337c478bd9Sstevel@tonic-gate 	/*
21347c478bd9Sstevel@tonic-gate 	 * Either we didn't find an entry to the server of interest, or we
21357c478bd9Sstevel@tonic-gate 	 * don't have the maximum number of connections to that server -
21367c478bd9Sstevel@tonic-gate 	 * create a new connection.
21377c478bd9Sstevel@tonic-gate 	 */
21387c478bd9Sstevel@tonic-gate 	RPCLOG0(8, "connmgr_get: creating new connection\n");
21397c478bd9Sstevel@tonic-gate 	rpcerr->re_status = RPC_TLIERROR;
21407c478bd9Sstevel@tonic-gate 
214145916cd2Sjpk 	i = t_kopen(NULL, device, FREAD|FWRITE|FNDELAY, &tiptr, zone_kcred());
21427c478bd9Sstevel@tonic-gate 	if (i) {
21437c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't open cots device, error %d\n", i);
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 	rpc_poptimod(tiptr->fp->f_vnode);
21497c478bd9Sstevel@tonic-gate 
21507c478bd9Sstevel@tonic-gate 	if (i = strioctl(tiptr->fp->f_vnode, I_PUSH, (intptr_t)"rpcmod", 0,
21517c478bd9Sstevel@tonic-gate 	    K_TO_K, kcred, &retval)) {
21527c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't push cots 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 	if (i = strioctl(tiptr->fp->f_vnode, RPC_CLIENT, 0, 0, K_TO_K,
21607c478bd9Sstevel@tonic-gate 	    kcred, &retval)) {
21617c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't set client status with cots "
21627c478bd9Sstevel@tonic-gate 		    "module, %d\n", i);
21637c478bd9Sstevel@tonic-gate 		(void) t_kclose(tiptr, 1);
21647c478bd9Sstevel@tonic-gate 		rpcerr->re_errno = i;
21657c478bd9Sstevel@tonic-gate 		connmgr_cancelconn(cm_entry);
21667c478bd9Sstevel@tonic-gate 		return (NULL);
21677c478bd9Sstevel@tonic-gate 	}
21687c478bd9Sstevel@tonic-gate 
21697c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
21707c478bd9Sstevel@tonic-gate 
21717c478bd9Sstevel@tonic-gate 	wq = tiptr->fp->f_vnode->v_stream->sd_wrq->q_next;
21727c478bd9Sstevel@tonic-gate 	cm_entry->x_wq = wq;
21737c478bd9Sstevel@tonic-gate 
21747c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
21757c478bd9Sstevel@tonic-gate 
21767c478bd9Sstevel@tonic-gate 	if (i = strioctl(tiptr->fp->f_vnode, I_PUSH, (intptr_t)"timod", 0,
21777c478bd9Sstevel@tonic-gate 	    K_TO_K, kcred, &retval)) {
21787c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_get: can't push timod, %d\n", i);
21797c478bd9Sstevel@tonic-gate 		(void) t_kclose(tiptr, 1);
21807c478bd9Sstevel@tonic-gate 		rpcerr->re_errno = i;
21817c478bd9Sstevel@tonic-gate 		connmgr_cancelconn(cm_entry);
21827c478bd9Sstevel@tonic-gate 		return (NULL);
21837c478bd9Sstevel@tonic-gate 	}
21847c478bd9Sstevel@tonic-gate 
21857c478bd9Sstevel@tonic-gate 	/*
21867c478bd9Sstevel@tonic-gate 	 * If the caller has not specified reserved port usage then
21877c478bd9Sstevel@tonic-gate 	 * take the system default.
21887c478bd9Sstevel@tonic-gate 	 */
21897c478bd9Sstevel@tonic-gate 	if (useresvport == -1)
21907c478bd9Sstevel@tonic-gate 		useresvport = clnt_cots_do_bindresvport;
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate 	if ((useresvport || retryaddr != NULL) &&
21937c478bd9Sstevel@tonic-gate 	    (addrfmly == AF_INET || addrfmly == AF_INET6)) {
21947c478bd9Sstevel@tonic-gate 		bool_t alloc_src = FALSE;
21957c478bd9Sstevel@tonic-gate 
21967c478bd9Sstevel@tonic-gate 		if (srcaddr->len != destaddr->len) {
21977c478bd9Sstevel@tonic-gate 			kmem_free(srcaddr->buf, srcaddr->maxlen);
21987c478bd9Sstevel@tonic-gate 			srcaddr->buf = kmem_zalloc(destaddr->len, KM_SLEEP);
21997c478bd9Sstevel@tonic-gate 			srcaddr->maxlen = destaddr->len;
22007c478bd9Sstevel@tonic-gate 			srcaddr->len = destaddr->len;
22017c478bd9Sstevel@tonic-gate 			alloc_src = TRUE;
22027c478bd9Sstevel@tonic-gate 		}
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 		if ((i = bindresvport(tiptr, retryaddr, srcaddr, TRUE)) != 0) {
22057c478bd9Sstevel@tonic-gate 			(void) t_kclose(tiptr, 1);
22067c478bd9Sstevel@tonic-gate 			RPCLOG(1, "connmgr_get: couldn't bind, retryaddr: "
22077c478bd9Sstevel@tonic-gate 			    "%p\n", (void *)retryaddr);
22087c478bd9Sstevel@tonic-gate 
22097c478bd9Sstevel@tonic-gate 			/*
22107c478bd9Sstevel@tonic-gate 			 * 1225408: If we allocated a source address, then it
22117c478bd9Sstevel@tonic-gate 			 * is either garbage or all zeroes. In that case
22127c478bd9Sstevel@tonic-gate 			 * we need to clear srcaddr.
22137c478bd9Sstevel@tonic-gate 			 */
22147c478bd9Sstevel@tonic-gate 			if (alloc_src == TRUE) {
22157c478bd9Sstevel@tonic-gate 				kmem_free(srcaddr->buf, srcaddr->maxlen);
22167c478bd9Sstevel@tonic-gate 				srcaddr->maxlen = srcaddr->len = 0;
22177c478bd9Sstevel@tonic-gate 				srcaddr->buf = NULL;
22187c478bd9Sstevel@tonic-gate 			}
22197c478bd9Sstevel@tonic-gate 			rpcerr->re_errno = i;
22207c478bd9Sstevel@tonic-gate 			connmgr_cancelconn(cm_entry);
22217c478bd9Sstevel@tonic-gate 			return (NULL);
22227c478bd9Sstevel@tonic-gate 		}
22237c478bd9Sstevel@tonic-gate 	} else {
22247c478bd9Sstevel@tonic-gate 		if ((i = t_kbind(tiptr, NULL, NULL)) != 0) {
22257c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_cots_kcreate: t_kbind: %d\n", i);
22267c478bd9Sstevel@tonic-gate 			(void) t_kclose(tiptr, 1);
22277c478bd9Sstevel@tonic-gate 			rpcerr->re_errno = i;
22287c478bd9Sstevel@tonic-gate 			connmgr_cancelconn(cm_entry);
22297c478bd9Sstevel@tonic-gate 			return (NULL);
22307c478bd9Sstevel@tonic-gate 		}
22317c478bd9Sstevel@tonic-gate 	}
22327c478bd9Sstevel@tonic-gate 
22337c478bd9Sstevel@tonic-gate 	{
22347c478bd9Sstevel@tonic-gate 		/*
22357c478bd9Sstevel@tonic-gate 		 * Keep the kernel stack lean. Don't move this call
22367c478bd9Sstevel@tonic-gate 		 * declaration to the top of this function because a
22377c478bd9Sstevel@tonic-gate 		 * call is declared in connmgr_wrapconnect()
22387c478bd9Sstevel@tonic-gate 		 */
22397c478bd9Sstevel@tonic-gate 		calllist_t call;
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 		bzero(&call, sizeof (call));
22427c478bd9Sstevel@tonic-gate 		cv_init(&call.call_cv, NULL, CV_DEFAULT, NULL);
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 		/*
22457c478bd9Sstevel@tonic-gate 		 * This is a bound end-point so don't close it's stream.
22467c478bd9Sstevel@tonic-gate 		 */
22477c478bd9Sstevel@tonic-gate 		connected = connmgr_connect(cm_entry, wq, destaddr, addrfmly,
2248de8c4a14SErik Nordmark 		    &call, &tidu_size, FALSE, waitp, nosignal, cr);
22497c478bd9Sstevel@tonic-gate 		*rpcerr = call.call_err;
22507c478bd9Sstevel@tonic-gate 		cv_destroy(&call.call_cv);
22517c478bd9Sstevel@tonic-gate 
22527c478bd9Sstevel@tonic-gate 	}
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
22557c478bd9Sstevel@tonic-gate 
22567c478bd9Sstevel@tonic-gate 	/*
22577c478bd9Sstevel@tonic-gate 	 * Set up a transport entry in the connection manager's list.
22587c478bd9Sstevel@tonic-gate 	 */
22594b6bffb4SDan McDonald 	if (srcaddr->len > 0) {
22604b6bffb4SDan McDonald 		cm_entry->x_src.buf = kmem_alloc(srcaddr->len, KM_SLEEP);
22617c478bd9Sstevel@tonic-gate 		bcopy(srcaddr->buf, cm_entry->x_src.buf, srcaddr->len);
22627c478bd9Sstevel@tonic-gate 		cm_entry->x_src.len = cm_entry->x_src.maxlen = srcaddr->len;
22634b6bffb4SDan McDonald 	} /* Else kmem_zalloc() of cm_entry already sets its x_src to NULL. */
22647c478bd9Sstevel@tonic-gate 
22657c478bd9Sstevel@tonic-gate 	cm_entry->x_tiptr = tiptr;
2266d3d50737SRafael Vanoni 	cm_entry->x_time = ddi_get_lbolt();
22677c478bd9Sstevel@tonic-gate 
22687c478bd9Sstevel@tonic-gate 	if (tiptr->tp_info.servtype == T_COTS_ORD)
22697c478bd9Sstevel@tonic-gate 		cm_entry->x_ordrel = TRUE;
22707c478bd9Sstevel@tonic-gate 	else
22717c478bd9Sstevel@tonic-gate 		cm_entry->x_ordrel = FALSE;
22727c478bd9Sstevel@tonic-gate 
22737c478bd9Sstevel@tonic-gate 	cm_entry->x_tidu_size = tidu_size;
22747c478bd9Sstevel@tonic-gate 
2275af41c4bfSvv149972 	if (cm_entry->x_early_disc) {
2276af41c4bfSvv149972 		/*
2277af41c4bfSvv149972 		 * We need to check if a disconnect request has come
2278af41c4bfSvv149972 		 * while we are connected, if so, then we need to
2279af41c4bfSvv149972 		 * set rpcerr->re_status appropriately before returning
2280af41c4bfSvv149972 		 * NULL to caller.
2281af41c4bfSvv149972 		 */
2282af41c4bfSvv149972 		if (rpcerr->re_status == RPC_SUCCESS)
2283af41c4bfSvv149972 			rpcerr->re_status = RPC_XPRTFAILED;
22847c478bd9Sstevel@tonic-gate 		cm_entry->x_connected = FALSE;
2285af41c4bfSvv149972 	} else
22867c478bd9Sstevel@tonic-gate 		cm_entry->x_connected = connected;
22877c478bd9Sstevel@tonic-gate 
22887c478bd9Sstevel@tonic-gate 	/*
22897c478bd9Sstevel@tonic-gate 	 * There could be a discrepancy here such that
22907c478bd9Sstevel@tonic-gate 	 * x_early_disc is TRUE yet connected is TRUE as well
22917c478bd9Sstevel@tonic-gate 	 * and the connection is actually connected. In that case
22927c478bd9Sstevel@tonic-gate 	 * lets be conservative and declare the connection as not
22937c478bd9Sstevel@tonic-gate 	 * connected.
22947c478bd9Sstevel@tonic-gate 	 */
22957c478bd9Sstevel@tonic-gate 	cm_entry->x_early_disc = FALSE;
22967c478bd9Sstevel@tonic-gate 	cm_entry->x_needdis = (cm_entry->x_connected == FALSE);
2297d3d50737SRafael Vanoni 	cm_entry->x_ctime = ddi_get_lbolt();
22987c478bd9Sstevel@tonic-gate 
22997c478bd9Sstevel@tonic-gate 	/*
23007c478bd9Sstevel@tonic-gate 	 * Notify any threads waiting that the connection attempt is done.
23017c478bd9Sstevel@tonic-gate 	 */
23027c478bd9Sstevel@tonic-gate 	cm_entry->x_thread = FALSE;
23037c478bd9Sstevel@tonic-gate 	cv_broadcast(&cm_entry->x_conn_cv);
23047c478bd9Sstevel@tonic-gate 
23057c478bd9Sstevel@tonic-gate 	if (cm_entry->x_connected == FALSE) {
2306af41c4bfSvv149972 		mutex_exit(&connmgr_lock);
23077c478bd9Sstevel@tonic-gate 		connmgr_release(cm_entry);
23087c478bd9Sstevel@tonic-gate 		return (NULL);
23097c478bd9Sstevel@tonic-gate 	}
2310af41c4bfSvv149972 
2311af41c4bfSvv149972 	mutex_exit(&connmgr_lock);
2312af41c4bfSvv149972 
23137c478bd9Sstevel@tonic-gate 	return (cm_entry);
23147c478bd9Sstevel@tonic-gate }
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate /*
23177c478bd9Sstevel@tonic-gate  * Keep the cm_xprt entry on the connecton list when making a connection. This
23187c478bd9Sstevel@tonic-gate  * is to prevent multiple connections to a slow server from appearing.
23197c478bd9Sstevel@tonic-gate  * We use the bit field x_thread to tell if a thread is doing a connection
23207c478bd9Sstevel@tonic-gate  * which keeps other interested threads from messing with connection.
23217c478bd9Sstevel@tonic-gate  * Those other threads just wait if x_thread is set.
23227c478bd9Sstevel@tonic-gate  *
23237c478bd9Sstevel@tonic-gate  * If x_thread is not set, then we do the actual work of connecting via
23247c478bd9Sstevel@tonic-gate  * connmgr_connect().
23257c478bd9Sstevel@tonic-gate  *
23267c478bd9Sstevel@tonic-gate  * mutex convention: called with connmgr_lock held, returns with it released.
23277c478bd9Sstevel@tonic-gate  */
23287c478bd9Sstevel@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)23297c478bd9Sstevel@tonic-gate connmgr_wrapconnect(
23307c478bd9Sstevel@tonic-gate 	struct cm_xprt	*cm_entry,
23317c478bd9Sstevel@tonic-gate 	const struct timeval	*waitp,
23327c478bd9Sstevel@tonic-gate 	struct netbuf	*destaddr,
23337c478bd9Sstevel@tonic-gate 	int		addrfmly,
23347c478bd9Sstevel@tonic-gate 	struct netbuf	*srcaddr,
23357c478bd9Sstevel@tonic-gate 	struct rpc_err	*rpcerr,
23367c478bd9Sstevel@tonic-gate 	bool_t		reconnect,
2337de8c4a14SErik Nordmark 	bool_t		nosignal,
2338de8c4a14SErik Nordmark 	cred_t		*cr)
23397c478bd9Sstevel@tonic-gate {
23407c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
23417c478bd9Sstevel@tonic-gate 	/*
23427c478bd9Sstevel@tonic-gate 	 * Hold this entry as we are about to drop connmgr_lock.
23437c478bd9Sstevel@tonic-gate 	 */
23447c478bd9Sstevel@tonic-gate 	CONN_HOLD(cm_entry);
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 	/*
23477c478bd9Sstevel@tonic-gate 	 * If there is a thread already making a connection for us, then
23487c478bd9Sstevel@tonic-gate 	 * wait for it to complete the connection.
23497c478bd9Sstevel@tonic-gate 	 */
23507c478bd9Sstevel@tonic-gate 	if (cm_entry->x_thread == TRUE) {
23517c478bd9Sstevel@tonic-gate 		rpcerr->re_status = connmgr_cwait(cm_entry, waitp, nosignal);
23527c478bd9Sstevel@tonic-gate 
23537c478bd9Sstevel@tonic-gate 		if (rpcerr->re_status != RPC_SUCCESS) {
23547c478bd9Sstevel@tonic-gate 			mutex_exit(&connmgr_lock);
23557c478bd9Sstevel@tonic-gate 			connmgr_release(cm_entry);
23567c478bd9Sstevel@tonic-gate 			return (NULL);
23577c478bd9Sstevel@tonic-gate 		}
23587c478bd9Sstevel@tonic-gate 	} else {
23597c478bd9Sstevel@tonic-gate 		bool_t connected;
23607c478bd9Sstevel@tonic-gate 		calllist_t call;
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 		cm_entry->x_thread = TRUE;
23637c478bd9Sstevel@tonic-gate 
23647c478bd9Sstevel@tonic-gate 		while (cm_entry->x_needrel == TRUE) {
23657c478bd9Sstevel@tonic-gate 			cm_entry->x_needrel = FALSE;
23667c478bd9Sstevel@tonic-gate 
23677c478bd9Sstevel@tonic-gate 			connmgr_sndrel(cm_entry);
23687c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(1000000));
23697c478bd9Sstevel@tonic-gate 
23707c478bd9Sstevel@tonic-gate 			mutex_enter(&connmgr_lock);
23717c478bd9Sstevel@tonic-gate 		}
23727c478bd9Sstevel@tonic-gate 
23737c478bd9Sstevel@tonic-gate 		/*
23747c478bd9Sstevel@tonic-gate 		 * If we need to send a T_DISCON_REQ, send one.
23757c478bd9Sstevel@tonic-gate 		 */
23767c478bd9Sstevel@tonic-gate 		connmgr_dis_and_wait(cm_entry);
23777c478bd9Sstevel@tonic-gate 
23787c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
23797c478bd9Sstevel@tonic-gate 
23807c478bd9Sstevel@tonic-gate 		bzero(&call, sizeof (call));
23817c478bd9Sstevel@tonic-gate 		cv_init(&call.call_cv, NULL, CV_DEFAULT, NULL);
23827c478bd9Sstevel@tonic-gate 
23837c478bd9Sstevel@tonic-gate 		connected = connmgr_connect(cm_entry, cm_entry->x_wq,
23848ffff9fdSgt29601 		    destaddr, addrfmly, &call, &cm_entry->x_tidu_size,
2385de8c4a14SErik Nordmark 		    reconnect, waitp, nosignal, cr);
23867c478bd9Sstevel@tonic-gate 
23877c478bd9Sstevel@tonic-gate 		*rpcerr = call.call_err;
23887c478bd9Sstevel@tonic-gate 		cv_destroy(&call.call_cv);
23897c478bd9Sstevel@tonic-gate 
23907c478bd9Sstevel@tonic-gate 		mutex_enter(&connmgr_lock);
23917c478bd9Sstevel@tonic-gate 
23927c478bd9Sstevel@tonic-gate 
2393af41c4bfSvv149972 		if (cm_entry->x_early_disc) {
2394af41c4bfSvv149972 			/*
2395af41c4bfSvv149972 			 * We need to check if a disconnect request has come
2396af41c4bfSvv149972 			 * while we are connected, if so, then we need to
2397af41c4bfSvv149972 			 * set rpcerr->re_status appropriately before returning
2398af41c4bfSvv149972 			 * NULL to caller.
2399af41c4bfSvv149972 			 */
2400af41c4bfSvv149972 			if (rpcerr->re_status == RPC_SUCCESS)
2401af41c4bfSvv149972 				rpcerr->re_status = RPC_XPRTFAILED;
24027c478bd9Sstevel@tonic-gate 			cm_entry->x_connected = FALSE;
2403af41c4bfSvv149972 		} else
24047c478bd9Sstevel@tonic-gate 			cm_entry->x_connected = connected;
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate 		/*
24077c478bd9Sstevel@tonic-gate 		 * There could be a discrepancy here such that
24087c478bd9Sstevel@tonic-gate 		 * x_early_disc is TRUE yet connected is TRUE as well
24097c478bd9Sstevel@tonic-gate 		 * and the connection is actually connected. In that case
24107c478bd9Sstevel@tonic-gate 		 * lets be conservative and declare the connection as not
24117c478bd9Sstevel@tonic-gate 		 * connected.
24127c478bd9Sstevel@tonic-gate 		 */
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 		cm_entry->x_early_disc = FALSE;
24157c478bd9Sstevel@tonic-gate 		cm_entry->x_needdis = (cm_entry->x_connected == FALSE);
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate 		/*
24197c478bd9Sstevel@tonic-gate 		 * connmgr_connect() may have given up before the connection
24207c478bd9Sstevel@tonic-gate 		 * actually timed out. So ensure that before the next
24217c478bd9Sstevel@tonic-gate 		 * connection attempt we do a disconnect.
24227c478bd9Sstevel@tonic-gate 		 */
2423d3d50737SRafael Vanoni 		cm_entry->x_ctime = ddi_get_lbolt();
24247c478bd9Sstevel@tonic-gate 		cm_entry->x_thread = FALSE;
24257c478bd9Sstevel@tonic-gate 
24267c478bd9Sstevel@tonic-gate 		cv_broadcast(&cm_entry->x_conn_cv);
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 		if (cm_entry->x_connected == FALSE) {
24297c478bd9Sstevel@tonic-gate 			mutex_exit(&connmgr_lock);
24307c478bd9Sstevel@tonic-gate 			connmgr_release(cm_entry);
24317c478bd9Sstevel@tonic-gate 			return (NULL);
24327c478bd9Sstevel@tonic-gate 		}
24337c478bd9Sstevel@tonic-gate 	}
24347c478bd9Sstevel@tonic-gate 
24357c478bd9Sstevel@tonic-gate 	if (srcaddr != NULL) {
24367c478bd9Sstevel@tonic-gate 		/*
24377c478bd9Sstevel@tonic-gate 		 * Copy into the handle the
24387c478bd9Sstevel@tonic-gate 		 * source address of the
24397c478bd9Sstevel@tonic-gate 		 * connection, which we will use
24407c478bd9Sstevel@tonic-gate 		 * in case of a later retry.
24417c478bd9Sstevel@tonic-gate 		 */
24427c478bd9Sstevel@tonic-gate 		if (srcaddr->len != cm_entry->x_src.len) {
24437c478bd9Sstevel@tonic-gate 			kmem_free(srcaddr->buf, srcaddr->maxlen);
24444b6bffb4SDan McDonald 			ASSERT(cm_entry->x_src.len != 0);
24454b6bffb4SDan McDonald 			srcaddr->buf = kmem_alloc(cm_entry->x_src.len,
24467c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
24474b6bffb4SDan McDonald 			srcaddr->maxlen = srcaddr->len = cm_entry->x_src.len;
24487c478bd9Sstevel@tonic-gate 		}
24497c478bd9Sstevel@tonic-gate 		bcopy(cm_entry->x_src.buf, srcaddr->buf, srcaddr->len);
24507c478bd9Sstevel@tonic-gate 	}
2451d3d50737SRafael Vanoni 	cm_entry->x_time = ddi_get_lbolt();
24527c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
24537c478bd9Sstevel@tonic-gate 	return (cm_entry);
24547c478bd9Sstevel@tonic-gate }
24557c478bd9Sstevel@tonic-gate 
24567c478bd9Sstevel@tonic-gate /*
24577c478bd9Sstevel@tonic-gate  * If we need to send a T_DISCON_REQ, send one.
24587c478bd9Sstevel@tonic-gate  */
24597c478bd9Sstevel@tonic-gate static void
connmgr_dis_and_wait(struct cm_xprt * cm_entry)24607c478bd9Sstevel@tonic-gate connmgr_dis_and_wait(struct cm_xprt *cm_entry)
24617c478bd9Sstevel@tonic-gate {
24627c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
24637c478bd9Sstevel@tonic-gate 	for (;;) {
24647c478bd9Sstevel@tonic-gate 		while (cm_entry->x_needdis == TRUE) {
24657c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_dis_and_wait: need "
24667c478bd9Sstevel@tonic-gate 			    "T_DISCON_REQ for connection 0x%p\n",
24677c478bd9Sstevel@tonic-gate 			    (void *)cm_entry);
24687c478bd9Sstevel@tonic-gate 			cm_entry->x_needdis = FALSE;
24697c478bd9Sstevel@tonic-gate 			cm_entry->x_waitdis = TRUE;
24707c478bd9Sstevel@tonic-gate 
24717c478bd9Sstevel@tonic-gate 			connmgr_snddis(cm_entry);
24727c478bd9Sstevel@tonic-gate 
24737c478bd9Sstevel@tonic-gate 			mutex_enter(&connmgr_lock);
24747c478bd9Sstevel@tonic-gate 		}
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate 		if (cm_entry->x_waitdis == TRUE) {
24777c478bd9Sstevel@tonic-gate 			clock_t timout;
24787c478bd9Sstevel@tonic-gate 
24797c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_dis_and_wait waiting for "
24807c478bd9Sstevel@tonic-gate 			    "T_DISCON_REQ's ACK for connection %p\n",
24817c478bd9Sstevel@tonic-gate 			    (void *)cm_entry);
24827c478bd9Sstevel@tonic-gate 
2483d3d50737SRafael Vanoni 			timout = clnt_cots_min_conntout * drv_usectohz(1000000);
24847c478bd9Sstevel@tonic-gate 
24857c478bd9Sstevel@tonic-gate 			/*
24867c478bd9Sstevel@tonic-gate 			 * The TPI spec says that the T_DISCON_REQ
24877c478bd9Sstevel@tonic-gate 			 * will get acknowledged, but in practice
24887c478bd9Sstevel@tonic-gate 			 * the ACK may never get sent. So don't
24897c478bd9Sstevel@tonic-gate 			 * block forever.
24907c478bd9Sstevel@tonic-gate 			 */
2491d3d50737SRafael Vanoni 			(void) cv_reltimedwait(&cm_entry->x_dis_cv,
2492d3d50737SRafael Vanoni 			    &connmgr_lock, timout, TR_CLOCK_TICK);
24937c478bd9Sstevel@tonic-gate 		}
24947c478bd9Sstevel@tonic-gate 		/*
24957c478bd9Sstevel@tonic-gate 		 * If we got the ACK, break. If we didn't,
24967c478bd9Sstevel@tonic-gate 		 * then send another T_DISCON_REQ.
24977c478bd9Sstevel@tonic-gate 		 */
24987c478bd9Sstevel@tonic-gate 		if (cm_entry->x_waitdis == FALSE) {
24997c478bd9Sstevel@tonic-gate 			break;
25007c478bd9Sstevel@tonic-gate 		} else {
25017c478bd9Sstevel@tonic-gate 			RPCLOG(8, "connmgr_dis_and_wait: did"
25027c478bd9Sstevel@tonic-gate 			    "not get T_DISCON_REQ's ACK for "
25037c478bd9Sstevel@tonic-gate 			    "connection  %p\n", (void *)cm_entry);
25047c478bd9Sstevel@tonic-gate 			cm_entry->x_needdis = TRUE;
25057c478bd9Sstevel@tonic-gate 		}
25067c478bd9Sstevel@tonic-gate 	}
25077c478bd9Sstevel@tonic-gate }
25087c478bd9Sstevel@tonic-gate 
25097c478bd9Sstevel@tonic-gate static void
connmgr_cancelconn(struct cm_xprt * cm_entry)25107c478bd9Sstevel@tonic-gate connmgr_cancelconn(struct cm_xprt *cm_entry)
25117c478bd9Sstevel@tonic-gate {
25127c478bd9Sstevel@tonic-gate 	/*
25137c478bd9Sstevel@tonic-gate 	 * Mark the connection table entry as dead; the next thread that
25147c478bd9Sstevel@tonic-gate 	 * goes through connmgr_release() will notice this and deal with it.
25157c478bd9Sstevel@tonic-gate 	 */
25167c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
25177c478bd9Sstevel@tonic-gate 	cm_entry->x_dead = TRUE;
25187c478bd9Sstevel@tonic-gate 
25197c478bd9Sstevel@tonic-gate 	/*
25207c478bd9Sstevel@tonic-gate 	 * Notify any threads waiting for the connection that it isn't
25217c478bd9Sstevel@tonic-gate 	 * going to happen.
25227c478bd9Sstevel@tonic-gate 	 */
25237c478bd9Sstevel@tonic-gate 	cm_entry->x_thread = FALSE;
25247c478bd9Sstevel@tonic-gate 	cv_broadcast(&cm_entry->x_conn_cv);
25257c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 	connmgr_release(cm_entry);
25287c478bd9Sstevel@tonic-gate }
25297c478bd9Sstevel@tonic-gate 
25307c478bd9Sstevel@tonic-gate static void
connmgr_close(struct cm_xprt * cm_entry)25317c478bd9Sstevel@tonic-gate connmgr_close(struct cm_xprt *cm_entry)
25327c478bd9Sstevel@tonic-gate {
25337c478bd9Sstevel@tonic-gate 	mutex_enter(&cm_entry->x_lock);
25347c478bd9Sstevel@tonic-gate 	while (cm_entry->x_ref != 0) {
25357c478bd9Sstevel@tonic-gate 		/*
25367c478bd9Sstevel@tonic-gate 		 * Must be a noninterruptible wait.
25377c478bd9Sstevel@tonic-gate 		 */
25387c478bd9Sstevel@tonic-gate 		cv_wait(&cm_entry->x_cv, &cm_entry->x_lock);
25397c478bd9Sstevel@tonic-gate 	}
25407c478bd9Sstevel@tonic-gate 
25417c478bd9Sstevel@tonic-gate 	if (cm_entry->x_tiptr != NULL)
25427c478bd9Sstevel@tonic-gate 		(void) t_kclose(cm_entry->x_tiptr, 1);
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate 	mutex_exit(&cm_entry->x_lock);
25457c478bd9Sstevel@tonic-gate 	if (cm_entry->x_ksp != NULL) {
2546*6f914afdSToomas Soome 		mutex_enter(cm_entry->x_ksp->ks_lock);
25477c478bd9Sstevel@tonic-gate 		cm_entry->x_ksp->ks_private = NULL;
2548*6f914afdSToomas Soome 		mutex_exit(cm_entry->x_ksp->ks_lock);
25497c478bd9Sstevel@tonic-gate 
25507c478bd9Sstevel@tonic-gate 		/*
25517c478bd9Sstevel@tonic-gate 		 * Must free the buffer we allocated for the
25527c478bd9Sstevel@tonic-gate 		 * server address in the update function
25537c478bd9Sstevel@tonic-gate 		 */
25547c478bd9Sstevel@tonic-gate 		if (((struct cm_kstat_xprt *)(cm_entry->x_ksp->ks_data))->
2555a1b5e537Sbmc 		    x_server.value.str.addr.ptr != NULL)
25567c478bd9Sstevel@tonic-gate 			kmem_free(((struct cm_kstat_xprt *)(cm_entry->x_ksp->
2557a1b5e537Sbmc 			    ks_data))->x_server.value.str.addr.ptr,
25587c478bd9Sstevel@tonic-gate 			    INET6_ADDRSTRLEN);
25597c478bd9Sstevel@tonic-gate 		kmem_free(cm_entry->x_ksp->ks_data,
25607c478bd9Sstevel@tonic-gate 		    cm_entry->x_ksp->ks_data_size);
25617c478bd9Sstevel@tonic-gate 		kstat_delete(cm_entry->x_ksp);
25627c478bd9Sstevel@tonic-gate 	}
25637c478bd9Sstevel@tonic-gate 
25647c478bd9Sstevel@tonic-gate 	mutex_destroy(&cm_entry->x_lock);
25657c478bd9Sstevel@tonic-gate 	cv_destroy(&cm_entry->x_cv);
25667c478bd9Sstevel@tonic-gate 	cv_destroy(&cm_entry->x_conn_cv);
25677c478bd9Sstevel@tonic-gate 	cv_destroy(&cm_entry->x_dis_cv);
25687c478bd9Sstevel@tonic-gate 
25697c478bd9Sstevel@tonic-gate 	kmem_free(cm_entry->x_server.buf, cm_entry->x_server.maxlen);
25707c478bd9Sstevel@tonic-gate 	kmem_free(cm_entry->x_src.buf, cm_entry->x_src.maxlen);
25717c478bd9Sstevel@tonic-gate 	kmem_free(cm_entry, sizeof (struct cm_xprt));
25727c478bd9Sstevel@tonic-gate }
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate /*
25757c478bd9Sstevel@tonic-gate  * Called by KRPC after sending the call message to release the connection
25767c478bd9Sstevel@tonic-gate  * it was using.
25777c478bd9Sstevel@tonic-gate  */
25787c478bd9Sstevel@tonic-gate static void
connmgr_release(struct cm_xprt * cm_entry)25797c478bd9Sstevel@tonic-gate connmgr_release(struct cm_xprt *cm_entry)
25807c478bd9Sstevel@tonic-gate {
25817c478bd9Sstevel@tonic-gate 	mutex_enter(&cm_entry->x_lock);
25827c478bd9Sstevel@tonic-gate 	cm_entry->x_ref--;
25837c478bd9Sstevel@tonic-gate 	if (cm_entry->x_ref == 0)
25847c478bd9Sstevel@tonic-gate 		cv_signal(&cm_entry->x_cv);
25857c478bd9Sstevel@tonic-gate 	mutex_exit(&cm_entry->x_lock);
25867c478bd9Sstevel@tonic-gate }
25877c478bd9Sstevel@tonic-gate 
25887c478bd9Sstevel@tonic-gate /*
25892081ac19SDai Ngo  * Set TCP receive and xmit buffer size for RPC connections.
25902081ac19SDai Ngo  */
25912081ac19SDai Ngo static bool_t
connmgr_setbufsz(calllist_t * e,queue_t * wq,cred_t * cr)25922081ac19SDai Ngo connmgr_setbufsz(calllist_t *e, queue_t *wq, cred_t *cr)
25932081ac19SDai Ngo {
25942081ac19SDai Ngo 	int ok = FALSE;
25952081ac19SDai Ngo 	int val;
25962081ac19SDai Ngo 
25972081ac19SDai Ngo 	if (rpc_default_tcp_bufsz)
25982081ac19SDai Ngo 		return (FALSE);
25992081ac19SDai Ngo 
26002081ac19SDai Ngo 	/*
26012081ac19SDai Ngo 	 * Only set new buffer size if it's larger than the system
26022081ac19SDai Ngo 	 * default buffer size. If smaller buffer size is needed
26032081ac19SDai Ngo 	 * then use /etc/system to set rpc_default_tcp_bufsz to 1.
26042081ac19SDai Ngo 	 */
26052081ac19SDai Ngo 	ok = connmgr_getopt_int(wq, SOL_SOCKET, SO_RCVBUF, &val, e, cr);
26062081ac19SDai Ngo 	if ((ok == TRUE) && (val < rpc_send_bufsz)) {
26072081ac19SDai Ngo 		ok = connmgr_setopt_int(wq, SOL_SOCKET, SO_RCVBUF,
26082081ac19SDai Ngo 		    rpc_send_bufsz, e, cr);
26092081ac19SDai Ngo 		DTRACE_PROBE2(krpc__i__connmgr_rcvbufsz,
26102081ac19SDai Ngo 		    int, ok, calllist_t *, e);
26112081ac19SDai Ngo 	}
26122081ac19SDai Ngo 
26132081ac19SDai Ngo 	ok = connmgr_getopt_int(wq, SOL_SOCKET, SO_SNDBUF, &val, e, cr);
26142081ac19SDai Ngo 	if ((ok == TRUE) && (val < rpc_recv_bufsz)) {
26152081ac19SDai Ngo 		ok = connmgr_setopt_int(wq, SOL_SOCKET, SO_SNDBUF,
26162081ac19SDai Ngo 		    rpc_recv_bufsz, e, cr);
26172081ac19SDai Ngo 		DTRACE_PROBE2(krpc__i__connmgr_sndbufsz,
26182081ac19SDai Ngo 		    int, ok, calllist_t *, e);
26192081ac19SDai Ngo 	}
26202081ac19SDai Ngo 	return (TRUE);
26212081ac19SDai Ngo }
26222081ac19SDai Ngo 
26232081ac19SDai Ngo /*
26247c478bd9Sstevel@tonic-gate  * Given an open stream, connect to the remote.  Returns true if connected,
26257c478bd9Sstevel@tonic-gate  * false otherwise.
26267c478bd9Sstevel@tonic-gate  */
26277c478bd9Sstevel@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)26287c478bd9Sstevel@tonic-gate connmgr_connect(
26297c478bd9Sstevel@tonic-gate 	struct cm_xprt		*cm_entry,
26307c478bd9Sstevel@tonic-gate 	queue_t			*wq,
26317c478bd9Sstevel@tonic-gate 	struct netbuf		*addr,
26327c478bd9Sstevel@tonic-gate 	int			addrfmly,
26337c478bd9Sstevel@tonic-gate 	calllist_t		*e,
26347c478bd9Sstevel@tonic-gate 	int			*tidu_ptr,
26357c478bd9Sstevel@tonic-gate 	bool_t			reconnect,
26367c478bd9Sstevel@tonic-gate 	const struct timeval	*waitp,
2637de8c4a14SErik Nordmark 	bool_t			nosignal,
2638de8c4a14SErik Nordmark 	cred_t			*cr)
26397c478bd9Sstevel@tonic-gate {
26407c478bd9Sstevel@tonic-gate 	mblk_t *mp;
26417c478bd9Sstevel@tonic-gate 	struct T_conn_req *tcr;
26427c478bd9Sstevel@tonic-gate 	struct T_info_ack *tinfo;
26437c478bd9Sstevel@tonic-gate 	int interrupted, error;
26447c478bd9Sstevel@tonic-gate 	int tidu_size, kstat_instance;
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 	/* if it's a reconnect, flush any lingering data messages */
26477c478bd9Sstevel@tonic-gate 	if (reconnect)
26487c478bd9Sstevel@tonic-gate 		(void) putctl1(wq, M_FLUSH, FLUSHRW);
26497c478bd9Sstevel@tonic-gate 
2650de8c4a14SErik Nordmark 	/*
2651de8c4a14SErik Nordmark 	 * Note: if the receiver uses SCM_UCRED/getpeerucred the pid will
2652de8c4a14SErik Nordmark 	 * appear as -1.
2653de8c4a14SErik Nordmark 	 */
2654de8c4a14SErik Nordmark 	mp = allocb_cred(sizeof (*tcr) + addr->len, cr, NOPID);
26557c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
26567c478bd9Sstevel@tonic-gate 		/*
26577c478bd9Sstevel@tonic-gate 		 * This is unfortunate, but we need to look up the stats for
26587c478bd9Sstevel@tonic-gate 		 * this zone to increment the "memory allocation failed"
26597c478bd9Sstevel@tonic-gate 		 * counter.  curproc->p_zone is safe since we're initiating a
26607c478bd9Sstevel@tonic-gate 		 * connection and not in some strange streams context.
26617c478bd9Sstevel@tonic-gate 		 */
26627c478bd9Sstevel@tonic-gate 		struct rpcstat *rpcstat;
26637c478bd9Sstevel@tonic-gate 
2664108322fbScarlsonj 		rpcstat = zone_getspecific(rpcstat_zone_key, rpc_zone());
26657c478bd9Sstevel@tonic-gate 		ASSERT(rpcstat != NULL);
26667c478bd9Sstevel@tonic-gate 
26677c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "connmgr_connect: cannot alloc mp for "
26687c478bd9Sstevel@tonic-gate 		    "sending conn request\n");
26697c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(rpcstat->rpc_cots_client, rcnomem);
26707c478bd9Sstevel@tonic-gate 		e->call_status = RPC_SYSTEMERROR;
26717c478bd9Sstevel@tonic-gate 		e->call_reason = ENOSR;
26727c478bd9Sstevel@tonic-gate 		return (FALSE);
26737c478bd9Sstevel@tonic-gate 	}
26747c478bd9Sstevel@tonic-gate 
26752081ac19SDai Ngo 	/* Set TCP buffer size for RPC connections if needed */
26762081ac19SDai Ngo 	if (addrfmly == AF_INET || addrfmly == AF_INET6)
26772081ac19SDai Ngo 		(void) connmgr_setbufsz(e, wq, cr);
26782081ac19SDai Ngo 
26797c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
26807c478bd9Sstevel@tonic-gate 	tcr = (struct T_conn_req *)mp->b_rptr;
26817c478bd9Sstevel@tonic-gate 	bzero(tcr, sizeof (*tcr));
26827c478bd9Sstevel@tonic-gate 	tcr->PRIM_type = T_CONN_REQ;
26837c478bd9Sstevel@tonic-gate 	tcr->DEST_length = addr->len;
26847c478bd9Sstevel@tonic-gate 	tcr->DEST_offset = sizeof (struct T_conn_req);
26857c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (*tcr);
26867c478bd9Sstevel@tonic-gate 
26877c478bd9Sstevel@tonic-gate 	bcopy(addr->buf, mp->b_wptr, tcr->DEST_length);
26887c478bd9Sstevel@tonic-gate 	mp->b_wptr += tcr->DEST_length;
26897c478bd9Sstevel@tonic-gate 
26907c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_connect: sending conn request on queue "
26917c478bd9Sstevel@tonic-gate 	    "%p", (void *)wq);
26927c478bd9Sstevel@tonic-gate 	RPCLOG(8, " call %p\n", (void *)wq);
26937c478bd9Sstevel@tonic-gate 	/*
26947c478bd9Sstevel@tonic-gate 	 * We use the entry in the handle that is normally used for
26957c478bd9Sstevel@tonic-gate 	 * waiting for RPC replies to wait for the connection accept.
26967c478bd9Sstevel@tonic-gate 	 */
2697125a8fd9SSiddheshwar Mahesh 	if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) {
2698125a8fd9SSiddheshwar Mahesh 		DTRACE_PROBE(krpc__e__connmgr__connect__cantsend);
2699125a8fd9SSiddheshwar Mahesh 		freemsg(mp);
2700125a8fd9SSiddheshwar Mahesh 		return (FALSE);
2701125a8fd9SSiddheshwar Mahesh 	}
27027c478bd9Sstevel@tonic-gate 
27037c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
27047c478bd9Sstevel@tonic-gate 
27057c478bd9Sstevel@tonic-gate 	/*
27067c478bd9Sstevel@tonic-gate 	 * We wait for the transport connection to be made, or an
27077c478bd9Sstevel@tonic-gate 	 * indication that it could not be made.
27087c478bd9Sstevel@tonic-gate 	 */
27097c478bd9Sstevel@tonic-gate 	interrupted = 0;
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate 	/*
27127c478bd9Sstevel@tonic-gate 	 * waitforack should have been called with T_OK_ACK, but the
27137c478bd9Sstevel@tonic-gate 	 * present implementation needs to be passed T_INFO_ACK to
27147c478bd9Sstevel@tonic-gate 	 * work correctly.
27157c478bd9Sstevel@tonic-gate 	 */
27167c478bd9Sstevel@tonic-gate 	error = waitforack(e, T_INFO_ACK, waitp, nosignal);
27177c478bd9Sstevel@tonic-gate 	if (error == EINTR)
27187c478bd9Sstevel@tonic-gate 		interrupted = 1;
27197c478bd9Sstevel@tonic-gate 	if (zone_status_get(curproc->p_zone) >= ZONE_IS_EMPTY) {
27207c478bd9Sstevel@tonic-gate 		/*
27217c478bd9Sstevel@tonic-gate 		 * No time to lose; we essentially have been signaled to
27227c478bd9Sstevel@tonic-gate 		 * quit.
27237c478bd9Sstevel@tonic-gate 		 */
27247c478bd9Sstevel@tonic-gate 		interrupted = 1;
27257c478bd9Sstevel@tonic-gate 	}
27267c478bd9Sstevel@tonic-gate #ifdef RPCDEBUG
27277c478bd9Sstevel@tonic-gate 	if (error == ETIME)
27287c478bd9Sstevel@tonic-gate 		RPCLOG0(8, "connmgr_connect: giving up "
27297c478bd9Sstevel@tonic-gate 		    "on connection attempt; "
27307c478bd9Sstevel@tonic-gate 		    "clnt_dispatch notifyconn "
27317c478bd9Sstevel@tonic-gate 		    "diagnostic 'no one waiting for "
27327c478bd9Sstevel@tonic-gate 		    "connection' should not be "
27337c478bd9Sstevel@tonic-gate 		    "unexpected\n");
27347c478bd9Sstevel@tonic-gate #endif
27357c478bd9Sstevel@tonic-gate 	if (e->call_prev)
27367c478bd9Sstevel@tonic-gate 		e->call_prev->call_next = e->call_next;
27377c478bd9Sstevel@tonic-gate 	else
27387c478bd9Sstevel@tonic-gate 		clnt_pending = e->call_next;
27397c478bd9Sstevel@tonic-gate 	if (e->call_next)
27407c478bd9Sstevel@tonic-gate 		e->call_next->call_prev = e->call_prev;
27417c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
27427c478bd9Sstevel@tonic-gate 
27437c478bd9Sstevel@tonic-gate 	if (e->call_status != RPC_SUCCESS || error != 0) {
27447c478bd9Sstevel@tonic-gate 		if (interrupted)
27457c478bd9Sstevel@tonic-gate 			e->call_status = RPC_INTR;
27467c478bd9Sstevel@tonic-gate 		else if (error == ETIME)
27477c478bd9Sstevel@tonic-gate 			e->call_status = RPC_TIMEDOUT;
27488c3630f0SGerald Thornbrugh 		else if (error == EPROTO) {
27497c478bd9Sstevel@tonic-gate 			e->call_status = RPC_SYSTEMERROR;
27508c3630f0SGerald Thornbrugh 			e->call_reason = EPROTO;
27518c3630f0SGerald Thornbrugh 		}
27527c478bd9Sstevel@tonic-gate 
27537c478bd9Sstevel@tonic-gate 		RPCLOG(8, "connmgr_connect: can't connect, status: "
27547c478bd9Sstevel@tonic-gate 		    "%s\n", clnt_sperrno(e->call_status));
27557c478bd9Sstevel@tonic-gate 
27567c478bd9Sstevel@tonic-gate 		if (e->call_reply) {
27577c478bd9Sstevel@tonic-gate 			freemsg(e->call_reply);
27587c478bd9Sstevel@tonic-gate 			e->call_reply = NULL;
27597c478bd9Sstevel@tonic-gate 		}
27607c478bd9Sstevel@tonic-gate 
27617c478bd9Sstevel@tonic-gate 		return (FALSE);
27627c478bd9Sstevel@tonic-gate 	}
27637c478bd9Sstevel@tonic-gate 	/*
27647c478bd9Sstevel@tonic-gate 	 * The result of the "connection accept" is a T_info_ack
27657c478bd9Sstevel@tonic-gate 	 * in the call_reply field.
27667c478bd9Sstevel@tonic-gate 	 */
27677c478bd9Sstevel@tonic-gate 	ASSERT(e->call_reply != NULL);
27687c478bd9Sstevel@tonic-gate 	mp = e->call_reply;
27697c478bd9Sstevel@tonic-gate 	e->call_reply = NULL;
27707c478bd9Sstevel@tonic-gate 	tinfo = (struct T_info_ack *)mp->b_rptr;
27717c478bd9Sstevel@tonic-gate 
27727c478bd9Sstevel@tonic-gate 	tidu_size = tinfo->TIDU_size;
27737c478bd9Sstevel@tonic-gate 	tidu_size -= (tidu_size % BYTES_PER_XDR_UNIT);
27747c478bd9Sstevel@tonic-gate 	if (tidu_size > COTS_DEFAULT_ALLOCSIZE || (tidu_size <= 0))
27757c478bd9Sstevel@tonic-gate 		tidu_size = COTS_DEFAULT_ALLOCSIZE;
27767c478bd9Sstevel@tonic-gate 	*tidu_ptr = tidu_size;
27777c478bd9Sstevel@tonic-gate 
27787c478bd9Sstevel@tonic-gate 	freemsg(mp);
27797c478bd9Sstevel@tonic-gate 
27807c478bd9Sstevel@tonic-gate 	/*
27817c478bd9Sstevel@tonic-gate 	 * Set up the pertinent options.  NODELAY is so the transport doesn't
27827c478bd9Sstevel@tonic-gate 	 * buffer up RPC messages on either end.  This may not be valid for
27837c478bd9Sstevel@tonic-gate 	 * all transports.  Failure to set this option is not cause to
27847c478bd9Sstevel@tonic-gate 	 * bail out so we return success anyway.  Note that lack of NODELAY
27857c478bd9Sstevel@tonic-gate 	 * or some other way to flush the message on both ends will cause
27867c478bd9Sstevel@tonic-gate 	 * lots of retries and terrible performance.
27877c478bd9Sstevel@tonic-gate 	 */
27887c478bd9Sstevel@tonic-gate 	if (addrfmly == AF_INET || addrfmly == AF_INET6) {
2789de8c4a14SErik Nordmark 		(void) connmgr_setopt(wq, IPPROTO_TCP, TCP_NODELAY, e, cr);
27907c478bd9Sstevel@tonic-gate 		if (e->call_status == RPC_XPRTFAILED)
27917c478bd9Sstevel@tonic-gate 			return (FALSE);
27927c478bd9Sstevel@tonic-gate 	}
27937c478bd9Sstevel@tonic-gate 
27947c478bd9Sstevel@tonic-gate 	/*
27957c478bd9Sstevel@tonic-gate 	 * Since we have a connection, we now need to figure out if
27967c478bd9Sstevel@tonic-gate 	 * we need to create a kstat. If x_ksp is not NULL then we
27977c478bd9Sstevel@tonic-gate 	 * are reusing a connection and so we do not need to create
27987c478bd9Sstevel@tonic-gate 	 * another kstat -- lets just return.
27997c478bd9Sstevel@tonic-gate 	 */
28007c478bd9Sstevel@tonic-gate 	if (cm_entry->x_ksp != NULL)
28017c478bd9Sstevel@tonic-gate 		return (TRUE);
28027c478bd9Sstevel@tonic-gate 
28037c478bd9Sstevel@tonic-gate 	/*
28047c478bd9Sstevel@tonic-gate 	 * We need to increment rpc_kstat_instance atomically to prevent
28057c478bd9Sstevel@tonic-gate 	 * two kstats being created with the same instance.
28067c478bd9Sstevel@tonic-gate 	 */
28071a5e258fSJosef 'Jeff' Sipek 	kstat_instance = atomic_inc_32_nv((uint32_t *)&rpc_kstat_instance);
28087c478bd9Sstevel@tonic-gate 
28097c478bd9Sstevel@tonic-gate 	if ((cm_entry->x_ksp = kstat_create_zone("unix", kstat_instance,
28107c478bd9Sstevel@tonic-gate 	    "rpc_cots_connections", "rpc", KSTAT_TYPE_NAMED,
28117c478bd9Sstevel@tonic-gate 	    (uint_t)(sizeof (cm_kstat_xprt_t) / sizeof (kstat_named_t)),
28127c478bd9Sstevel@tonic-gate 	    KSTAT_FLAG_VIRTUAL, cm_entry->x_zoneid)) == NULL) {
28137c478bd9Sstevel@tonic-gate 		return (TRUE);
28147c478bd9Sstevel@tonic-gate 	}
28157c478bd9Sstevel@tonic-gate 
28167c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_lock = &connmgr_lock;
28177c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_private = cm_entry;
28187c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_data_size = ((INET6_ADDRSTRLEN * sizeof (char))
28197c478bd9Sstevel@tonic-gate 	    + sizeof (cm_kstat_template));
28207c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_data = kmem_alloc(cm_entry->x_ksp->ks_data_size,
28217c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
28227c478bd9Sstevel@tonic-gate 	bcopy(&cm_kstat_template, cm_entry->x_ksp->ks_data,
28237c478bd9Sstevel@tonic-gate 	    cm_entry->x_ksp->ks_data_size);
28247c478bd9Sstevel@tonic-gate 	((struct cm_kstat_xprt *)(cm_entry->x_ksp->ks_data))->
2825a1b5e537Sbmc 	    x_server.value.str.addr.ptr =
28267c478bd9Sstevel@tonic-gate 	    kmem_alloc(INET6_ADDRSTRLEN, KM_SLEEP);
28277c478bd9Sstevel@tonic-gate 
28287c478bd9Sstevel@tonic-gate 	cm_entry->x_ksp->ks_update = conn_kstat_update;
28297c478bd9Sstevel@tonic-gate 	kstat_install(cm_entry->x_ksp);
28307c478bd9Sstevel@tonic-gate 	return (TRUE);
28317c478bd9Sstevel@tonic-gate }
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate /*
28342081ac19SDai Ngo  * Verify that the specified offset falls within the mblk and
28352081ac19SDai Ngo  * that the resulting pointer is aligned.
28362081ac19SDai Ngo  * Returns NULL if not.
28372081ac19SDai Ngo  *
28382081ac19SDai Ngo  * code from fs/sockfs/socksubr.c
28392081ac19SDai Ngo  */
28402081ac19SDai Ngo static void *
connmgr_opt_getoff(mblk_t * mp,t_uscalar_t offset,t_uscalar_t length,uint_t align_size)28412081ac19SDai Ngo connmgr_opt_getoff(mblk_t *mp, t_uscalar_t offset,
28422081ac19SDai Ngo     t_uscalar_t length, uint_t align_size)
28432081ac19SDai Ngo {
28442081ac19SDai Ngo 	uintptr_t ptr1, ptr2;
28452081ac19SDai Ngo 
28462081ac19SDai Ngo 	ASSERT(mp && mp->b_wptr >= mp->b_rptr);
28472081ac19SDai Ngo 	ptr1 = (uintptr_t)mp->b_rptr + offset;
28482081ac19SDai Ngo 	ptr2 = (uintptr_t)ptr1 + length;
28492081ac19SDai Ngo 	if (ptr1 < (uintptr_t)mp->b_rptr || ptr2 > (uintptr_t)mp->b_wptr) {
28502081ac19SDai Ngo 		return (NULL);
28512081ac19SDai Ngo 	}
28522081ac19SDai Ngo 	if ((ptr1 & (align_size - 1)) != 0) {
28532081ac19SDai Ngo 		return (NULL);
28542081ac19SDai Ngo 	}
28552081ac19SDai Ngo 	return ((void *)ptr1);
28562081ac19SDai Ngo }
28572081ac19SDai Ngo 
28582081ac19SDai Ngo static bool_t
connmgr_getopt_int(queue_t * wq,int level,int name,int * val,calllist_t * e,cred_t * cr)28592081ac19SDai Ngo connmgr_getopt_int(queue_t *wq, int level, int name, int *val,
28602081ac19SDai Ngo     calllist_t *e, cred_t *cr)
28612081ac19SDai Ngo {
28622081ac19SDai Ngo 	mblk_t *mp;
28632081ac19SDai Ngo 	struct opthdr *opt, *opt_res;
28642081ac19SDai Ngo 	struct T_optmgmt_req *tor;
28652081ac19SDai Ngo 	struct T_optmgmt_ack *opt_ack;
28662081ac19SDai Ngo 	struct timeval waitp;
28672081ac19SDai Ngo 	int error;
28682081ac19SDai Ngo 
28692081ac19SDai Ngo 	mp = allocb_cred(sizeof (struct T_optmgmt_req) +
28702081ac19SDai Ngo 	    sizeof (struct opthdr) + sizeof (int), cr, NOPID);
28712081ac19SDai Ngo 	if (mp == NULL)
28722081ac19SDai Ngo 		return (FALSE);
28732081ac19SDai Ngo 
28742081ac19SDai Ngo 	mp->b_datap->db_type = M_PROTO;
28752081ac19SDai Ngo 	tor = (struct T_optmgmt_req *)(mp->b_rptr);
28762081ac19SDai Ngo 	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
28772081ac19SDai Ngo 	tor->MGMT_flags = T_CURRENT;
28782081ac19SDai Ngo 	tor->OPT_length = sizeof (struct opthdr) + sizeof (int);
28792081ac19SDai Ngo 	tor->OPT_offset = sizeof (struct T_optmgmt_req);
28802081ac19SDai Ngo 
28812081ac19SDai Ngo 	opt = (struct opthdr *)(mp->b_rptr + sizeof (struct T_optmgmt_req));
28822081ac19SDai Ngo 	opt->level = level;
28832081ac19SDai Ngo 	opt->name = name;
28842081ac19SDai Ngo 	opt->len = sizeof (int);
28852081ac19SDai Ngo 	mp->b_wptr += sizeof (struct T_optmgmt_req) + sizeof (struct opthdr) +
28862081ac19SDai Ngo 	    sizeof (int);
28872081ac19SDai Ngo 
28882081ac19SDai Ngo 	/*
28892081ac19SDai Ngo 	 * We will use this connection regardless
28902081ac19SDai Ngo 	 * of whether or not the option is readable.
28912081ac19SDai Ngo 	 */
28922081ac19SDai Ngo 	if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) {
28932081ac19SDai Ngo 		DTRACE_PROBE(krpc__e__connmgr__getopt__cantsend);
28942081ac19SDai Ngo 		freemsg(mp);
28952081ac19SDai Ngo 		return (FALSE);
28962081ac19SDai Ngo 	}
28972081ac19SDai Ngo 
28982081ac19SDai Ngo 	mutex_enter(&clnt_pending_lock);
28992081ac19SDai Ngo 
29002081ac19SDai Ngo 	waitp.tv_sec = clnt_cots_min_conntout;
29012081ac19SDai Ngo 	waitp.tv_usec = 0;
29022081ac19SDai Ngo 	error = waitforack(e, T_OPTMGMT_ACK, &waitp, 1);
29032081ac19SDai Ngo 
29042081ac19SDai Ngo 	if (e->call_prev)
29052081ac19SDai Ngo 		e->call_prev->call_next = e->call_next;
29062081ac19SDai Ngo 	else
29072081ac19SDai Ngo 		clnt_pending = e->call_next;
29082081ac19SDai Ngo 	if (e->call_next)
29092081ac19SDai Ngo 		e->call_next->call_prev = e->call_prev;
29102081ac19SDai Ngo 	mutex_exit(&clnt_pending_lock);
29112081ac19SDai Ngo 
29122081ac19SDai Ngo 	/* get reply message */
29132081ac19SDai Ngo 	mp = e->call_reply;
29142081ac19SDai Ngo 	e->call_reply = NULL;
29152081ac19SDai Ngo 
29162081ac19SDai Ngo 	if ((!mp) || (e->call_status != RPC_SUCCESS) || (error != 0)) {
29172081ac19SDai Ngo 
29182081ac19SDai Ngo 		DTRACE_PROBE4(krpc__e__connmgr_getopt, int, name,
29192081ac19SDai Ngo 		    int, e->call_status, int, error, mblk_t *, mp);
29202081ac19SDai Ngo 
29212081ac19SDai Ngo 		if (mp)
29222081ac19SDai Ngo 			freemsg(mp);
29232081ac19SDai Ngo 		return (FALSE);
29242081ac19SDai Ngo 	}
29252081ac19SDai Ngo 
29262081ac19SDai Ngo 	opt_ack = (struct T_optmgmt_ack *)mp->b_rptr;
29272081ac19SDai Ngo 	opt_res = (struct opthdr *)connmgr_opt_getoff(mp, opt_ack->OPT_offset,
29282081ac19SDai Ngo 	    opt_ack->OPT_length, __TPI_ALIGN_SIZE);
29292081ac19SDai Ngo 
29302081ac19SDai Ngo 	if (!opt_res) {
29312081ac19SDai Ngo 		DTRACE_PROBE4(krpc__e__connmgr_optres, mblk_t *, mp, int, name,
29322081ac19SDai Ngo 		    int, opt_ack->OPT_offset, int, opt_ack->OPT_length);
29332081ac19SDai Ngo 		freemsg(mp);
29342081ac19SDai Ngo 		return (FALSE);
29352081ac19SDai Ngo 	}
29362081ac19SDai Ngo 	*val = *(int *)&opt_res[1];
29372081ac19SDai Ngo 
29382081ac19SDai Ngo 	DTRACE_PROBE2(connmgr_getopt__ok, int, name, int, *val);
29392081ac19SDai Ngo 
29402081ac19SDai Ngo 	freemsg(mp);
29412081ac19SDai Ngo 	return (TRUE);
29422081ac19SDai Ngo }
29432081ac19SDai Ngo 
29442081ac19SDai Ngo /*
29457c478bd9Sstevel@tonic-gate  * Called by connmgr_connect to set an option on the new stream.
29467c478bd9Sstevel@tonic-gate  */
29477c478bd9Sstevel@tonic-gate static bool_t
connmgr_setopt_int(queue_t * wq,int level,int name,int val,calllist_t * e,cred_t * cr)29482081ac19SDai Ngo connmgr_setopt_int(queue_t *wq, int level, int name, int val,
29492081ac19SDai Ngo     calllist_t *e, cred_t *cr)
29507c478bd9Sstevel@tonic-gate {
29517c478bd9Sstevel@tonic-gate 	mblk_t *mp;
29527c478bd9Sstevel@tonic-gate 	struct opthdr *opt;
29537c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor;
29547c478bd9Sstevel@tonic-gate 	struct timeval waitp;
29557c478bd9Sstevel@tonic-gate 	int error;
29567c478bd9Sstevel@tonic-gate 
2957de8c4a14SErik Nordmark 	mp = allocb_cred(sizeof (struct T_optmgmt_req) +
2958de8c4a14SErik Nordmark 	    sizeof (struct opthdr) + sizeof (int), cr, NOPID);
29597c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
29607c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "connmgr_setopt: cannot alloc mp for option "
29617c478bd9Sstevel@tonic-gate 		    "request\n");
29627c478bd9Sstevel@tonic-gate 		return (FALSE);
29637c478bd9Sstevel@tonic-gate 	}
29647c478bd9Sstevel@tonic-gate 
29657c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
29667c478bd9Sstevel@tonic-gate 	tor = (struct T_optmgmt_req *)(mp->b_rptr);
29677c478bd9Sstevel@tonic-gate 	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
29687c478bd9Sstevel@tonic-gate 	tor->MGMT_flags = T_NEGOTIATE;
29697c478bd9Sstevel@tonic-gate 	tor->OPT_length = sizeof (struct opthdr) + sizeof (int);
29707c478bd9Sstevel@tonic-gate 	tor->OPT_offset = sizeof (struct T_optmgmt_req);
29717c478bd9Sstevel@tonic-gate 
29727c478bd9Sstevel@tonic-gate 	opt = (struct opthdr *)(mp->b_rptr + sizeof (struct T_optmgmt_req));
29737c478bd9Sstevel@tonic-gate 	opt->level = level;
29747c478bd9Sstevel@tonic-gate 	opt->name = name;
29757c478bd9Sstevel@tonic-gate 	opt->len = sizeof (int);
29762081ac19SDai Ngo 	*(int *)((char *)opt + sizeof (*opt)) = val;
29777c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (struct T_optmgmt_req) + sizeof (struct opthdr) +
29787c478bd9Sstevel@tonic-gate 	    sizeof (int);
29797c478bd9Sstevel@tonic-gate 
29807c478bd9Sstevel@tonic-gate 	/*
29817c478bd9Sstevel@tonic-gate 	 * We will use this connection regardless
29827c478bd9Sstevel@tonic-gate 	 * of whether or not the option is settable.
29837c478bd9Sstevel@tonic-gate 	 */
2984125a8fd9SSiddheshwar Mahesh 	if (clnt_dispatch_send(wq, mp, e, 0, 0) != RPC_SUCCESS) {
2985125a8fd9SSiddheshwar Mahesh 		DTRACE_PROBE(krpc__e__connmgr__setopt__cantsend);
2986125a8fd9SSiddheshwar Mahesh 		freemsg(mp);
2987125a8fd9SSiddheshwar Mahesh 		return (FALSE);
2988125a8fd9SSiddheshwar Mahesh 	}
2989125a8fd9SSiddheshwar Mahesh 
29907c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 	waitp.tv_sec = clnt_cots_min_conntout;
29937c478bd9Sstevel@tonic-gate 	waitp.tv_usec = 0;
29947c478bd9Sstevel@tonic-gate 	error = waitforack(e, T_OPTMGMT_ACK, &waitp, 1);
29957c478bd9Sstevel@tonic-gate 
29967c478bd9Sstevel@tonic-gate 	if (e->call_prev)
29977c478bd9Sstevel@tonic-gate 		e->call_prev->call_next = e->call_next;
29987c478bd9Sstevel@tonic-gate 	else
29997c478bd9Sstevel@tonic-gate 		clnt_pending = e->call_next;
30007c478bd9Sstevel@tonic-gate 	if (e->call_next)
30017c478bd9Sstevel@tonic-gate 		e->call_next->call_prev = e->call_prev;
30027c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
30037c478bd9Sstevel@tonic-gate 
30047c478bd9Sstevel@tonic-gate 	if (e->call_reply != NULL) {
30057c478bd9Sstevel@tonic-gate 		freemsg(e->call_reply);
30067c478bd9Sstevel@tonic-gate 		e->call_reply = NULL;
30077c478bd9Sstevel@tonic-gate 	}
30087c478bd9Sstevel@tonic-gate 
30097c478bd9Sstevel@tonic-gate 	if (e->call_status != RPC_SUCCESS || error != 0) {
30107c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_setopt: can't set option: %d\n", name);
30117c478bd9Sstevel@tonic-gate 		return (FALSE);
30127c478bd9Sstevel@tonic-gate 	}
30137c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_setopt: successfully set option: %d\n", name);
30147c478bd9Sstevel@tonic-gate 	return (TRUE);
30157c478bd9Sstevel@tonic-gate }
30167c478bd9Sstevel@tonic-gate 
30172081ac19SDai Ngo static bool_t
connmgr_setopt(queue_t * wq,int level,int name,calllist_t * e,cred_t * cr)30182081ac19SDai Ngo connmgr_setopt(queue_t *wq, int level, int name, calllist_t *e, cred_t *cr)
30192081ac19SDai Ngo {
30202081ac19SDai Ngo 	return (connmgr_setopt_int(wq, level, name, 1, e, cr));
30212081ac19SDai Ngo }
30222081ac19SDai Ngo 
30237c478bd9Sstevel@tonic-gate #ifdef	DEBUG
30247c478bd9Sstevel@tonic-gate 
30257c478bd9Sstevel@tonic-gate /*
30267c478bd9Sstevel@tonic-gate  * This is a knob to let us force code coverage in allocation failure
30277c478bd9Sstevel@tonic-gate  * case.
30287c478bd9Sstevel@tonic-gate  */
30297c478bd9Sstevel@tonic-gate static int	connmgr_failsnd;
30307c478bd9Sstevel@tonic-gate #define	CONN_SND_ALLOC(Size, Pri)	\
30317c478bd9Sstevel@tonic-gate 	((connmgr_failsnd-- > 0) ? NULL : allocb(Size, Pri))
30327c478bd9Sstevel@tonic-gate 
30337c478bd9Sstevel@tonic-gate #else
30347c478bd9Sstevel@tonic-gate 
30357c478bd9Sstevel@tonic-gate #define	CONN_SND_ALLOC(Size, Pri)	allocb(Size, Pri)
30367c478bd9Sstevel@tonic-gate 
30377c478bd9Sstevel@tonic-gate #endif
30387c478bd9Sstevel@tonic-gate 
30397c478bd9Sstevel@tonic-gate /*
30407c478bd9Sstevel@tonic-gate  * Sends an orderly release on the specified queue.
30417c478bd9Sstevel@tonic-gate  * Entered with connmgr_lock. Exited without connmgr_lock
30427c478bd9Sstevel@tonic-gate  */
30437c478bd9Sstevel@tonic-gate static void
connmgr_sndrel(struct cm_xprt * cm_entry)30447c478bd9Sstevel@tonic-gate connmgr_sndrel(struct cm_xprt *cm_entry)
30457c478bd9Sstevel@tonic-gate {
30467c478bd9Sstevel@tonic-gate 	struct T_ordrel_req *torr;
30477c478bd9Sstevel@tonic-gate 	mblk_t *mp;
30487c478bd9Sstevel@tonic-gate 	queue_t *q = cm_entry->x_wq;
30497c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
30507c478bd9Sstevel@tonic-gate 	mp = CONN_SND_ALLOC(sizeof (struct T_ordrel_req), BPRI_LO);
30517c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
30527c478bd9Sstevel@tonic-gate 		cm_entry->x_needrel = TRUE;
30537c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
30547c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_sndrel: cannot alloc mp for sending ordrel "
30557c478bd9Sstevel@tonic-gate 		    "to queue %p\n", (void *)q);
30567c478bd9Sstevel@tonic-gate 		return;
30577c478bd9Sstevel@tonic-gate 	}
30587c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
30597c478bd9Sstevel@tonic-gate 
30607c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
30617c478bd9Sstevel@tonic-gate 	torr = (struct T_ordrel_req *)(mp->b_rptr);
30627c478bd9Sstevel@tonic-gate 	torr->PRIM_type = T_ORDREL_REQ;
30637c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (struct T_ordrel_req);
30647c478bd9Sstevel@tonic-gate 
30657c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_sndrel: sending ordrel to queue %p\n", (void *)q);
30667c478bd9Sstevel@tonic-gate 	put(q, mp);
30677c478bd9Sstevel@tonic-gate }
30687c478bd9Sstevel@tonic-gate 
30697c478bd9Sstevel@tonic-gate /*
30707c478bd9Sstevel@tonic-gate  * Sends an disconnect on the specified queue.
30717c478bd9Sstevel@tonic-gate  * Entered with connmgr_lock. Exited without connmgr_lock
30727c478bd9Sstevel@tonic-gate  */
30737c478bd9Sstevel@tonic-gate static void
connmgr_snddis(struct cm_xprt * cm_entry)30747c478bd9Sstevel@tonic-gate connmgr_snddis(struct cm_xprt *cm_entry)
30757c478bd9Sstevel@tonic-gate {
30767c478bd9Sstevel@tonic-gate 	struct T_discon_req *tdis;
30777c478bd9Sstevel@tonic-gate 	mblk_t *mp;
30787c478bd9Sstevel@tonic-gate 	queue_t *q = cm_entry->x_wq;
30797c478bd9Sstevel@tonic-gate 
30807c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connmgr_lock));
30817c478bd9Sstevel@tonic-gate 	mp = CONN_SND_ALLOC(sizeof (*tdis), BPRI_LO);
30827c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
30837c478bd9Sstevel@tonic-gate 		cm_entry->x_needdis = TRUE;
30847c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
30857c478bd9Sstevel@tonic-gate 		RPCLOG(1, "connmgr_snddis: cannot alloc mp for sending discon "
30867c478bd9Sstevel@tonic-gate 		    "to queue %p\n", (void *)q);
30877c478bd9Sstevel@tonic-gate 		return;
30887c478bd9Sstevel@tonic-gate 	}
30897c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
30907c478bd9Sstevel@tonic-gate 
30917c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
30927c478bd9Sstevel@tonic-gate 	tdis = (struct T_discon_req *)mp->b_rptr;
30937c478bd9Sstevel@tonic-gate 	tdis->PRIM_type = T_DISCON_REQ;
30947c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (*tdis);
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate 	RPCLOG(8, "connmgr_snddis: sending discon to queue %p\n", (void *)q);
30977c478bd9Sstevel@tonic-gate 	put(q, mp);
30987c478bd9Sstevel@tonic-gate }
30997c478bd9Sstevel@tonic-gate 
31007c478bd9Sstevel@tonic-gate /*
31017c478bd9Sstevel@tonic-gate  * Sets up the entry for receiving replies, and calls rpcmod's write put proc
31027c478bd9Sstevel@tonic-gate  * (through put) to send the call.
31037c478bd9Sstevel@tonic-gate  */
3104125a8fd9SSiddheshwar Mahesh static int
clnt_dispatch_send(queue_t * q,mblk_t * mp,calllist_t * e,uint_t xid,uint_t queue_flag)31057c478bd9Sstevel@tonic-gate clnt_dispatch_send(queue_t *q, mblk_t *mp, calllist_t *e, uint_t xid,
31067c478bd9Sstevel@tonic-gate     uint_t queue_flag)
31077c478bd9Sstevel@tonic-gate {
31087c478bd9Sstevel@tonic-gate 	ASSERT(e != NULL);
31097c478bd9Sstevel@tonic-gate 
31107c478bd9Sstevel@tonic-gate 	e->call_status = RPC_TIMEDOUT;	/* optimistic, eh? */
31117c478bd9Sstevel@tonic-gate 	e->call_reason = 0;
31127c478bd9Sstevel@tonic-gate 	e->call_wq = q;
31137c478bd9Sstevel@tonic-gate 	e->call_xid = xid;
31147c478bd9Sstevel@tonic-gate 	e->call_notified = FALSE;
31157c478bd9Sstevel@tonic-gate 
3116125a8fd9SSiddheshwar Mahesh 	if (!canput(q)) {
3117125a8fd9SSiddheshwar Mahesh 		e->call_status = RPC_CANTSEND;
3118e280ed37SDai Ngo 		e->call_reason = ENOBUFS;
3119125a8fd9SSiddheshwar Mahesh 		return (RPC_CANTSEND);
3120125a8fd9SSiddheshwar Mahesh 	}
3121125a8fd9SSiddheshwar Mahesh 
31227c478bd9Sstevel@tonic-gate 	/*
31237c478bd9Sstevel@tonic-gate 	 * If queue_flag is set then the calllist_t is already on the hash
31247c478bd9Sstevel@tonic-gate 	 * queue.  In this case just send the message and return.
31257c478bd9Sstevel@tonic-gate 	 */
31267c478bd9Sstevel@tonic-gate 	if (queue_flag) {
31277c478bd9Sstevel@tonic-gate 		put(q, mp);
3128125a8fd9SSiddheshwar Mahesh 		return (RPC_SUCCESS);
3129125a8fd9SSiddheshwar Mahesh 
31307c478bd9Sstevel@tonic-gate 	}
31317c478bd9Sstevel@tonic-gate 
31327c478bd9Sstevel@tonic-gate 	/*
31337c478bd9Sstevel@tonic-gate 	 * Set up calls for RPC requests (with XID != 0) on the hash
31347c478bd9Sstevel@tonic-gate 	 * queue for fast lookups and place other calls (i.e.
31357c478bd9Sstevel@tonic-gate 	 * connection management) on the linked list.
31367c478bd9Sstevel@tonic-gate 	 */
31377c478bd9Sstevel@tonic-gate 	if (xid != 0) {
31387c478bd9Sstevel@tonic-gate 		RPCLOG(64, "clnt_dispatch_send: putting xid 0x%x on "
31397c478bd9Sstevel@tonic-gate 		    "dispatch list\n", xid);
31407c478bd9Sstevel@tonic-gate 		e->call_hash = call_hash(xid, clnt_cots_hash_size);
31417c478bd9Sstevel@tonic-gate 		e->call_bucket = &cots_call_ht[e->call_hash];
31427c478bd9Sstevel@tonic-gate 		call_table_enter(e);
31437c478bd9Sstevel@tonic-gate 	} else {
31447c478bd9Sstevel@tonic-gate 		mutex_enter(&clnt_pending_lock);
31457c478bd9Sstevel@tonic-gate 		if (clnt_pending)
31467c478bd9Sstevel@tonic-gate 			clnt_pending->call_prev = e;
31477c478bd9Sstevel@tonic-gate 		e->call_next = clnt_pending;
31487c478bd9Sstevel@tonic-gate 		e->call_prev = NULL;
31497c478bd9Sstevel@tonic-gate 		clnt_pending = e;
31507c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_pending_lock);
31517c478bd9Sstevel@tonic-gate 	}
31527c478bd9Sstevel@tonic-gate 
31537c478bd9Sstevel@tonic-gate 	put(q, mp);
3154125a8fd9SSiddheshwar Mahesh 	return (RPC_SUCCESS);
31557c478bd9Sstevel@tonic-gate }
31567c478bd9Sstevel@tonic-gate 
31577c478bd9Sstevel@tonic-gate /*
31587c478bd9Sstevel@tonic-gate  * Called by rpcmod to notify a client with a clnt_pending call that its reply
31597c478bd9Sstevel@tonic-gate  * has arrived.  If we can't find a client waiting for this reply, we log
31607c478bd9Sstevel@tonic-gate  * the error and return.
31617c478bd9Sstevel@tonic-gate  */
31627c478bd9Sstevel@tonic-gate bool_t
clnt_dispatch_notify(mblk_t * mp,zoneid_t zoneid)31637c478bd9Sstevel@tonic-gate clnt_dispatch_notify(mblk_t *mp, zoneid_t zoneid)
31647c478bd9Sstevel@tonic-gate {
31657c478bd9Sstevel@tonic-gate 	calllist_t *e = NULL;
31667c478bd9Sstevel@tonic-gate 	call_table_t *chtp;
31677c478bd9Sstevel@tonic-gate 	uint32_t xid;
31687c478bd9Sstevel@tonic-gate 	uint_t hash;
31697c478bd9Sstevel@tonic-gate 
31707c478bd9Sstevel@tonic-gate 	if ((IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) &&
31717c478bd9Sstevel@tonic-gate 	    (mp->b_wptr - mp->b_rptr) >= sizeof (xid))
31727c478bd9Sstevel@tonic-gate 		xid = *((uint32_t *)mp->b_rptr);
31737c478bd9Sstevel@tonic-gate 	else {
31747c478bd9Sstevel@tonic-gate 		int i = 0;
31757c478bd9Sstevel@tonic-gate 		unsigned char *p = (unsigned char *)&xid;
31767c478bd9Sstevel@tonic-gate 		unsigned char *rptr;
31777c478bd9Sstevel@tonic-gate 		mblk_t *tmp = mp;
31787c478bd9Sstevel@tonic-gate 
31797c478bd9Sstevel@tonic-gate 		/*
31807c478bd9Sstevel@tonic-gate 		 * Copy the xid, byte-by-byte into xid.
31817c478bd9Sstevel@tonic-gate 		 */
31827c478bd9Sstevel@tonic-gate 		while (tmp) {
31837c478bd9Sstevel@tonic-gate 			rptr = tmp->b_rptr;
31847c478bd9Sstevel@tonic-gate 			while (rptr < tmp->b_wptr) {
31857c478bd9Sstevel@tonic-gate 				*p++ = *rptr++;
31867c478bd9Sstevel@tonic-gate 				if (++i >= sizeof (xid))
31877c478bd9Sstevel@tonic-gate 					goto done_xid_copy;
31887c478bd9Sstevel@tonic-gate 			}
31897c478bd9Sstevel@tonic-gate 			tmp = tmp->b_cont;
31907c478bd9Sstevel@tonic-gate 		}
31917c478bd9Sstevel@tonic-gate 
31927c478bd9Sstevel@tonic-gate 		/*
31937c478bd9Sstevel@tonic-gate 		 * If we got here, we ran out of mblk space before the
31947c478bd9Sstevel@tonic-gate 		 * xid could be copied.
31957c478bd9Sstevel@tonic-gate 		 */
31967c478bd9Sstevel@tonic-gate 		ASSERT(tmp == NULL && i < sizeof (xid));
31977c478bd9Sstevel@tonic-gate 
31987c478bd9Sstevel@tonic-gate 		RPCLOG0(1,
31997c478bd9Sstevel@tonic-gate 		    "clnt_dispatch_notify: message less than size of xid\n");
32007c478bd9Sstevel@tonic-gate 		return (FALSE);
32017c478bd9Sstevel@tonic-gate 
32027c478bd9Sstevel@tonic-gate 	}
32037c478bd9Sstevel@tonic-gate done_xid_copy:
32047c478bd9Sstevel@tonic-gate 
32057c478bd9Sstevel@tonic-gate 	hash = call_hash(xid, clnt_cots_hash_size);
32067c478bd9Sstevel@tonic-gate 	chtp = &cots_call_ht[hash];
32077c478bd9Sstevel@tonic-gate 	/* call_table_find returns with the hash bucket locked */
32087c478bd9Sstevel@tonic-gate 	call_table_find(chtp, xid, e);
32097c478bd9Sstevel@tonic-gate 
32107c478bd9Sstevel@tonic-gate 	if (e != NULL) {
32117c478bd9Sstevel@tonic-gate 		/*
32127c478bd9Sstevel@tonic-gate 		 * Found thread waiting for this reply
32137c478bd9Sstevel@tonic-gate 		 */
32147c478bd9Sstevel@tonic-gate 		mutex_enter(&e->call_lock);
32158ffff9fdSgt29601 
32168ffff9fdSgt29601 		/*
32178ffff9fdSgt29601 		 * verify that the reply is coming in on
32188ffff9fdSgt29601 		 * the same zone that it was sent from.
32198ffff9fdSgt29601 		 */
32208ffff9fdSgt29601 		if (e->call_zoneid != zoneid) {
32218ffff9fdSgt29601 			mutex_exit(&e->call_lock);
32228ffff9fdSgt29601 			mutex_exit(&chtp->ct_lock);
32238c3630f0SGerald Thornbrugh 			RPCLOG0(1, "clnt_dispatch_notify: incorrect zoneid\n");
32248ffff9fdSgt29601 			return (FALSE);
32258ffff9fdSgt29601 		}
32268ffff9fdSgt29601 
32277c478bd9Sstevel@tonic-gate 		if (e->call_reply)
32287c478bd9Sstevel@tonic-gate 			/*
32297c478bd9Sstevel@tonic-gate 			 * This can happen under the following scenario:
32307c478bd9Sstevel@tonic-gate 			 * clnt_cots_kcallit() times out on the response,
32317c478bd9Sstevel@tonic-gate 			 * rfscall() repeats the CLNT_CALL() with
32327c478bd9Sstevel@tonic-gate 			 * the same xid, clnt_cots_kcallit() sends the retry,
32337c478bd9Sstevel@tonic-gate 			 * thereby putting the clnt handle on the pending list,
32347c478bd9Sstevel@tonic-gate 			 * the first response arrives, signalling the thread
32357c478bd9Sstevel@tonic-gate 			 * in clnt_cots_kcallit(). Before that thread is
32367c478bd9Sstevel@tonic-gate 			 * dispatched, the second response arrives as well,
32377c478bd9Sstevel@tonic-gate 			 * and clnt_dispatch_notify still finds the handle on
32387c478bd9Sstevel@tonic-gate 			 * the pending list, with call_reply set. So free the
32397c478bd9Sstevel@tonic-gate 			 * old reply now.
32407c478bd9Sstevel@tonic-gate 			 *
32417c478bd9Sstevel@tonic-gate 			 * It is also possible for a response intended for
32427c478bd9Sstevel@tonic-gate 			 * an RPC call with a different xid to reside here.
32437c478bd9Sstevel@tonic-gate 			 * This can happen if the thread that owned this
32447c478bd9Sstevel@tonic-gate 			 * client handle prior to the current owner bailed
32457c478bd9Sstevel@tonic-gate 			 * out and left its call record on the dispatch
32467c478bd9Sstevel@tonic-gate 			 * queue.  A window exists where the response can
32477c478bd9Sstevel@tonic-gate 			 * arrive before the current owner dispatches its
32487c478bd9Sstevel@tonic-gate 			 * RPC call.
32497c478bd9Sstevel@tonic-gate 			 *
32507c478bd9Sstevel@tonic-gate 			 * In any case, this is the very last point where we
32517c478bd9Sstevel@tonic-gate 			 * can safely check the call_reply field before
32527c478bd9Sstevel@tonic-gate 			 * placing the new response there.
32537c478bd9Sstevel@tonic-gate 			 */
32547c478bd9Sstevel@tonic-gate 			freemsg(e->call_reply);
32557c478bd9Sstevel@tonic-gate 		e->call_reply = mp;
32567c478bd9Sstevel@tonic-gate 		e->call_status = RPC_SUCCESS;
32577c478bd9Sstevel@tonic-gate 		e->call_notified = TRUE;
32587c478bd9Sstevel@tonic-gate 		cv_signal(&e->call_cv);
32597c478bd9Sstevel@tonic-gate 		mutex_exit(&e->call_lock);
32607c478bd9Sstevel@tonic-gate 		mutex_exit(&chtp->ct_lock);
32617c478bd9Sstevel@tonic-gate 		return (TRUE);
32627c478bd9Sstevel@tonic-gate 	} else {
32637c478bd9Sstevel@tonic-gate 		zone_t *zone;
32647c478bd9Sstevel@tonic-gate 		struct rpcstat *rpcstat;
32657c478bd9Sstevel@tonic-gate 
32667c478bd9Sstevel@tonic-gate 		mutex_exit(&chtp->ct_lock);
32677c478bd9Sstevel@tonic-gate 		RPCLOG(65, "clnt_dispatch_notify: no caller for reply 0x%x\n",
32687c478bd9Sstevel@tonic-gate 		    xid);
32697c478bd9Sstevel@tonic-gate 		/*
32707c478bd9Sstevel@tonic-gate 		 * This is unfortunate, but we need to lookup the zone so we
32717c478bd9Sstevel@tonic-gate 		 * can increment its "rcbadxids" counter.
32727c478bd9Sstevel@tonic-gate 		 */
32737c478bd9Sstevel@tonic-gate 		zone = zone_find_by_id(zoneid);
32747c478bd9Sstevel@tonic-gate 		if (zone == NULL) {
32757c478bd9Sstevel@tonic-gate 			/*
32767c478bd9Sstevel@tonic-gate 			 * The zone went away...
32777c478bd9Sstevel@tonic-gate 			 */
32787c478bd9Sstevel@tonic-gate 			return (FALSE);
32797c478bd9Sstevel@tonic-gate 		}
32807c478bd9Sstevel@tonic-gate 		rpcstat = zone_getspecific(rpcstat_zone_key, zone);
32817c478bd9Sstevel@tonic-gate 		if (zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN) {
32827c478bd9Sstevel@tonic-gate 			/*
32837c478bd9Sstevel@tonic-gate 			 * Not interested
32847c478bd9Sstevel@tonic-gate 			 */
32857c478bd9Sstevel@tonic-gate 			zone_rele(zone);
32867c478bd9Sstevel@tonic-gate 			return (FALSE);
32877c478bd9Sstevel@tonic-gate 		}
32887c478bd9Sstevel@tonic-gate 		COTSRCSTAT_INCR(rpcstat->rpc_cots_client, rcbadxids);
32897c478bd9Sstevel@tonic-gate 		zone_rele(zone);
32907c478bd9Sstevel@tonic-gate 	}
32917c478bd9Sstevel@tonic-gate 	return (FALSE);
32927c478bd9Sstevel@tonic-gate }
32937c478bd9Sstevel@tonic-gate 
32947c478bd9Sstevel@tonic-gate /*
32957c478bd9Sstevel@tonic-gate  * Called by rpcmod when a non-data indication arrives.  The ones in which we
32967c478bd9Sstevel@tonic-gate  * are interested are connection indications and options acks.  We dispatch
32977c478bd9Sstevel@tonic-gate  * based on the queue the indication came in on.  If we are not interested in
32987c478bd9Sstevel@tonic-gate  * what came in, we return false to rpcmod, who will then pass it upstream.
32997c478bd9Sstevel@tonic-gate  */
33007c478bd9Sstevel@tonic-gate bool_t
clnt_dispatch_notifyconn(queue_t * q,mblk_t * mp)33017c478bd9Sstevel@tonic-gate clnt_dispatch_notifyconn(queue_t *q, mblk_t *mp)
33027c478bd9Sstevel@tonic-gate {
33037c478bd9Sstevel@tonic-gate 	calllist_t *e;
33047c478bd9Sstevel@tonic-gate 	int type;
33057c478bd9Sstevel@tonic-gate 
33067c478bd9Sstevel@tonic-gate 	ASSERT((q->q_flag & QREADR) == 0);
33077c478bd9Sstevel@tonic-gate 
33087c478bd9Sstevel@tonic-gate 	type = ((union T_primitives *)mp->b_rptr)->type;
33097c478bd9Sstevel@tonic-gate 	RPCLOG(8, "clnt_dispatch_notifyconn: prim type: [%s]\n",
33107c478bd9Sstevel@tonic-gate 	    rpc_tpiprim2name(type));
33117c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
33127c478bd9Sstevel@tonic-gate 	for (e = clnt_pending; /* NO CONDITION */; e = e->call_next) {
33137c478bd9Sstevel@tonic-gate 		if (e == NULL) {
33147c478bd9Sstevel@tonic-gate 			mutex_exit(&clnt_pending_lock);
33157c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_dispatch_notifyconn: no one waiting "
33167c478bd9Sstevel@tonic-gate 			    "for connection on queue 0x%p\n", (void *)q);
33177c478bd9Sstevel@tonic-gate 			return (FALSE);
33187c478bd9Sstevel@tonic-gate 		}
33197c478bd9Sstevel@tonic-gate 		if (e->call_wq == q)
33207c478bd9Sstevel@tonic-gate 			break;
33217c478bd9Sstevel@tonic-gate 	}
33227c478bd9Sstevel@tonic-gate 
33237c478bd9Sstevel@tonic-gate 	switch (type) {
33247c478bd9Sstevel@tonic-gate 	case T_CONN_CON:
33257c478bd9Sstevel@tonic-gate 		/*
33267c478bd9Sstevel@tonic-gate 		 * The transport is now connected, send a T_INFO_REQ to get
33277c478bd9Sstevel@tonic-gate 		 * the tidu size.
33287c478bd9Sstevel@tonic-gate 		 */
33297c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_pending_lock);
33307c478bd9Sstevel@tonic-gate 		ASSERT(mp->b_datap->db_lim - mp->b_datap->db_base >=
33317c478bd9Sstevel@tonic-gate 		    sizeof (struct T_info_req));
33327c478bd9Sstevel@tonic-gate 		mp->b_rptr = mp->b_datap->db_base;
33337c478bd9Sstevel@tonic-gate 		((union T_primitives *)mp->b_rptr)->type = T_INFO_REQ;
33347c478bd9Sstevel@tonic-gate 		mp->b_wptr = mp->b_rptr + sizeof (struct T_info_req);
33357c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PCPROTO;
33367c478bd9Sstevel@tonic-gate 		put(q, mp);
33377c478bd9Sstevel@tonic-gate 		return (TRUE);
33387c478bd9Sstevel@tonic-gate 	case T_INFO_ACK:
33397c478bd9Sstevel@tonic-gate 	case T_OPTMGMT_ACK:
33407c478bd9Sstevel@tonic-gate 		e->call_status = RPC_SUCCESS;
33417c478bd9Sstevel@tonic-gate 		e->call_reply = mp;
33427c478bd9Sstevel@tonic-gate 		e->call_notified = TRUE;
33437c478bd9Sstevel@tonic-gate 		cv_signal(&e->call_cv);
33447c478bd9Sstevel@tonic-gate 		break;
33457c478bd9Sstevel@tonic-gate 	case T_ERROR_ACK:
33467c478bd9Sstevel@tonic-gate 		e->call_status = RPC_CANTCONNECT;
33477c478bd9Sstevel@tonic-gate 		e->call_reply = mp;
33487c478bd9Sstevel@tonic-gate 		e->call_notified = TRUE;
33497c478bd9Sstevel@tonic-gate 		cv_signal(&e->call_cv);
33507c478bd9Sstevel@tonic-gate 		break;
33517c478bd9Sstevel@tonic-gate 	case T_OK_ACK:
33527c478bd9Sstevel@tonic-gate 		/*
33537c478bd9Sstevel@tonic-gate 		 * Great, but we are really waiting for a T_CONN_CON
33547c478bd9Sstevel@tonic-gate 		 */
33557c478bd9Sstevel@tonic-gate 		freemsg(mp);
33567c478bd9Sstevel@tonic-gate 		break;
33577c478bd9Sstevel@tonic-gate 	default:
33587c478bd9Sstevel@tonic-gate 		mutex_exit(&clnt_pending_lock);
33597c478bd9Sstevel@tonic-gate 		RPCLOG(1, "clnt_dispatch_notifyconn: bad type %d\n", type);
33607c478bd9Sstevel@tonic-gate 		return (FALSE);
33617c478bd9Sstevel@tonic-gate 	}
33627c478bd9Sstevel@tonic-gate 
33637c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
33647c478bd9Sstevel@tonic-gate 	return (TRUE);
33657c478bd9Sstevel@tonic-gate }
33667c478bd9Sstevel@tonic-gate 
33677c478bd9Sstevel@tonic-gate /*
33687c478bd9Sstevel@tonic-gate  * Called by rpcmod when the transport is (or should be) going away.  Informs
33697c478bd9Sstevel@tonic-gate  * all callers waiting for replies and marks the entry in the connection
33707c478bd9Sstevel@tonic-gate  * manager's list as unconnected, and either closing (close handshake in
33717c478bd9Sstevel@tonic-gate  * progress) or dead.
33727c478bd9Sstevel@tonic-gate  */
33737c478bd9Sstevel@tonic-gate void
clnt_dispatch_notifyall(queue_t * q,int32_t msg_type,int32_t reason)33747c478bd9Sstevel@tonic-gate clnt_dispatch_notifyall(queue_t *q, int32_t msg_type, int32_t reason)
33757c478bd9Sstevel@tonic-gate {
33767c478bd9Sstevel@tonic-gate 	calllist_t *e;
33777c478bd9Sstevel@tonic-gate 	call_table_t *ctp;
33787c478bd9Sstevel@tonic-gate 	struct cm_xprt *cm_entry;
33797c478bd9Sstevel@tonic-gate 	int have_connmgr_lock;
33807c478bd9Sstevel@tonic-gate 	int i;
33817c478bd9Sstevel@tonic-gate 
33827c478bd9Sstevel@tonic-gate 	ASSERT((q->q_flag & QREADR) == 0);
33837c478bd9Sstevel@tonic-gate 
33847c478bd9Sstevel@tonic-gate 	RPCLOG(1, "clnt_dispatch_notifyall on queue %p", (void *)q);
33857c478bd9Sstevel@tonic-gate 	RPCLOG(1, " received a notifcation prim type [%s]",
33867c478bd9Sstevel@tonic-gate 	    rpc_tpiprim2name(msg_type));
33877c478bd9Sstevel@tonic-gate 	RPCLOG(1, " and reason %d\n", reason);
33887c478bd9Sstevel@tonic-gate 
33897c478bd9Sstevel@tonic-gate 	/*
33907c478bd9Sstevel@tonic-gate 	 * Find the transport entry in the connection manager's list, close
33917c478bd9Sstevel@tonic-gate 	 * the transport and delete the entry.  In the case where rpcmod's
33927c478bd9Sstevel@tonic-gate 	 * idle timer goes off, it sends us a T_ORDREL_REQ, indicating we
33937c478bd9Sstevel@tonic-gate 	 * should gracefully close the connection.
33947c478bd9Sstevel@tonic-gate 	 */
33957c478bd9Sstevel@tonic-gate 	have_connmgr_lock = 1;
33967c478bd9Sstevel@tonic-gate 	mutex_enter(&connmgr_lock);
33977c478bd9Sstevel@tonic-gate 	for (cm_entry = cm_hd; cm_entry; cm_entry = cm_entry->x_next) {
33987c478bd9Sstevel@tonic-gate 		ASSERT(cm_entry != cm_entry->x_next);
33997c478bd9Sstevel@tonic-gate 		if (cm_entry->x_wq == q) {
34007c478bd9Sstevel@tonic-gate 			ASSERT(MUTEX_HELD(&connmgr_lock));
34017c478bd9Sstevel@tonic-gate 			ASSERT(have_connmgr_lock == 1);
34027c478bd9Sstevel@tonic-gate 			switch (msg_type) {
34037c478bd9Sstevel@tonic-gate 			case T_ORDREL_REQ:
34047c478bd9Sstevel@tonic-gate 
34057c478bd9Sstevel@tonic-gate 				if (cm_entry->x_dead) {
34067c478bd9Sstevel@tonic-gate 					RPCLOG(1, "idle timeout on dead "
34077c478bd9Sstevel@tonic-gate 					    "connection: %p\n",
34087c478bd9Sstevel@tonic-gate 					    (void *)cm_entry);
34097c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
34107c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
34117c478bd9Sstevel@tonic-gate 					break;
34127c478bd9Sstevel@tonic-gate 				}
34137c478bd9Sstevel@tonic-gate 
34147c478bd9Sstevel@tonic-gate 				/*
34157c478bd9Sstevel@tonic-gate 				 * Only mark the connection as dead if it is
34167c478bd9Sstevel@tonic-gate 				 * connected and idle.
34177c478bd9Sstevel@tonic-gate 				 * An unconnected connection has probably
34187c478bd9Sstevel@tonic-gate 				 * gone idle because the server is down,
34197c478bd9Sstevel@tonic-gate 				 * and when it comes back up there will be
34207c478bd9Sstevel@tonic-gate 				 * retries that need to use that connection.
34217c478bd9Sstevel@tonic-gate 				 */
34227c478bd9Sstevel@tonic-gate 				if (cm_entry->x_connected ||
34237c478bd9Sstevel@tonic-gate 				    cm_entry->x_doomed) {
34247c478bd9Sstevel@tonic-gate 					if (cm_entry->x_ordrel) {
34258ffff9fdSgt29601 						if (cm_entry->x_closing ==
34268ffff9fdSgt29601 						    TRUE) {
34277c478bd9Sstevel@tonic-gate 							/*
34288ffff9fdSgt29601 							 * The connection is
34298ffff9fdSgt29601 							 * obviously wedged due
34308ffff9fdSgt29601 							 * to a bug or problem
34318ffff9fdSgt29601 							 * with the transport.
34328ffff9fdSgt29601 							 * Mark it as dead.
34338ffff9fdSgt29601 							 * Otherwise we can
34348ffff9fdSgt29601 							 * leak connections.
34357c478bd9Sstevel@tonic-gate 							 */
34367c478bd9Sstevel@tonic-gate 							cm_entry->x_dead = TRUE;
34378ffff9fdSgt29601 							mutex_exit(
34388ffff9fdSgt29601 							    &connmgr_lock);
34397c478bd9Sstevel@tonic-gate 							have_connmgr_lock = 0;
34408ffff9fdSgt29601 							if (clnt_stop_idle !=
34418ffff9fdSgt29601 							    NULL)
34427c478bd9Sstevel@tonic-gate 							(*clnt_stop_idle)(q);
34437c478bd9Sstevel@tonic-gate 							break;
34447c478bd9Sstevel@tonic-gate 						}
34457c478bd9Sstevel@tonic-gate 						cm_entry->x_closing = TRUE;
34467c478bd9Sstevel@tonic-gate 						connmgr_sndrel(cm_entry);
34477c478bd9Sstevel@tonic-gate 						have_connmgr_lock = 0;
34487c478bd9Sstevel@tonic-gate 					} else {
34497c478bd9Sstevel@tonic-gate 						cm_entry->x_dead = TRUE;
34507c478bd9Sstevel@tonic-gate 						mutex_exit(&connmgr_lock);
34517c478bd9Sstevel@tonic-gate 						have_connmgr_lock = 0;
34527c478bd9Sstevel@tonic-gate 						if (clnt_stop_idle != NULL)
34537c478bd9Sstevel@tonic-gate 							(*clnt_stop_idle)(q);
34547c478bd9Sstevel@tonic-gate 					}
34557c478bd9Sstevel@tonic-gate 				} else {
34567c478bd9Sstevel@tonic-gate 					/*
34577c478bd9Sstevel@tonic-gate 					 * We don't mark the connection
34587c478bd9Sstevel@tonic-gate 					 * as dead, but we turn off the
34597c478bd9Sstevel@tonic-gate 					 * idle timer.
34607c478bd9Sstevel@tonic-gate 					 */
34617c478bd9Sstevel@tonic-gate 					mutex_exit(&connmgr_lock);
34627c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
34637c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
34647c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
34657c478bd9Sstevel@tonic-gate 					RPCLOG(1, "clnt_dispatch_notifyall:"
34667c478bd9Sstevel@tonic-gate 					    " ignoring timeout from rpcmod"
34677c478bd9Sstevel@tonic-gate 					    " (q %p) because we are not "
34687c478bd9Sstevel@tonic-gate 					    " connected\n", (void *)q);
34697c478bd9Sstevel@tonic-gate 				}
34707c478bd9Sstevel@tonic-gate 				break;
34717c478bd9Sstevel@tonic-gate 			case T_ORDREL_IND:
34727c478bd9Sstevel@tonic-gate 				/*
34737c478bd9Sstevel@tonic-gate 				 * If this entry is marked closing, then we are
34747c478bd9Sstevel@tonic-gate 				 * completing a close handshake, and the
34757c478bd9Sstevel@tonic-gate 				 * connection is dead.  Otherwise, the server is
34767c478bd9Sstevel@tonic-gate 				 * trying to close. Since the server will not
34777c478bd9Sstevel@tonic-gate 				 * be sending any more RPC replies, we abort
34787c478bd9Sstevel@tonic-gate 				 * the connection, including flushing
34797c478bd9Sstevel@tonic-gate 				 * any RPC requests that are in-transit.
348030f84f37SDai Ngo 				 * In either case, mark the entry as dead so
348130f84f37SDai Ngo 				 * that it can be closed by the connection
348230f84f37SDai Ngo 				 * manager's garbage collector.
34837c478bd9Sstevel@tonic-gate 				 */
34847c478bd9Sstevel@tonic-gate 				cm_entry->x_dead = TRUE;
348530f84f37SDai Ngo 				if (cm_entry->x_closing) {
34867c478bd9Sstevel@tonic-gate 					mutex_exit(&connmgr_lock);
34877c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
34887c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
34897c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
34907c478bd9Sstevel@tonic-gate 				} else {
34917c478bd9Sstevel@tonic-gate 					/*
34927c478bd9Sstevel@tonic-gate 					 * if we're getting a disconnect
34937c478bd9Sstevel@tonic-gate 					 * before we've finished our
34947c478bd9Sstevel@tonic-gate 					 * connect attempt, mark it for
34957c478bd9Sstevel@tonic-gate 					 * later processing
34967c478bd9Sstevel@tonic-gate 					 */
34977c478bd9Sstevel@tonic-gate 					if (cm_entry->x_thread)
34987c478bd9Sstevel@tonic-gate 						cm_entry->x_early_disc = TRUE;
34997c478bd9Sstevel@tonic-gate 					else
35007c478bd9Sstevel@tonic-gate 						cm_entry->x_connected = FALSE;
35017c478bd9Sstevel@tonic-gate 					cm_entry->x_waitdis = TRUE;
35027c478bd9Sstevel@tonic-gate 					connmgr_snddis(cm_entry);
35037c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
35047c478bd9Sstevel@tonic-gate 				}
35057c478bd9Sstevel@tonic-gate 				break;
35067c478bd9Sstevel@tonic-gate 
35077c478bd9Sstevel@tonic-gate 			case T_ERROR_ACK:
35087c478bd9Sstevel@tonic-gate 			case T_OK_ACK:
35097c478bd9Sstevel@tonic-gate 				cm_entry->x_waitdis = FALSE;
35107c478bd9Sstevel@tonic-gate 				cv_signal(&cm_entry->x_dis_cv);
35117c478bd9Sstevel@tonic-gate 				mutex_exit(&connmgr_lock);
35127c478bd9Sstevel@tonic-gate 				return;
35137c478bd9Sstevel@tonic-gate 
35147c478bd9Sstevel@tonic-gate 			case T_DISCON_REQ:
35157c478bd9Sstevel@tonic-gate 				if (cm_entry->x_thread)
35167c478bd9Sstevel@tonic-gate 					cm_entry->x_early_disc = TRUE;
35177c478bd9Sstevel@tonic-gate 				else
35187c478bd9Sstevel@tonic-gate 					cm_entry->x_connected = FALSE;
35197c478bd9Sstevel@tonic-gate 				cm_entry->x_waitdis = TRUE;
35207c478bd9Sstevel@tonic-gate 
35217c478bd9Sstevel@tonic-gate 				connmgr_snddis(cm_entry);
35227c478bd9Sstevel@tonic-gate 				have_connmgr_lock = 0;
35237c478bd9Sstevel@tonic-gate 				break;
35247c478bd9Sstevel@tonic-gate 
35257c478bd9Sstevel@tonic-gate 			case T_DISCON_IND:
35267c478bd9Sstevel@tonic-gate 			default:
35277c478bd9Sstevel@tonic-gate 				/*
35287c478bd9Sstevel@tonic-gate 				 * if we're getting a disconnect before
35297c478bd9Sstevel@tonic-gate 				 * we've finished our connect attempt,
35307c478bd9Sstevel@tonic-gate 				 * mark it for later processing
35317c478bd9Sstevel@tonic-gate 				 */
35327c478bd9Sstevel@tonic-gate 				if (cm_entry->x_closing) {
35337c478bd9Sstevel@tonic-gate 					cm_entry->x_dead = TRUE;
35347c478bd9Sstevel@tonic-gate 					mutex_exit(&connmgr_lock);
35357c478bd9Sstevel@tonic-gate 					have_connmgr_lock = 0;
35367c478bd9Sstevel@tonic-gate 					if (clnt_stop_idle != NULL)
35377c478bd9Sstevel@tonic-gate 						(*clnt_stop_idle)(q);
35387c478bd9Sstevel@tonic-gate 				} else {
35397c478bd9Sstevel@tonic-gate 					if (cm_entry->x_thread) {
35407c478bd9Sstevel@tonic-gate 						cm_entry->x_early_disc = TRUE;
35417c478bd9Sstevel@tonic-gate 					} else {
35427c478bd9Sstevel@tonic-gate 						cm_entry->x_dead = TRUE;
35437c478bd9Sstevel@tonic-gate 						cm_entry->x_connected = FALSE;
35447c478bd9Sstevel@tonic-gate 					}
35457c478bd9Sstevel@tonic-gate 				}
35467c478bd9Sstevel@tonic-gate 				break;
35477c478bd9Sstevel@tonic-gate 			}
35487c478bd9Sstevel@tonic-gate 			break;
35497c478bd9Sstevel@tonic-gate 		}
35507c478bd9Sstevel@tonic-gate 	}
35517c478bd9Sstevel@tonic-gate 
35527c478bd9Sstevel@tonic-gate 	if (have_connmgr_lock)
35537c478bd9Sstevel@tonic-gate 		mutex_exit(&connmgr_lock);
35547c478bd9Sstevel@tonic-gate 
35557c478bd9Sstevel@tonic-gate 	if (msg_type == T_ERROR_ACK || msg_type == T_OK_ACK) {
35567c478bd9Sstevel@tonic-gate 		RPCLOG(1, "clnt_dispatch_notifyall: (wq %p) could not find "
35577c478bd9Sstevel@tonic-gate 		    "connmgr entry for discon ack\n", (void *)q);
35587c478bd9Sstevel@tonic-gate 		return;
35597c478bd9Sstevel@tonic-gate 	}
35607c478bd9Sstevel@tonic-gate 
35617c478bd9Sstevel@tonic-gate 	/*
35627c478bd9Sstevel@tonic-gate 	 * Then kick all the clnt_pending calls out of their wait.  There
35637c478bd9Sstevel@tonic-gate 	 * should be no clnt_pending calls in the case of rpcmod's idle
35647c478bd9Sstevel@tonic-gate 	 * timer firing.
35657c478bd9Sstevel@tonic-gate 	 */
35667c478bd9Sstevel@tonic-gate 	for (i = 0; i < clnt_cots_hash_size; i++) {
35677c478bd9Sstevel@tonic-gate 		ctp = &cots_call_ht[i];
35687c478bd9Sstevel@tonic-gate 		mutex_enter(&ctp->ct_lock);
35697c478bd9Sstevel@tonic-gate 		for (e = ctp->ct_call_next;
35707c478bd9Sstevel@tonic-gate 		    e != (calllist_t *)ctp;
35717c478bd9Sstevel@tonic-gate 		    e = e->call_next) {
35727c478bd9Sstevel@tonic-gate 			if (e->call_wq == q && e->call_notified == FALSE) {
35737c478bd9Sstevel@tonic-gate 				RPCLOG(1,
35747c478bd9Sstevel@tonic-gate 				    "clnt_dispatch_notifyall for queue %p ",
35757c478bd9Sstevel@tonic-gate 				    (void *)q);
35767c478bd9Sstevel@tonic-gate 				RPCLOG(1, "aborting clnt_pending call %p\n",
35777c478bd9Sstevel@tonic-gate 				    (void *)e);
35787c478bd9Sstevel@tonic-gate 
35797c478bd9Sstevel@tonic-gate 				if (msg_type == T_DISCON_IND)
35807c478bd9Sstevel@tonic-gate 					e->call_reason = reason;
35817c478bd9Sstevel@tonic-gate 				e->call_notified = TRUE;
35827c478bd9Sstevel@tonic-gate 				e->call_status = RPC_XPRTFAILED;
35837c478bd9Sstevel@tonic-gate 				cv_signal(&e->call_cv);
35847c478bd9Sstevel@tonic-gate 			}
35857c478bd9Sstevel@tonic-gate 		}
35867c478bd9Sstevel@tonic-gate 		mutex_exit(&ctp->ct_lock);
35877c478bd9Sstevel@tonic-gate 	}
35887c478bd9Sstevel@tonic-gate 
35897c478bd9Sstevel@tonic-gate 	mutex_enter(&clnt_pending_lock);
35907c478bd9Sstevel@tonic-gate 	for (e = clnt_pending; e; e = e->call_next) {
35917c478bd9Sstevel@tonic-gate 		/*
35927c478bd9Sstevel@tonic-gate 		 * Only signal those RPC handles that haven't been
35937c478bd9Sstevel@tonic-gate 		 * signalled yet. Otherwise we can get a bogus call_reason.
35947c478bd9Sstevel@tonic-gate 		 * This can happen if thread A is making a call over a
35957c478bd9Sstevel@tonic-gate 		 * connection. If the server is killed, it will cause
35967c478bd9Sstevel@tonic-gate 		 * reset, and reason will default to EIO as a result of
35977c478bd9Sstevel@tonic-gate 		 * a T_ORDREL_IND. Thread B then attempts to recreate
35987c478bd9Sstevel@tonic-gate 		 * the connection but gets a T_DISCON_IND. If we set the
35997c478bd9Sstevel@tonic-gate 		 * call_reason code for all threads, then if thread A
36007c478bd9Sstevel@tonic-gate 		 * hasn't been dispatched yet, it will get the wrong
36017c478bd9Sstevel@tonic-gate 		 * reason. The bogus call_reason can make it harder to
36027c478bd9Sstevel@tonic-gate 		 * discriminate between calls that fail because the
36037c478bd9Sstevel@tonic-gate 		 * connection attempt failed versus those where the call
36047c478bd9Sstevel@tonic-gate 		 * may have been executed on the server.
36057c478bd9Sstevel@tonic-gate 		 */
36067c478bd9Sstevel@tonic-gate 		if (e->call_wq == q && e->call_notified == FALSE) {
36077c478bd9Sstevel@tonic-gate 			RPCLOG(1, "clnt_dispatch_notifyall for queue %p ",
36087c478bd9Sstevel@tonic-gate 			    (void *)q);
36097c478bd9Sstevel@tonic-gate 			RPCLOG(1, " aborting clnt_pending call %p\n",
36107c478bd9Sstevel@tonic-gate 			    (void *)e);
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 			if (msg_type == T_DISCON_IND)
36137c478bd9Sstevel@tonic-gate 				e->call_reason = reason;
36147c478bd9Sstevel@tonic-gate 			e->call_notified = TRUE;
36157c478bd9Sstevel@tonic-gate 			/*
361648bbca81SDaniel Hoffman 			 * Let the caller timeout, else it will retry
36177c478bd9Sstevel@tonic-gate 			 * immediately.
36187c478bd9Sstevel@tonic-gate 			 */
36197c478bd9Sstevel@tonic-gate 			e->call_status = RPC_XPRTFAILED;
36207c478bd9Sstevel@tonic-gate 
36217c478bd9Sstevel@tonic-gate 			/*
36227c478bd9Sstevel@tonic-gate 			 * We used to just signal those threads
36237c478bd9Sstevel@tonic-gate 			 * waiting for a connection, (call_xid = 0).
36247c478bd9Sstevel@tonic-gate 			 * That meant that threads waiting for a response
36257c478bd9Sstevel@tonic-gate 			 * waited till their timeout expired. This
36267c478bd9Sstevel@tonic-gate 			 * could be a long time if they've specified a
36277c478bd9Sstevel@tonic-gate 			 * maximum timeout. (2^31 - 1). So we
36287c478bd9Sstevel@tonic-gate 			 * Signal all threads now.
36297c478bd9Sstevel@tonic-gate 			 */
36307c478bd9Sstevel@tonic-gate 			cv_signal(&e->call_cv);
36317c478bd9Sstevel@tonic-gate 		}
36327c478bd9Sstevel@tonic-gate 	}
36337c478bd9Sstevel@tonic-gate 	mutex_exit(&clnt_pending_lock);
36347c478bd9Sstevel@tonic-gate }
36357c478bd9Sstevel@tonic-gate 
36367c478bd9Sstevel@tonic-gate 
36377c478bd9Sstevel@tonic-gate /*ARGSUSED*/
36387c478bd9Sstevel@tonic-gate /*
36397c478bd9Sstevel@tonic-gate  * after resuming a system that's been suspended for longer than the
36407c478bd9Sstevel@tonic-gate  * NFS server's idle timeout (svc_idle_timeout for Solaris 2), rfscall()
36417c478bd9Sstevel@tonic-gate  * generates "NFS server X not responding" and "NFS server X ok" messages;
36427c478bd9Sstevel@tonic-gate  * here we reset inet connections to cause a re-connect and avoid those
36437c478bd9Sstevel@tonic-gate  * NFS messages.  see 4045054
36447c478bd9Sstevel@tonic-gate  */
36457c478bd9Sstevel@tonic-gate boolean_t
connmgr_cpr_reset(void * arg,int code)36467c478bd9Sstevel@tonic-gate connmgr_cpr_reset(void *arg, int code)
36477c478bd9Sstevel@tonic-gate {
36487c478bd9Sstevel@tonic-gate 	struct cm_xprt *cxp;
36497c478bd9Sstevel@tonic-gate 
36507c478bd9Sstevel@tonic-gate 	if (code == CB_CODE_CPR_CHKPT)
36517c478bd9Sstevel@tonic-gate 		return (B_TRUE);
36527c478bd9Sstevel@tonic-gate 
36537c478bd9Sstevel@tonic-gate 	if (mutex_tryenter(&connmgr_lock) == 0)
36547c478bd9Sstevel@tonic-gate 		return (B_FALSE);
36557c478bd9Sstevel@tonic-gate 	for (cxp = cm_hd; cxp; cxp = cxp->x_next) {
36567c478bd9Sstevel@tonic-gate 		if ((cxp->x_family == AF_INET || cxp->x_family == AF_INET6) &&
36577c478bd9Sstevel@tonic-gate 		    cxp->x_connected == TRUE) {
36587c478bd9Sstevel@tonic-gate 			if (cxp->x_thread)
36597c478bd9Sstevel@tonic-gate 				cxp->x_early_disc = TRUE;
36607c478bd9Sstevel@tonic-gate 			else
36617c478bd9Sstevel@tonic-gate 				cxp->x_connected = FALSE;
36627c478bd9Sstevel@tonic-gate 			cxp->x_needdis = TRUE;
36637c478bd9Sstevel@tonic-gate 		}
36647c478bd9Sstevel@tonic-gate 	}
36657c478bd9Sstevel@tonic-gate 	mutex_exit(&connmgr_lock);
36667c478bd9Sstevel@tonic-gate 	return (B_TRUE);
36677c478bd9Sstevel@tonic-gate }
36687c478bd9Sstevel@tonic-gate 
36697c478bd9Sstevel@tonic-gate void
clnt_cots_stats_init(zoneid_t zoneid,struct rpc_cots_client ** statsp)36707c478bd9Sstevel@tonic-gate clnt_cots_stats_init(zoneid_t zoneid, struct rpc_cots_client **statsp)
36717c478bd9Sstevel@tonic-gate {
36727c478bd9Sstevel@tonic-gate 
36737c478bd9Sstevel@tonic-gate 	*statsp = (struct rpc_cots_client *)rpcstat_zone_init_common(zoneid,
36747c478bd9Sstevel@tonic-gate 	    "unix", "rpc_cots_client", (const kstat_named_t *)&cots_rcstat_tmpl,
36757c478bd9Sstevel@tonic-gate 	    sizeof (cots_rcstat_tmpl));
36767c478bd9Sstevel@tonic-gate }
36777c478bd9Sstevel@tonic-gate 
36787c478bd9Sstevel@tonic-gate void
clnt_cots_stats_fini(zoneid_t zoneid,struct rpc_cots_client ** statsp)36797c478bd9Sstevel@tonic-gate clnt_cots_stats_fini(zoneid_t zoneid, struct rpc_cots_client **statsp)
36807c478bd9Sstevel@tonic-gate {
36817c478bd9Sstevel@tonic-gate 	rpcstat_zone_fini_common(zoneid, "unix", "rpc_cots_client");
36827c478bd9Sstevel@tonic-gate 	kmem_free(*statsp, sizeof (cots_rcstat_tmpl));
36837c478bd9Sstevel@tonic-gate }
36847c478bd9Sstevel@tonic-gate 
36857c478bd9Sstevel@tonic-gate void
clnt_cots_init(void)36867c478bd9Sstevel@tonic-gate clnt_cots_init(void)
36877c478bd9Sstevel@tonic-gate {
36887c478bd9Sstevel@tonic-gate 	mutex_init(&connmgr_lock, NULL, MUTEX_DEFAULT, NULL);
36897c478bd9Sstevel@tonic-gate 	mutex_init(&clnt_pending_lock, NULL, MUTEX_DEFAULT, NULL);
36907c478bd9Sstevel@tonic-gate 
36917c478bd9Sstevel@tonic-gate 	if (clnt_cots_hash_size < DEFAULT_MIN_HASH_SIZE)
36927c478bd9Sstevel@tonic-gate 		clnt_cots_hash_size = DEFAULT_MIN_HASH_SIZE;
36937c478bd9Sstevel@tonic-gate 
36947c478bd9Sstevel@tonic-gate 	cots_call_ht = call_table_init(clnt_cots_hash_size);
36957c478bd9Sstevel@tonic-gate 	zone_key_create(&zone_cots_key, NULL, NULL, clnt_zone_destroy);
36967c478bd9Sstevel@tonic-gate }
36977c478bd9Sstevel@tonic-gate 
36987c478bd9Sstevel@tonic-gate void
clnt_cots_fini(void)36997c478bd9Sstevel@tonic-gate clnt_cots_fini(void)
37007c478bd9Sstevel@tonic-gate {
37017c478bd9Sstevel@tonic-gate 	(void) zone_key_delete(zone_cots_key);
37027c478bd9Sstevel@tonic-gate }
37037c478bd9Sstevel@tonic-gate 
37047c478bd9Sstevel@tonic-gate /*
37057c478bd9Sstevel@tonic-gate  * Wait for TPI ack, returns success only if expected ack is received
37067c478bd9Sstevel@tonic-gate  * within timeout period.
37077c478bd9Sstevel@tonic-gate  */
37087c478bd9Sstevel@tonic-gate 
37097c478bd9Sstevel@tonic-gate static int
waitforack(calllist_t * e,t_scalar_t ack_prim,const struct timeval * waitp,bool_t nosignal)37107c478bd9Sstevel@tonic-gate waitforack(calllist_t *e, t_scalar_t ack_prim, const struct timeval *waitp,
37117c478bd9Sstevel@tonic-gate     bool_t nosignal)
37127c478bd9Sstevel@tonic-gate {
37137c478bd9Sstevel@tonic-gate 	union T_primitives *tpr;
37147c478bd9Sstevel@tonic-gate 	clock_t timout;
37157c478bd9Sstevel@tonic-gate 	int cv_stat = 1;
37167c478bd9Sstevel@tonic-gate 
37177c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&clnt_pending_lock));
37187c478bd9Sstevel@tonic-gate 	while (e->call_reply == NULL) {
37197c478bd9Sstevel@tonic-gate 		if (waitp != NULL) {
37207c478bd9Sstevel@tonic-gate 			timout = waitp->tv_sec * drv_usectohz(MICROSEC) +
3721d3d50737SRafael Vanoni 			    drv_usectohz(waitp->tv_usec);
37227c478bd9Sstevel@tonic-gate 			if (nosignal)
3723d3d50737SRafael Vanoni 				cv_stat = cv_reltimedwait(&e->call_cv,
3724d3d50737SRafael Vanoni 				    &clnt_pending_lock, timout, TR_CLOCK_TICK);
37257c478bd9Sstevel@tonic-gate 			else
3726d3d50737SRafael Vanoni 				cv_stat = cv_reltimedwait_sig(&e->call_cv,
3727d3d50737SRafael Vanoni 				    &clnt_pending_lock, timout, TR_CLOCK_TICK);
37287c478bd9Sstevel@tonic-gate 		} else {
37297c478bd9Sstevel@tonic-gate 			if (nosignal)
37307c478bd9Sstevel@tonic-gate 				cv_wait(&e->call_cv, &clnt_pending_lock);
37317c478bd9Sstevel@tonic-gate 			else
37327c478bd9Sstevel@tonic-gate 				cv_stat = cv_wait_sig(&e->call_cv,
37337c478bd9Sstevel@tonic-gate 				    &clnt_pending_lock);
37347c478bd9Sstevel@tonic-gate 		}
37357c478bd9Sstevel@tonic-gate 		if (cv_stat == -1)
37367c478bd9Sstevel@tonic-gate 			return (ETIME);
37377c478bd9Sstevel@tonic-gate 		if (cv_stat == 0)
37387c478bd9Sstevel@tonic-gate 			return (EINTR);
37396ec2b7e3Sgt29601 		/*
37406ec2b7e3Sgt29601 		 * if we received an error from the server and we know a reply
37416ec2b7e3Sgt29601 		 * is not going to be sent, do not wait for the full timeout,
37426ec2b7e3Sgt29601 		 * return now.
37436ec2b7e3Sgt29601 		 */
37446ec2b7e3Sgt29601 		if (e->call_status == RPC_XPRTFAILED)
37456ec2b7e3Sgt29601 			return (e->call_reason);
37467c478bd9Sstevel@tonic-gate 	}
37477c478bd9Sstevel@tonic-gate 	tpr = (union T_primitives *)e->call_reply->b_rptr;
37487c478bd9Sstevel@tonic-gate 	if (tpr->type == ack_prim)
37497c478bd9Sstevel@tonic-gate 		return (0); /* Success */
37507c478bd9Sstevel@tonic-gate 
37517c478bd9Sstevel@tonic-gate 	if (tpr->type == T_ERROR_ACK) {
37527c478bd9Sstevel@tonic-gate 		if (tpr->error_ack.TLI_error == TSYSERR)
37537c478bd9Sstevel@tonic-gate 			return (tpr->error_ack.UNIX_error);
37547c478bd9Sstevel@tonic-gate 		else
37557c478bd9Sstevel@tonic-gate 			return (t_tlitosyserr(tpr->error_ack.TLI_error));
37567c478bd9Sstevel@tonic-gate 	}
37577c478bd9Sstevel@tonic-gate 
37587c478bd9Sstevel@tonic-gate 	return (EPROTO); /* unknown or unexpected primitive */
37597c478bd9Sstevel@tonic-gate }
3760