3354 kernel crash in rpcsec_gss after using gsscred
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Carlos Neira <cneirabustos@gmail.com>
Approved by: Robert Mustacchi <rm@joyent.com>
diff --git a/usr/src/uts/common/rpc/rpcmod.c b/usr/src/uts/common/rpc/rpcmod.c
index c8e7220..a59f6e7 100644
--- a/usr/src/uts/common/rpc/rpcmod.c
+++ b/usr/src/uts/common/rpc/rpcmod.c
@@ -24,12 +24,14 @@
  */
 /*
  * Copyright 2012 Milan Jurik. All rights reserved.
+ * Copyright 2012 Marcel Telka <marcel@telka.sk>
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 /* Copyright (c) 1990 Mentat Inc. */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
-/*	  All Rights Reserved  	*/
+/*	All Rights Reserved	*/
 
 /*
  * Kernel RPC filtering module
@@ -570,7 +572,6 @@
 	return ((*((struct temp_slot *)q->q_ptr)->ops->xo_close)(q, flag, crp));
 }
 
-static void rpcmod_release(queue_t *, mblk_t *, bool_t);
 /*
  * rpcmodopen -	open routine gets called when the module gets pushed
  *		onto the stream.
@@ -581,19 +582,9 @@
 {
 	struct rpcm *rmp;
 
-	extern void (*rpc_rele)(queue_t *, mblk_t *, bool_t);
-
 	TRACE_0(TR_FAC_KRPC, TR_RPCMODOPEN_START, "rpcmodopen_start:");
 
 	/*
-	 * Initialize entry points to release a rpcmod slot (and an input
-	 * message if supplied) and to send an output message to the module
-	 * below rpcmod.
-	 */
-	if (rpc_rele == NULL)
-		rpc_rele = rpcmod_release;
-
-	/*
 	 * Only sufficiently privileged users can use this module, and it
 	 * is assumed that they will use this module properly, and NOT send
 	 * bulk data from downstream.
@@ -950,9 +941,20 @@
 	}
 }
 
-/* ARGSUSED */
-static void
-rpcmod_release(queue_t *q, mblk_t *bp, bool_t enable)
+void
+rpcmod_hold(queue_t *q)
+{
+	struct rpcm *rmp = (struct rpcm *)q->q_ptr;
+
+	mutex_enter(&rmp->rm_lock);
+	rmp->rm_ref++;
+	mutex_exit(&rmp->rm_lock);
+}
+
+void
+rpcmod_release(queue_t *q, mblk_t *bp,
+    /* LINTED E_FUNC_ARG_UNUSED */
+    bool_t enable __unused)
 {
 	struct rpcm *rmp;
 
@@ -1005,7 +1007,6 @@
 static int	mir_clnt_dup_request(queue_t *q, mblk_t *mp);
 static void	mir_rput_proto(queue_t *q, mblk_t *mp);
 static int	mir_svc_policy_notify(queue_t *q, int event);
-static void	mir_svc_release(queue_t *wq, mblk_t *mp, bool_t);
 static void	mir_svc_start(queue_t *wq);
 static void	mir_svc_idle_start(queue_t *, mir_t *);
 static void	mir_svc_idle_stop(queue_t *, mir_t *);
@@ -1020,7 +1021,6 @@
 static	int	mir_check_len(queue_t *, mblk_t *);
 static	void	mir_timer(void *);
 
-extern void	(*mir_rele)(queue_t *, mblk_t *, bool_t);
 extern void	(*mir_start)(queue_t *);
 extern void	(*clnt_stop_idle)(queue_t *);
 
@@ -1259,8 +1259,6 @@
 
 	RPCLOG(32, "rpcmod: mir_open of q 0x%p\n", (void *)q);
 	/* Set variables used directly by kRPC. */
-	if (!mir_rele)
-		mir_rele = mir_svc_release;
 	if (!mir_start)
 		mir_start = mir_svc_start;
 	if (!clnt_stop_idle)
@@ -2019,11 +2017,21 @@
 	qenable(wq);
 }
 
