rem: } else {
add: }
context: clnt_cots_kcallit(CLIENT *h, rpcproc_t procnum, xdrproc_t xdr_args,    caddr_t argsp, xdrproc_t xdr_results, caddr_t resultsp, struct timeval wait){	/* LINTED pointer alignment */	cku_private_t *p = htop(h);	calllist_t *call = &p->cku_call;	XDR *xdrs;	struct rpc_msg reply_msg;	mblk_t *mp;#ifdef	RPCDEBUG	clock_t time_sent;#endif	struct netbuf *retryaddr;	struct cm_xprt *cm_entry = NULL;	queue_t *wq;	int len;	int mpsize;	int refreshes = REFRESHES;	int interrupted;	int tidu_size;	enum clnt_stat status;	struct timeval cwait;	bool_t delay_first = FALSE;	clock_t ticks;	RPCLOG(2, "clnt_cots_kcallit, procnum %u\n", procnum);	COTSRCSTAT_INCR(p->cku_stats, rccalls);	RPCLOG(2, "clnt_cots_kcallit: wait.tv_sec: %ld\n", wait.tv_sec);	RPCLOG(2, "clnt_cots_kcallit: wait.tv_usec: %ld\n", wait.tv_usec);	/*	 * Bug ID 1240234:	 * Look out for zero length timeouts. We don't want to	 * wait zero seconds for a connection to be established.	 */	if (wait.tv_sec < clnt_cots_min_conntout) {		cwait.tv_sec = clnt_cots_min_conntout;		cwait.tv_usec = 0;		RPCLOG(8, "clnt_cots_kcallit: wait.tv_sec (%ld) too low,",		    wait.tv_sec);		RPCLOG(8, " setting to: %d\n", clnt_cots_min_conntout);	} else {		cwait = wait;	}call_again:	if (cm_entry) {		connmgr_release(cm_entry);		cm_entry = NULL;	}	mp = NULL;	/*	 * If the call is not a retry, allocate a new xid and cache it	 * for future retries.	 * Bug ID 1246045:	 * Treat call as a retry for purposes of binding the source	 * port only if we actually attempted to send anything on	 * the previous call.	 */	if (p->cku_xid == 0) {		p->cku_xid = alloc_xid();		/*		 * We need to ASSERT here that our xid != 0 because this		 * determines whether or not our call record gets placed on		 * the hash table or the linked list.  By design, we mandate		 * that RPC calls over cots must have xid's != 0, so we can		 * ensure proper management of the hash table.		 */		ASSERT(p->cku_xid != 0);		retryaddr = NULL;		p->cku_flags &= ~CKU_SENT;		if (p->cku_flags & CKU_ONQUEUE) {			RPCLOG(8, "clnt_cots_kcallit: new call, dequeuing old"			    " one (%p)\n", (void *)call);			call_table_remove(call);			p->cku_flags &= ~CKU_ONQUEUE;			RPCLOG(64, "clnt_cots_kcallit: removing call from "			    "dispatch list because xid was zero (now 0x%x)\n",			    p->cku_xid);		}		if (call->call_reply != NULL) {			freemsg(call->call_reply);			call->call_reply = NULL;		}	} else if (p->cku_srcaddr.buf == NULL || p->cku_srcaddr.len == 0) {		retryaddr = NULL;	} else if (p->cku_flags & CKU_SENT) {		retryaddr = &p->cku_srcaddr;	} else {		/*		 * Bug ID 1246045: Nothing was sent, so set retryaddr to		 * NULL and let connmgr_get() bind to any source port it		 * can get.		 */		retryaddr = NULL;	}	RPCLOG(64, "clnt_cots_kcallit: xid = 0x%x", p->cku_xid);	RPCLOG(64, " flags = 0x%x\n", p->cku_flags);	p->cku_err.re_status = RPC_TIMEDOUT;	p->cku_err.re_errno = p->cku_err.re_terrno = 0;	cm_entry = connmgr_wrapget(retryaddr, &cwait, p);	if (cm_entry == NULL) {		RPCLOG(1, "clnt_cots_kcallit: can't connect status %s\n",		    clnt_sperrno(p->cku_err.re_status));		/*		 * The reasons why we fail to create a connection are		 * varied. In most cases we don't want the caller to		 * immediately retry. This could have one or more		 * bad effects. This includes flooding the net with		 * connect requests to ports with no listener; a hard		 * kernel loop due to all the "reserved" TCP ports being		 * in use.		 */		delay_first = TRUE;		/*		 * Even if we end up returning EINTR, we still count a		 * a "can't connect", because the connection manager		 * might have been committed to waiting for or timing out on		 * a connection.		 */		COTSRCSTAT_INCR(p->cku_stats, rccantconn);		switch (p->cku_err.re_status) {		case RPC_INTR:			p->cku_err.re_errno = EINTR;			/*			 * No need to delay because a UNIX signal(2)			 * interrupted us. The caller likely won't			 * retry the CLNT_CALL() and even if it does,			 * we assume the caller knows what it is doing.			 */			delay_first = FALSE;			break;		case RPC_TIMEDOUT:			p->cku_err.re_errno = ETIMEDOUT;			/*			 * No need to delay because timed out already			 * on the connection request and assume that the			 * transport time out is longer than our minimum			 * timeout, or least not too much smaller.			 */			delay_first = FALSE;			break;		case RPC_SYSTEMERROR:		case RPC_TLIERROR:			/*			 * We want to delay here because a transient			 * system error has a better chance of going away			 * if we delay a bit. If it's not transient, then			 * we don't want end up in a hard kernel loop			 * due to retries.			 */			ASSERT(p->cku_err.re_errno != 0);			break;		case RPC_CANTCONNECT:			/*			 * RPC_CANTCONNECT is set on T_ERROR_ACK which			 * implies some error down in the TCP layer or			 * below. If cku_nodelayonerror is set then we			 * assume the caller knows not to try too hard.			 */			RPCLOG0(8, "clnt_cots_kcallit: connection failed,");			RPCLOG0(8, " re_status=RPC_CANTCONNECT,");			RPCLOG(8, " re_errno=%d,", p->cku_err.re_errno);			RPCLOG(8, " cku_nodelayonerr=%d", p->cku_nodelayonerr);			if (p->cku_nodelayonerr == TRUE)				delay_first = FALSE;			p->cku_err.re_errno = EIO;			break;		case RPC_XPRTFAILED:			/*			 * We want to delay here because we likely			 * got a refused connection.			 */			if (p->cku_err.re_errno != 0)				break;			/* fall thru */		default:			/*			 * We delay here because it is better to err			 * on the side of caution. If we got here then			 * status could have been RPC_SUCCESS, but we			 * know that we did not get a connection, so			 * force the rpc status to RPC_CANTCONNECT.			 */			p->cku_err.re_status = RPC_CANTCONNECT;			p->cku_err.re_errno = EIO;			break;		}		if (delay_first == TRUE)			ticks = clnt_cots_min_tout * drv_usectohz(1000000);		goto cots_done;	}	/*	 * If we've never sent any request on this connection (send count	 * is zero, or the connection has been reset), cache the	 * the connection's create time and send a request (possibly a retry)	 */	if ((p->cku_flags & CKU_SENT) == 0 ||	    p->cku_ctime != cm_entry->x_ctime) {		p->cku_ctime = cm_entry->x_ctime;	} else if ((p->cku_flags & CKU_SENT) && (p->cku_flags & CKU_ONQUEUE) &&	    (call->call_reply != NULL ||	    p->cku_recv_attempts < clnt_cots_maxrecv)) {		/*		 * If we've sent a request and our call is on the dispatch		 * queue and we haven't made too many receive attempts, then		 * don't re-send, just receive.		 */		p->cku_recv_attempts++;		goto read_again;	}	/*	 * Now we create the RPC request in a STREAMS message.  We have to do	 * this after the call to connmgr_get so that we have the correct	 * TIDU size for the transport.	 */	tidu_size = cm_entry->x_tidu_size;	len = MSG_OFFSET + MAX(tidu_size, RM_HDR_SIZE + WIRE_HDR_SIZE);	while ((mp = allocb(len, BPRI_MED)) == NULL) {		if (strwaitbuf(len, BPRI_MED)) {			p->cku_err.re_status = RPC_SYSTEMERROR;			p->cku_err.re_errno = ENOSR;			COTSRCSTAT_INCR(p->cku_stats, rcnomem);			goto cots_done;		}	}	xdrs = &p->cku_outxdr;	xdrmblk_init(xdrs, mp, XDR_ENCODE, tidu_size);	mpsize = MBLKSIZE(mp);	ASSERT(mpsize >= len);	ASSERT(mp->b_rptr == mp->b_datap->db_base);	/*	 * If the size of mblk is not appreciably larger than what we	 * asked, then resize the mblk to exactly len bytes. The reason for	 * this: suppose len is 1600 bytes, the tidu is 1460 bytes	 * (from TCP over ethernet), and the arguments to the RPC require	 * 2800 bytes. Ideally we want the protocol to render two	 * ~1400 byte segments over the wire. However if allocb() gives us a 2k	 * mblk, and we allocate a second mblk for the remainder, the protocol	 * module may generate 3 segments over the wire:	 * 1460 bytes for the first, 448 (2048 - 1600) for the second, and	 * 892 for the third. If we "waste" 448 bytes in the first mblk,	 * the XDR encoding will generate two ~1400 byte mblks, and the	 * protocol module is more likely to produce properly sized segments.	 */	if ((mpsize >> 1) <= len)		mp->b_rptr += (mpsize - len);	/*	 * Adjust b_rptr to reserve space for the non-data protocol headers	 * any downstream modules might like to add, and for the	 * record marking header.	 */	mp->b_rptr += (MSG_OFFSET + RM_HDR_SIZE);	if (h->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {		/* Copy in the preserialized RPC header information. */		bcopy(p->cku_rpchdr, mp->b_rptr, WIRE_HDR_SIZE);		/* Use XDR_SETPOS() to set the b_wptr to past the RPC header. */		XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base +		    WIRE_HDR_SIZE));		ASSERT((mp->b_wptr - mp->b_rptr) == WIRE_HDR_SIZE);		/* Serialize the procedure number and the arguments. */		if ((!XDR_PUTINT32(xdrs, (int32_t *)&procnum)) ||		    (!AUTH_MARSHALL(h->cl_auth, xdrs, p->cku_cred)) ||		    (!(*xdr_args)(xdrs, argsp))) {			p->cku_err.re_status = RPC_CANTENCODEARGS;			p->cku_err.re_errno = EIO;			goto cots_done;		}		(*(uint32_t *)(mp->b_rptr)) = p->cku_xid;	} else {		uint32_t *uproc = (uint32_t *)&p->cku_rpchdr[WIRE_HDR_SIZE];		IXDR_PUT_U_INT32(uproc, procnum);		(*(uint32_t *)(&p->cku_rpchdr[0])) = p->cku_xid;		/* Use XDR_SETPOS() to set the b_wptr. */		XDR_SETPOS(xdrs, (uint_t)(mp->b_rptr - mp->b_datap->db_base));		/* Serialize the procedure number and the arguments. */		if (!AUTH_WRAP(h->cl_auth, p->cku_rpchdr, WIRE_HDR_SIZE+4,		    xdrs, xdr_args, argsp)) {			p->cku_err.re_status = RPC_CANTENCODEARGS;			p->cku_err.re_errno = EIO;			goto cots_done;		}	}	RPCLOG(2, "clnt_cots_kcallit: connected, sending call, tidu_size %d\n",	    tidu_size);	wq = cm_entry->x_wq;	clnt_dispatch_send(wq, mp, call, p->cku_xid,				(p->cku_flags & CKU_ONQUEUE));	RPCLOG(64, "clnt_cots_kcallit: sent call for xid 0x%x\n",		(uint_t)p->cku_xid);	p->cku_flags = (CKU_ONQUEUE|CKU_SENT);	p->cku_recv_attempts = 1;#ifdef	RPCDEBUG	time_sent = lbolt;#endif	/*	 * Wait for a reply or a timeout.  If there is no error or timeout,	 * (both indicated by call_status), call->call_reply will contain	 * the RPC reply message.	 */read_again:	mutex_enter(&call->call_lock);	interrupted = 0;	if (call->call_status == RPC_TIMEDOUT) {		/*		 * Indicate that the lwp is not to be stopped while waiting		 * for this network traffic.  This is to avoid deadlock while		 * debugging a process via /proc and also to avoid recursive		 * mutex_enter()s due to NFS page faults while stopping		 * (NFS holds locks when it calls here).		 */		clock_t cv_wait_ret;		clock_t timout;		clock_t oldlbolt;		klwp_t *lwp = ttolwp(curthread);		if (lwp != NULL)			lwp->lwp_nostop++;		oldlbolt = lbolt;		timout = wait.tv_sec * drv_usectohz(1000000) +		    drv_usectohz(wait.tv_usec) + oldlbolt;		/*		 * Iterate until the call_status is changed to something		 * other that RPC_TIMEDOUT, or if cv_timedwait_sig() returns		 * something <=0 zero. The latter means that we timed		 * out.		 */		if (h->cl_nosignal)			while ((cv_wait_ret = cv_timedwait(&call->call_cv,			    &call->call_lock, timout)) > 0 &&			    call->call_status == RPC_TIMEDOUT);		else			while ((cv_wait_ret = cv_timedwait_sig(			    &call->call_cv,			    &call->call_lock, timout)) > 0 &&			    call->call_status == RPC_TIMEDOUT);		switch (cv_wait_ret) {		case 0:			/*			 * If we got out of the above loop with			 * cv_timedwait_sig() returning 0, then we were			 * interrupted regardless what call_status is.			 */			interrupted = 1;			break;		case -1:			/* cv_timedwait_sig() timed out */			break;		default:			/*			 * We were cv_signaled(). If we didn't			 * get a successful call_status and returned			 * before time expired, delay up to clnt_cots_min_tout			 * seconds so that the caller doesn't immediately			 * try to call us again and thus force the			 * same condition that got us here (such			 * as a RPC_XPRTFAILED due to the server not			 * listening on the end-point.			 */			if (call->call_status != RPC_SUCCESS) {				clock_t curlbolt;				clock_t diff;				curlbolt = ddi_get_lbolt();				ticks = clnt_cots_min_tout *				    drv_usectohz(1000000);				diff = curlbolt - oldlbolt;				if (diff < ticks) {					delay_first = TRUE;					if (diff > 0)						ticks -= diff;				}			}			break;		}		if (lwp != NULL)			lwp->lwp_nostop--;	}	/*	 * Get the reply message, if any.  This will be freed at the end	 * whether or not an error occurred.	 */	mp = call->call_reply;	call->call_reply = NULL;	/*	 * call_err is the error info when the call is on dispatch queue.	 * cku_err is the error info returned to the caller.	 * Sync cku_err with call_err for local message processing.	 */	status = call->call_status;	p->cku_err = call->call_err;	mutex_exit(&call->call_lock);	if (status != RPC_SUCCESS) {		switch (status) {		case RPC_TIMEDOUT:			if (interrupted) {				COTSRCSTAT_INCR(p->cku_stats, rcintrs);				p->cku_err.re_status = RPC_INTR;				p->cku_err.re_errno = EINTR;				RPCLOG(1, "clnt_cots_kcallit: xid 0x%x",				    p->cku_xid);				RPCLOG(1, "signal interrupted at %ld", lbolt);				RPCLOG(1, ", was sent at %ld\n", time_sent);			} else {				COTSRCSTAT_INCR(p->cku_stats, rctimeouts);				p->cku_err.re_errno = ETIMEDOUT;				RPCLOG(1, "clnt_cots_kcallit: timed out at %ld",				    lbolt);				RPCLOG(1, ", was sent at %ld\n", time_sent);			}			break;		case RPC_XPRTFAILED:			if (p->cku_err.re_errno == 0)				p->cku_err.re_errno = EIO;			RPCLOG(1, "clnt_cots_kcallit: transport failed: %d\n",			    p->cku_err.re_errno);			break;		case RPC_SYSTEMERROR:			ASSERT(p->cku_err.re_errno);			RPCLOG(1, "clnt_cots_kcallit: system error: %d\n",			    p->cku_err.re_errno);			break;		default:			p->cku_err.re_status = RPC_SYSTEMERROR;			p->cku_err.re_errno = EIO;			RPCLOG(1, "clnt_cots_kcallit: error: %s\n",			    clnt_sperrno(status));			break;		}		if (p->cku_err.re_status != RPC_TIMEDOUT) {			if (p->cku_flags & CKU_ONQUEUE) {				call_table_remove(call);				p->cku_flags &= ~CKU_ONQUEUE;			}			RPCLOG(64, "clnt_cots_kcallit: non TIMEOUT so xid 0x%x "			    "taken off dispatch list\n", p->cku_xid);			if (call->call_reply) {				freemsg(call->call_reply);				call->call_reply = NULL;			}		} else if (wait.tv_sec != 0) {			/*			 * We've sent the request over TCP and so we have			 * every reason to believe it will get			 * delivered. In which case returning a timeout is not			 * appropriate.			 */			if (p->cku_progress == TRUE &&			    p->cku_recv_attempts < clnt_cots_maxrecv) {				p->cku_err.re_status = RPC_INPROGRESS;			}		}		goto cots_done;	}	xdrs = &p->cku_inxdr;	xdrmblk_init(xdrs, mp, XDR_DECODE, 0);	reply_msg.rm_direction = REPLY;	reply_msg.rm_reply.rp_stat = MSG_ACCEPTED;	reply_msg.acpted_rply.ar_stat = SUCCESS;	reply_msg.acpted_rply.ar_verf = _null_auth;	/*	 *  xdr_results will be done in AUTH_UNWRAP.	 */	reply_msg.acpted_rply.ar_results.where = NULL;	reply_msg.acpted_rply.ar_results.proc = xdr_void;	if (xdr_replymsg(xdrs, &reply_msg)) {		enum clnt_stat re_status;		_seterr_reply(&reply_msg, &p->cku_err);		re_status = p->cku_err.re_status;		if (re_status == RPC_SUCCESS) {			/*			 * Reply is good, check auth.			 */			if (!AUTH_VALIDATE(h->cl_auth,					&reply_msg.acpted_rply.ar_verf)) {				COTSRCSTAT_INCR(p->cku_stats, rcbadverfs);				RPCLOG0(1, "clnt_cots_kcallit: validation "					"failure\n");				freemsg(mp);				(void) xdr_rpc_free_verifier(xdrs, &reply_msg);				mutex_enter(&call->call_lock);				if (call->call_reply == NULL)					call->call_status = RPC_TIMEDOUT;				mutex_exit(&call->call_lock);				goto read_again;			} else if (!AUTH_UNWRAP(h->cl_auth, xdrs,						xdr_results, resultsp)) {				RPCLOG0(1, "clnt_cots_kcallit: validation "					"failure (unwrap)\n");				p->cku_err.re_status = RPC_CANTDECODERES;				p->cku_err.re_errno = EIO;			}		} else {			/* set errno in case we can't recover */			if (re_status != RPC_VERSMISMATCH &&			    re_status != RPC_AUTHERROR &&			    re_status != RPC_PROGVERSMISMATCH)				p->cku_err.re_errno = EIO;			if (re_status == RPC_AUTHERROR) {				/*				 * Maybe our credential need to be				 * refreshed				 */				if (cm_entry) {					/*					 * There is the potential that the					 * cm_entry has/will be marked dead,					 * so drop our ref to it, force REFRESH					 * to establish new connection.					 */					connmgr_release(cm_entry);					cm_entry = NULL;				}				if ((refreshes > 0) &&				    AUTH_REFRESH(h->cl_auth, &reply_msg,						p->cku_cred)) {					refreshes--;					(void) xdr_rpc_free_verifier(xdrs,								&reply_msg);					freemsg(mp);					mp = NULL;					if (p->cku_flags & CKU_ONQUEUE) {						call_table_remove(call);						p->cku_flags &= ~CKU_ONQUEUE;					}					RPCLOG(64,					    "clnt_cots_kcallit: AUTH_ERROR, xid"					    " 0x%x removed off dispatch list\n",					    p->cku_xid);					if (call->call_reply) {						freemsg(call->call_reply);						call->call_reply = NULL;					}					COTSRCSTAT_INCR(p->cku_stats,							rcbadcalls);					COTSRCSTAT_INCR(p->cku_stats,							rcnewcreds);					goto call_again;				} else {				/*				 * We have used the client handle to				 * do an AUTH_REFRESH and the RPC status may				 * be set to RPC_SUCCESS; Let's make sure to				 * set it to RPC_AUTHERROR.				 */				p->cku_err.re_status = RPC_AUTHERROR;				/*				 * Map recoverable and unrecoverable				 * authentication errors to appropriate errno				 */				switch (p->cku_err.re_why) {				case AUTH_TOOWEAK:					/*					 * This could be a failure where the					 * server requires use of a reserved					 * port,  check and optionally set the					 * client handle useresvport trying					 * one more time. Next go round we					 * fall out with the tooweak error.					 */					if (p->cku_useresvport != 1) {						p->cku_useresvport = 1;						p->cku_xid = 0;						(void) xdr_rpc_free_verifier							    (xdrs, &reply_msg);						freemsg(mp);						/*						 * If we got this far the						 * connection we are using is						 * connected but not valid,						 * (not a resv port) drop it						 * and force a connection.						 */						if (cm_entry) {							connmgr_cancelconn								(cm_entry);							cm_entry = NULL;						}						goto call_again;					}					/* FALLTHRU */				case AUTH_BADCRED:				case AUTH_BADVERF:				case AUTH_INVALIDRESP:				case AUTH_FAILED:				case RPCSEC_GSS_NOCRED:				case RPCSEC_GSS_FAILED:						p->cku_err.re_errno = EACCES;						break;				case AUTH_REJECTEDCRED:				case AUTH_REJECTEDVERF:				default:	p->cku_err.re_errno = EIO;						break;				}				RPCLOG(1, "clnt_cots_kcallit : authentication"				    " failed with RPC_AUTHERROR of type %d\n",				    (int)p->cku_err.re_why);			    }			}		}	} else {		/* reply didn't decode properly. */		p->cku_err.re_status = RPC_CANTDECODERES;		p->cku_err.re_errno = EIO;		RPCLOG0(1, "clnt_cots_kcallit: decode failure\n");	}	(void) xdr_rpc_free_verifier(xdrs, &reply_msg);	if (p->cku_flags & CKU_ONQUEUE) {		call_table_remove(call);		p->cku_flags &= ~CKU_ONQUEUE;	}	RPCLOG(64, "clnt_cots_kcallit: xid 0x%x taken off dispatch list",	    p->cku_xid);	RPCLOG(64, " status is %s\n", clnt_sperrno(p->cku_err.re_status));cots_done:	if (cm_entry)		connmgr_release(cm_entry);	if (mp != NULL)		freemsg(mp);	if ((p->cku_flags & CKU_ONQUEUE) == 0 && call->call_reply) {		freemsg(call->call_reply);		call->call_reply = NULL;	}	if (p->cku_err.re_status != RPC_SUCCESS) {		RPCLOG0(1, "clnt_cots_kcallit: tail-end failure\n");		COTSRCSTAT_INCR(p->cku_stats, rcbadcalls);	}	/*	 * No point in delaying if the zone is going away.	 */	if (delay_first == TRUE &&	    !(zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)) {		if (clnt_delay(ticks, h->cl_nosignal) == EINTR) {			p->cku_err.re_errno = EINTR;			p->cku_err.re_status = RPC_INTR;		}	}	return (p->cku_err.re_status);}