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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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