+void
+mir_svc_hold(queue_t *wq)
+{
+	mir_t *mir = (mir_t *)wq->q_ptr;
+
+	mutex_enter(&mir->mir_mutex);
+	mir->mir_ref_cnt++;
+	mutex_exit(&mir->mir_mutex);
+}
+
 /*
  * This routine is called directly by kRPC after a request is completed,
  * whether a reply was sent or the request was dropped.
  */
-static void
+void
 mir_svc_release(queue_t *wq, mblk_t *mp, bool_t enable)
 {
 	mir_t   *mir = (mir_t *)wq->q_ptr;
diff --git a/usr/src/uts/common/rpc/sec_gss/svc_rpcsec_gss.c b/usr/src/uts/common/rpc/sec_gss/svc_rpcsec_gss.c
index 23f98b9..aa0eb24 100644
--- a/usr/src/uts/common/rpc/sec_gss/svc_rpcsec_gss.c
+++ b/usr/src/uts/common/rpc/sec_gss/svc_rpcsec_gss.c
@@ -23,6 +23,8 @@
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Milan Jurik. All rights reserved.
+ * Copyright 2012 Marcel Telka <marcel@telka.sk>
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -374,10 +376,7 @@
  * Shift the array arr of length arrlen right by nbits bits.
  */
 static void
-shift_bits(arr, arrlen, nbits)
-	uint_t	*arr;
-	int	arrlen;
-	int	nbits;
+shift_bits(uint_t *arr, int arrlen, int nbits)
 {
 	int	i, j;
 	uint_t	lo, hi;
@@ -407,10 +406,7 @@
  * Check that the received sequence number seq_num is valid.
  */
 static bool_t
-check_seq(cl, seq_num, kill_context)
-	svc_rpc_gss_data	*cl;
-	uint_t			seq_num;
-	bool_t			*kill_context;
+check_seq(svc_rpc_gss_data *cl, uint_t seq_num, bool_t *kill_context)
 {
 	int			i, j;
 	uint_t			bit;
@@ -431,7 +427,7 @@
 	 */
 	if (seq_num > cl->seq_num) {
 		(void) shift_bits(cl->seq_bits, SEQ_ARR_SIZE,
-				(int)(seq_num - cl->seq_num));
+		    (int)(seq_num - cl->seq_num));
 		cl->seq_bits[0] |= SEQ_HI_BIT;
 		cl->seq_num = seq_num;
 		return (TRUE);
@@ -465,8 +461,7 @@
  * Set server callback.
  */
 bool_t
-rpc_gss_set_callback(cb)
-	rpc_gss_callback_t	*cb;
+rpc_gss_set_callback(rpc_gss_callback_t *cb)
 {
 	rpc_gss_cblist_t		*cbl, *tmp;
 
@@ -508,9 +503,7 @@
  * the incoming context.
  */
 static bool_t
-do_callback(req, client_data)
-	struct svc_req		*req;
-	svc_rpc_gss_data	*client_data;
+do_callback(struct svc_req *req, svc_rpc_gss_data *client_data)
 {
 	rpc_gss_cblist_t		*cbl;
 	bool_t			ret = TRUE, found = FALSE;
@@ -519,13 +512,13 @@
 	mutex_enter(&cb_mutex);
 	for (cbl = rpc_gss_cblist; cbl != NULL; cbl = cbl->next) {
 		if (req->rq_prog != cbl->cb.program ||
-					req->rq_vers != cbl->cb.version)
+		    req->rq_vers != cbl->cb.version)
 			continue;
 		found = TRUE;
 		lock.locked = FALSE;
 		lock.raw_cred = &client_data->raw_cred;
 		ret = (*cbl->cb.callback)(req, client_data->deleg,
-			client_data->context, &lock, &client_data->cookie);
+		    client_data->context, &lock, &client_data->cookie);
 		req->rq_xprt->xp_cookie = client_data->cookie;
 
 		if (ret) {
@@ -537,7 +530,7 @@
 	if (!found) {
 		if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
 			(void) kgss_release_cred(&minor, &client_data->deleg,
-					crgetuid(CRED()));
+			    crgetuid(CRED()));
 			client_data->deleg = GSS_C_NO_CREDENTIAL;
 		}
 	}
@@ -549,11 +542,8 @@
  * Get caller credentials.
  */
 bool_t
-rpc_gss_getcred(req, rcred, ucred, cookie)
-	struct svc_req		*req;
-	rpc_gss_rawcred_t	**rcred;
-	rpc_gss_ucred_t		**ucred;
-	void			**cookie;
+rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
+    rpc_gss_ucred_t **ucred, void **cookie)
 {
 	SVCAUTH			*svcauth;
 	svc_rpc_gss_data	*client_data;
@@ -573,38 +563,45 @@
 
 		if (client_data->u_cred_set == 0 ||
 		    client_data->u_cred_set < gethrestime_sec()) {
-		    if (client_data->u_cred_set == 0) {
-			if ((gssstat = kgsscred_expname_to_unix_cred(
-			    &client_data->client_name,
-			    &client_data->u_cred.uid,
-			    &client_data->u_cred.gid,
-			    &client_data->u_cred.gidlist,
-			    &gidlen, crgetuid(CRED()))) != GSS_S_COMPLETE) {
-				RPCGSS_LOG(1, "rpc_gss_getcred: "
-				    "kgsscred_expname_to_unix_cred failed %x\n",
-				    gssstat);
-				*ucred = NULL;
-			} else {
-				client_data->u_cred.gidlen = (short)gidlen;
-				client_data->u_cred_set =
-				    gethrestime_sec() + svc_rpcgss_gid_timeout;
+			if (client_data->u_cred_set == 0) {
+				if ((gssstat = kgsscred_expname_to_unix_cred(
+				    &client_data->client_name,
+				    &client_data->u_cred.uid,
+				    &client_data->u_cred.gid,
+				    &client_data->u_cred.gidlist,
+				    &gidlen, crgetuid(CRED())))
+				    != GSS_S_COMPLETE) {
+					RPCGSS_LOG(1, "rpc_gss_getcred: "
+					    "kgsscred_expname_to_unix_cred "
+					    "failed %x\n", gssstat);
+					*ucred = NULL;
+				} else {
+					client_data->u_cred.gidlen =
+					    (short)gidlen;
+					client_data->u_cred_set =
+					    gethrestime_sec() +
+					    svc_rpcgss_gid_timeout;
+				}
+			} else if (client_data->u_cred_set
+			    < gethrestime_sec()) {
+				if ((gssstat = kgss_get_group_info(
+				    client_data->u_cred.uid,
+				    &client_data->u_cred.gid,
+				    &client_data->u_cred.gidlist,
+				    &gidlen, crgetuid(CRED())))
+				    != GSS_S_COMPLETE) {
+					RPCGSS_LOG(1, "rpc_gss_getcred: "
+					    "kgss_get_group_info failed %x\n",
+					    gssstat);
+					*ucred = NULL;
+				} else {
+					client_data->u_cred.gidlen =
+					    (short)gidlen;
+					client_data->u_cred_set =
+					    gethrestime_sec() +
+					    svc_rpcgss_gid_timeout;
+				}
 			}
-		    } else if (client_data->u_cred_set < gethrestime_sec()) {
-			if ((gssstat = kgss_get_group_info(
-			    client_data->u_cred.uid,
-			    &client_data->u_cred.gid,
-			    &client_data->u_cred.gidlist,
-			    &gidlen, crgetuid(CRED()))) != GSS_S_COMPLETE) {
-				RPCGSS_LOG(1, "rpc_gss_getcred: "
-				    "kgss_get_group_info failed %x\n",
-				    gssstat);
-				*ucred = NULL;
-			} else {
-				client_data->u_cred.gidlen = (short)gidlen;
-				client_data->u_cred_set =
-				    gethrestime_sec() + svc_rpcgss_gid_timeout;
-			}
-		    }
 		}
 	}
 
@@ -694,7 +691,7 @@
 	gss_buffer_desc		output_token;
 	OM_uint32		gssstat, minor, minor_stat, time_rec;
 	int			ret_flags, ret;
-	gss_OID 		mech_type = GSS_C_NULL_OID;
+	gss_OID			mech_type = GSS_C_NULL_OID;
 	int			free_mech_type = 1;
 	struct svc_req		r, *rqst;
 
@@ -909,6 +906,7 @@
 		    retval);
 	}
 	rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
+	SVC_RELE(arg->rq_xprt, NULL, FALSE);
 	svc_clone_unlink(arg->rq_xprt);
 	svc_clone_free(arg->rq_xprt);
 	xdr_free(__xdr_rpc_gss_init_arg, (caddr_t)arg->rpc_call_arg);
@@ -974,6 +972,11 @@
 	svc_clone_link(rqst->rq_xprt->xp_master, arg->rq_xprt, rqst->rq_xprt);
 	arg->rq_xprt->xp_xid = rqst->rq_xprt->xp_xid;
 
+	/*
+	 * Increment the reference count on the rpcmod slot so that is not
+	 * freed before the task has finished.
+	 */
+	SVC_HOLD(arg->rq_xprt);
 
 	/* set the appropriate wrap/unwrap routine for RPCSEC_GSS */
 	arg->rq_xprt->xp_auth.svc_ah_ops = svc_rpc_gss_ops;
@@ -993,6 +996,7 @@
 		cmn_err(CE_NOTE, "rpcsec_gss_init: taskq dispatch fail");
 		ret = RPCSEC_GSS_FAILED;
 		rpc_msg_free(&arg->msg, MAX_AUTH_BYTES);
+		SVC_RELE(arg->rq_xprt, NULL, FALSE);
 		svc_clone_unlink(arg->rq_xprt);
 		svc_clone_free(arg->rq_xprt);
 		kmem_free(arg, sizeof (*arg));
@@ -1473,11 +1477,8 @@
  * (e.g. sequence number or sequence window)
  */
 static bool_t
-set_response_verf(rqst, msg, cl, num)
-	struct svc_req		*rqst;
-	struct rpc_msg		*msg;
-	svc_rpc_gss_data	*cl;
-	uint_t			num;
+set_response_verf(struct svc_req *rqst, struct rpc_msg *msg,
+    svc_rpc_gss_data *cl, uint_t num)
 {
 	OM_uint32		minor;
 	gss_buffer_desc		in_buf, out_buf;
@@ -1488,8 +1489,8 @@
 	in_buf.value = (char *)&num_net;
 /* XXX uid ? */
 
-	if ((kgss_sign(&minor, cl->context, cl->qop, &in_buf,
-				&out_buf)) != GSS_S_COMPLETE)
+	if ((kgss_sign(&minor, cl->context, cl->qop, &in_buf, &out_buf))
+	    != GSS_S_COMPLETE)
 		return (FALSE);
 
 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
@@ -1564,8 +1565,7 @@
  * Insert client context into hash list and LRU list.
  */
 static void
-insert_client(client_data)
-	svc_rpc_gss_data	*client_data;
+insert_client(svc_rpc_gss_data *client_data)
 {
 	svc_rpc_gss_data	*cl;
 	int			index = HASH(client_data->key);
@@ -1593,8 +1593,7 @@
  * top of the LRU list since this is the most recently used context.
  */
 static svc_rpc_gss_data *
-get_client(ctx_handle)
-	gss_buffer_t		ctx_handle;
+get_client(gss_buffer_t ctx_handle)
 {
 	uint_t			key = *(uint_t *)ctx_handle->value;
 	svc_rpc_gss_data	*cl;
@@ -1636,8 +1635,7 @@
  * Don't change its LRU state since it may not be used.
  */
 static svc_rpc_gss_data *
-find_client(key)
-	uint_t			key;
+find_client(uint_t key)
 {
 	int			index = HASH(key);
 	svc_rpc_gss_data	*cl = NULL;
@@ -1655,8 +1653,7 @@
  * Destroy a client context.
  */
 static void
-destroy_client(client_data)
-	svc_rpc_gss_data	*client_data;
+destroy_client(svc_rpc_gss_data *client_data)
 {
 	OM_uint32		minor;
 	int			index = HASH(client_data->key);
@@ -1690,20 +1687,20 @@
 	 */
 	if (client_data->context != GSS_C_NO_CONTEXT) {
 		(void) kgss_delete_sec_context(&minor, &client_data->context,
-					NULL);
+		    NULL);
 
 		common_client_data_free(client_data);
 
 		if (client_data->deleg != GSS_C_NO_CREDENTIAL) {
-		    (void) kgss_release_cred(&minor, &client_data->deleg,
-				crgetuid(CRED()));
+			(void) kgss_release_cred(&minor, &client_data->deleg,
+			    crgetuid(CRED()));
 		}
 	}
 
 	if (client_data->u_cred.gidlist != NULL) {
-	    kmem_free((char *)client_data->u_cred.gidlist,
-			client_data->u_cred.gidlen * sizeof (gid_t));
-	    client_data->u_cred.gidlist = NULL;
+		kmem_free((char *)client_data->u_cred.gidlist,
+		    client_data->u_cred.gidlen * sizeof (gid_t));
+		client_data->u_cred.gidlist = NULL;
 	}
 	if (client_data->retrans_data != NULL)
 		retrans_del(client_data);
@@ -1773,11 +1770,8 @@
  * and write the result to xdrs.
  */
 static bool_t
-svc_rpc_gss_wrap(auth, out_xdrs, xdr_func, xdr_ptr)
-	SVCAUTH			*auth;
-	XDR			*out_xdrs;
-	bool_t			(*xdr_func)();
-	caddr_t			xdr_ptr;
+svc_rpc_gss_wrap(SVCAUTH *auth, XDR *out_xdrs, bool_t (*xdr_func)(),
+    caddr_t xdr_ptr)
 {
 	svc_rpc_gss_parms_t	*gss_parms = SVCAUTH_GSSPARMS(auth);
 	bool_t ret;
@@ -1787,15 +1781,14 @@
 	 * privacy service is used, don't wrap - just XDR encode.
 	 * Otherwise, wrap data using service and QOP parameters.
 	 */
-	if (!gss_parms->established ||
-				gss_parms->service == rpc_gss_svc_none)
+	if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
 		return ((*xdr_func)(out_xdrs, xdr_ptr));
 
 	ret = __rpc_gss_wrap_data(gss_parms->service,
-				(OM_uint32)gss_parms->qop_rcvd,
-				(gss_ctx_id_t)gss_parms->context,
-				gss_parms->seq_num,
-				out_xdrs, xdr_func, xdr_ptr);
+	    (OM_uint32)gss_parms->qop_rcvd,
+	    (gss_ctx_id_t)gss_parms->context,
+	    gss_parms->seq_num,
+	    out_xdrs, xdr_func, xdr_ptr);
 	return (ret);
 }
 
@@ -1803,11 +1796,8 @@
  * Decrypt the serialized arguments and XDR decode them.
  */
 static bool_t
-svc_rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
-	SVCAUTH			*auth;
-	XDR			*in_xdrs;
-	bool_t			(*xdr_func)();
-	caddr_t			xdr_ptr;
+svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *in_xdrs, bool_t (*xdr_func)(),
+    caddr_t xdr_ptr)
 {
 	svc_rpc_gss_parms_t	*gss_parms = SVCAUTH_GSSPARMS(auth);
 
@@ -1816,15 +1806,14 @@
 	 * privacy service is used, don't unwrap - just XDR decode.
 	 * Otherwise, unwrap data.
 	 */
-	if (!gss_parms->established ||
-				gss_parms->service == rpc_gss_svc_none)
+	if (!gss_parms->established || gss_parms->service == rpc_gss_svc_none)
 		return ((*xdr_func)(in_xdrs, xdr_ptr));
 
 	return (__rpc_gss_unwrap_data(gss_parms->service,
-				(gss_ctx_id_t)gss_parms->context,
-				gss_parms->seq_num,
-				gss_parms->qop_rcvd,
-				in_xdrs, xdr_func, xdr_ptr));
+	    (gss_ctx_id_t)gss_parms->context,
+	    gss_parms->seq_num,
+	    gss_parms->qop_rcvd,
+	    in_xdrs, xdr_func, xdr_ptr));
 }
 
 
diff --git a/usr/src/uts/common/rpc/svc.c b/usr/src/uts/common/rpc/svc.c
index 43f8a6d..9ea2693 100644
--- a/usr/src/uts/common/rpc/svc.c
+++ b/usr/src/uts/common/rpc/svc.c
@@ -20,7 +20,9 @@
  */
 
 /*
+ * Copyright 2012 Marcel Telka <marcel@telka.sk>
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -33,7 +35,7 @@
  */
 
 /*	Copyright (c) 1983, 1984, 1985,  1986, 1987, 1988, 1989 AT&T	*/
-/*	  All Rights Reserved  	*/
+/*	All Rights Reserved	*/
 
 /*
  * Portions of this source code were derived from Berkeley 4.3 BSD
@@ -296,27 +298,6 @@
 static kmutex_t rqcred_lock;
 
 /*
- * Pointers to transport specific `rele' routines in rpcmod (set from rpcmod).
- */
-void	(*rpc_rele)(queue_t *, mblk_t *, bool_t) = NULL;
-void	(*mir_rele)(queue_t *, mblk_t *, bool_t) = NULL;
-
-/* ARGSUSED */
-void
-rpc_rdma_rele(queue_t *q, mblk_t *mp, bool_t enable)
-{
-}
-void    (*rdma_rele)(queue_t *, mblk_t *, bool_t) = rpc_rdma_rele;
-
-
-/*
- * This macro picks which `rele' routine to use, based on the transport type.
- */
-#define	RELE_PROC(xprt) \
-	((xprt)->xp_type == T_RDMA ? rdma_rele : \
-	(((xprt)->xp_type == T_CLTS) ? rpc_rele : mir_rele))
-
-/*
  * If true, then keep quiet about version mismatch.
  * This macro is for broadcast RPC only. We have no broadcast RPC in
  * kernel now but one may define a flag in the transport structure
@@ -595,7 +576,7 @@
  */
 static int
 svc_pool_init(SVCPOOL *pool, uint_t maxthreads, uint_t redline,
-	uint_t qsize, uint_t timeout, uint_t stksize, uint_t max_same_xprt)
+    uint_t qsize, uint_t timeout, uint_t stksize, uint_t max_same_xprt)
 {
 	klwp_t *lwp = ttolwp(curthread);
 
@@ -2387,7 +2368,7 @@
 		if (enable)
 			xprt->xp_enable = FALSE;
 		mutex_exit(&xprt->xp_req_lock);
-		(*RELE_PROC(xprt)) (clone_xprt->xp_wq, NULL, enable);
+		SVC_RELE(clone_xprt, NULL, enable);
 	}
 	/* NOTREACHED */
 }
