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