@@ -2412,7 +2393,7 @@
 		/* remove the request from the list */
 		xprt->xp_req_head = mp->b_next;
 		mp->b_next = (mblk_t *)0;
-		(*RELE_PROC(xprt)) (xprt->xp_wq, mp, FALSE);
+		SVC_RELE(xprt, mp, FALSE);
 	}
 
 	mutex_enter(&pool->p_req_lock);
@@ -2729,7 +2710,7 @@
 	if (enable)
 		xprt->xp_enable = FALSE;
 	mutex_exit(&xprt->xp_req_lock);
-	(*RELE_PROC(xprt)) (clone_xprt->xp_wq, NULL, enable);
+	SVC_RELE(clone_xprt, NULL, enable);
 
 	/* Mark the clone (thread) as detached */
 	clone_xprt->xp_reserved = FALSE;
diff --git a/usr/src/uts/common/rpc/svc.h b/usr/src/uts/common/rpc/svc.h
index fadf6b2..2ed1ce5 100644
--- a/usr/src/uts/common/rpc/svc.h
+++ b/usr/src/uts/common/rpc/svc.h
@@ -20,7 +20,9 @@
  */
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Marcel Telka <marcel@telka.sk>
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
 /* All Rights Reserved */
@@ -189,7 +191,11 @@
 		/* `ready-to-receive' */
 	void	(*xp_clone_xprt)(SVCXPRT *, SVCXPRT *);
 		/* transport specific clone function */
-	void	(*xp_tattrs) (SVCXPRT *, int, void **);
+	void	(*xp_tattrs)(SVCXPRT *, int, void **);
+		/* transport specific hold function */
+	void	(*xp_hold)(queue_t *);
+		/* transport specific release function */
+	void	(*xp_release)(queue_t *, mblk_t *, bool_t);
 };
 
 #define	SVC_TATTR_ADDRMASK	1
@@ -272,7 +278,7 @@
 	kmutex_t	p_thread_lock;		/* Thread lock		  */
 	int		p_asleep;		/* Asleep threads	  */
 	int		p_drowsy;		/* Drowsy flag		  */
-	kcondvar_t 	p_req_cv;		/* svc_poll() sleep var.  */
+	kcondvar_t	p_req_cv;		/* svc_poll() sleep var.  */
 	clock_t		p_timeout;		/* svc_poll() timeout	  */
 	kmutex_t	p_req_lock;		/* Request lock		  */
 	int		p_reqs;			/* Pending requests	  */
@@ -414,8 +420,8 @@
 #define	xp_netid	xp_xpc.xpc_netid
 
 struct __svcmasterxprt {
-	SVCMASTERXPRT 	*xp_next;	/* Next transport in the list	*/
-	SVCMASTERXPRT 	*xp_prev;	/* Prev transport in the list	*/
+	SVCMASTERXPRT	*xp_next;	/* Next transport in the list	*/
+	SVCMASTERXPRT	*xp_prev;	/* Prev transport in the list	*/
 	__SVCXPRT_COMMON xp_xpc;	/* Fields common with the clone	*/
 	SVCPOOL		*xp_pool;	/* Pointer to the pool		*/
 	mblk_t		*xp_req_head;	/* Request queue head		*/
@@ -532,6 +538,14 @@
 		(*(src_xprt)->xp_ops->xp_clone_xprt) \
 		    (src_xprt, dst_xprt)
 
+#define	SVC_HOLD(xprt) \
+	if ((xprt)->xp_ops->xp_hold) \
+		(*(xprt)->xp_ops->xp_hold)((xprt)->xp_wq)
+
+#define	SVC_RELE(xprt, mp, enable) \
+	if ((xprt)->xp_ops->xp_release) \
+		(*(xprt)->xp_ops->xp_release)((xprt)->xp_wq, (mp), (enable))
+
 #define	SVC_RECV(clone_xprt, mp, msg) \
 	(*(clone_xprt)->xp_ops->xp_recv)((clone_xprt), (mp), (msg))
 
@@ -728,6 +742,15 @@
 #endif /* __STDC__ */
 #endif	/* _KERNEL */
 
+#ifdef _KERNEL
+/*
+ * Transport hold and release.
+ */
+extern void rpcmod_hold(queue_t *);
+extern void rpcmod_release(queue_t *, mblk_t *, bool_t);
+extern void mir_svc_hold(queue_t *);
+extern void mir_svc_release(queue_t *, mblk_t *, bool_t);
+#endif /* _KERNEL */
 
 /*
  * When the service routine is called, it must first check to see if it
@@ -903,7 +926,7 @@
 				const rpcprog_t, const rpcvers_t,
 				const char *);
 	/*
-	 * 	void (*dispatch)();		-- dispatch routine
+	 *	void (*dispatch)();		-- dispatch routine
 	 *	const rpcprog_t prognum;	-- program number
 	 *	const rpcvers_t versnum;	-- version number
 	 *	const char *nettype;		-- network type
@@ -976,9 +999,9 @@
  */
 extern  SVCXPRT	*svc_fd_create(const int, const uint_t, const uint_t);
 	/*
-	 * 	const int fd;			-- open connection end point
-	 * 	const uint_t sendsize;		-- max send size
-	 * 	const uint_t recvsize;		-- max recv size
+	 *	const int fd;			-- open connection end point
+	 *	const uint_t sendsize;		-- max send size
+	 *	const uint_t recvsize;		-- max recv size
 	 */
 
 /*
@@ -993,7 +1016,7 @@
 				const rpcprog_t, const rpcvers_t,
 				const uint_t);
 	/*
-	 * 	void (*dispatch)();		-- dispatch routine
+	 *	void (*dispatch)();		-- dispatch routine
 	 *	const rpcprog_t prognum;	-- program number
 	 *	const rpcvers_t versnum;	-- version number
 	 *	const uint_t sendsize;		-- send buffer size
diff --git a/usr/src/uts/common/rpc/svc_clts.c b/usr/src/uts/common/rpc/svc_clts.c
index cc161c1..f38f8e8 100644
--- a/usr/src/uts/common/rpc/svc_clts.c
+++ b/usr/src/uts/common/rpc/svc_clts.c
@@ -21,12 +21,14 @@
 
 /*
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
- *  Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2012 Marcel Telka <marcel@telka.sk>
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
-/*	  All Rights Reserved  	*/
+/*	All Rights Reserved	*/
 
 /*
  * Portions of this source code were derived from Berkeley 4.3 BSD
@@ -101,7 +103,9 @@
 	svc_clts_kclone_destroy, /* Destroy a clone xprt */
 	svc_clts_kstart,	/* Tell `ready-to-receive' to rpcmod */
 	svc_clts_kclone_xprt,	/* transport specific clone xprt function */
-	svc_clts_ktattrs	/* Transport specific attributes. */
+	svc_clts_ktattrs,	/* Transport specific attributes */
+	rpcmod_hold,		/* Increment transport reference count */
+	rpcmod_release		/* Decrement transport reference count */
 };
 
 /*
diff --git a/usr/src/uts/common/rpc/svc_cots.c b/usr/src/uts/common/rpc/svc_cots.c
index d4e93b0..6fda4ab 100644
--- a/usr/src/uts/common/rpc/svc_cots.c
+++ b/usr/src/uts/common/rpc/svc_cots.c
@@ -21,12 +21,14 @@
 
 /*
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
- *  Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2012 Marcel Telka <marcel@telka.sk>
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
-/*	  All Rights Reserved  	*/
+/*	All Rights Reserved	*/
 
 /*
  * Portions of this source code were derived from Berkeley 4.3 BSD
@@ -104,7 +106,9 @@
 	svc_cots_kclone_destroy, /* Destroy a clone xprt */
 	svc_cots_kstart,	/* Tell `ready-to-receive' to rpcmod */
 	NULL,			/* Transport specific clone xprt */
-	svc_cots_ktattrs	/* Transport Attributes */
+	svc_cots_ktattrs,	/* Transport Attributes */
+	mir_svc_hold,		/* Increment transport reference count */
+	mir_svc_release		/* Decrement transport reference count */
 };
 
 /*
diff --git a/usr/src/uts/common/rpc/svc_rdma.c b/usr/src/uts/common/rpc/svc_rdma.c
index f4d6efd..5449550 100644
--- a/usr/src/uts/common/rpc/svc_rdma.c
+++ b/usr/src/uts/common/rpc/svc_rdma.c
@@ -22,6 +22,8 @@
  * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2012 Marcel Telka <marcel@telka.sk>
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
 /* All Rights Reserved */
@@ -82,7 +84,7 @@
  * RDMA transport specific data associated with SVCMASTERXPRT
  */
 struct rdma_data {
-	SVCMASTERXPRT 	*rd_xprt;	/* back ptr to SVCMASTERXPRT */
+	SVCMASTERXPRT	*rd_xprt;	/* back ptr to SVCMASTERXPRT */
 	struct rdma_svc_data rd_data;	/* rdma data */
 	rdma_mod_t	*r_mod;		/* RDMA module containing ops ptr */
 };
@@ -148,7 +150,9 @@
 	svc_rdma_kclone_destroy,	/* Destroy a clone xprt */
 	svc_rdma_kstart,	/* Tell `ready-to-receive' to rpcmod */
 	svc_rdma_kclone_xprt,	/* Transport specific clone xprt */
-	svc_rdma_ktattrs	/* Get Transport Attributes */
+	svc_rdma_ktattrs,	/* Get Transport Attributes */
+	NULL,			/* Increment transport reference count */
+	NULL			/* Decrement transport reference count */
 };
 
 /*