11bdd6c0eSSue Gleeson /*
21bdd6c0eSSue Gleeson  * CDDL HEADER START
31bdd6c0eSSue Gleeson  *
41bdd6c0eSSue Gleeson  * The contents of this file are subject to the terms of the
51bdd6c0eSSue Gleeson  * Common Development and Distribution License (the "License").
61bdd6c0eSSue Gleeson  * You may not use this file except in compliance with the License.
71bdd6c0eSSue Gleeson  *
81bdd6c0eSSue Gleeson  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91bdd6c0eSSue Gleeson  * or http://www.opensolaris.org/os/licensing.
101bdd6c0eSSue Gleeson  * See the License for the specific language governing permissions
111bdd6c0eSSue Gleeson  * and limitations under the License.
121bdd6c0eSSue Gleeson  *
131bdd6c0eSSue Gleeson  * When distributing Covered Code, include this CDDL HEADER in each
141bdd6c0eSSue Gleeson  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151bdd6c0eSSue Gleeson  * If applicable, add the following below this CDDL HEADER, with the
161bdd6c0eSSue Gleeson  * fields enclosed by brackets "[]" replaced with your own identifying
171bdd6c0eSSue Gleeson  * information: Portions Copyright [yyyy] [name of copyright owner]
181bdd6c0eSSue Gleeson  *
191bdd6c0eSSue Gleeson  * CDDL HEADER END
201bdd6c0eSSue Gleeson  */
211bdd6c0eSSue Gleeson 
221bdd6c0eSSue Gleeson /*
23*4558d122SViswanathan Kannappan  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
241bdd6c0eSSue Gleeson  */
251bdd6c0eSSue Gleeson 
261bdd6c0eSSue Gleeson /*
271bdd6c0eSSue Gleeson  * RDMA channel interface for Solaris SCSI RDMA Protocol Target (SRP)
281bdd6c0eSSue Gleeson  * transport port provider module for the COMSTAR framework.
291bdd6c0eSSue Gleeson  */
301bdd6c0eSSue Gleeson 
311bdd6c0eSSue Gleeson #include <sys/cpuvar.h>
321bdd6c0eSSue Gleeson #include <sys/types.h>
331bdd6c0eSSue Gleeson #include <sys/conf.h>
341bdd6c0eSSue Gleeson #include <sys/stat.h>
351bdd6c0eSSue Gleeson #include <sys/file.h>
361bdd6c0eSSue Gleeson #include <sys/ddi.h>
371bdd6c0eSSue Gleeson #include <sys/sunddi.h>
381bdd6c0eSSue Gleeson #include <sys/modctl.h>
391bdd6c0eSSue Gleeson #include <sys/sysmacros.h>
401bdd6c0eSSue Gleeson #include <sys/sdt.h>
411bdd6c0eSSue Gleeson #include <sys/taskq.h>
421bdd6c0eSSue Gleeson #include <sys/scsi/scsi.h>
431bdd6c0eSSue Gleeson #include <sys/ib/ibtl/ibti.h>
441bdd6c0eSSue Gleeson 
45*4558d122SViswanathan Kannappan #include <sys/stmf.h>
46*4558d122SViswanathan Kannappan #include <sys/stmf_ioctl.h>
47*4558d122SViswanathan Kannappan #include <sys/portif.h>
481bdd6c0eSSue Gleeson 
491bdd6c0eSSue Gleeson #include "srp.h"
501bdd6c0eSSue Gleeson #include "srpt_impl.h"
511bdd6c0eSSue Gleeson #include "srpt_ioc.h"
521bdd6c0eSSue Gleeson #include "srpt_stp.h"
531bdd6c0eSSue Gleeson #include "srpt_ch.h"
541bdd6c0eSSue Gleeson 
551bdd6c0eSSue Gleeson extern srpt_ctxt_t *srpt_ctxt;
561bdd6c0eSSue Gleeson extern uint16_t srpt_send_msg_depth;
571bdd6c0eSSue Gleeson 
581bdd6c0eSSue Gleeson /*
591bdd6c0eSSue Gleeson  * Prototypes.
601bdd6c0eSSue Gleeson  */
611bdd6c0eSSue Gleeson static void srpt_ch_scq_hdlr(ibt_cq_hdl_t cq_dhl, void *arg);
621bdd6c0eSSue Gleeson static void srpt_ch_rcq_hdlr(ibt_cq_hdl_t cq_dhl, void *arg);
631bdd6c0eSSue Gleeson static void srpt_ch_process_iu(srpt_channel_t *ch, srpt_iu_t *iu);
641bdd6c0eSSue Gleeson 
651bdd6c0eSSue Gleeson /*
661bdd6c0eSSue Gleeson  * srpt_ch_alloc()
671bdd6c0eSSue Gleeson  */
681bdd6c0eSSue Gleeson srpt_channel_t *
srpt_ch_alloc(srpt_target_port_t * tgt,uint8_t port)691bdd6c0eSSue Gleeson srpt_ch_alloc(srpt_target_port_t *tgt, uint8_t port)
701bdd6c0eSSue Gleeson {
711bdd6c0eSSue Gleeson 	ibt_status_t			status;
721bdd6c0eSSue Gleeson 	srpt_channel_t			*ch;
731bdd6c0eSSue Gleeson 	ibt_cq_attr_t			cq_attr;
741bdd6c0eSSue Gleeson 	ibt_rc_chan_alloc_args_t	ch_args;
751bdd6c0eSSue Gleeson 	uint32_t			cq_real_size;
761bdd6c0eSSue Gleeson 	srpt_ioc_t			*ioc;
771bdd6c0eSSue Gleeson 
781bdd6c0eSSue Gleeson 	ASSERT(tgt != NULL);
791bdd6c0eSSue Gleeson 	ioc = tgt->tp_ioc;
801bdd6c0eSSue Gleeson 	ASSERT(ioc != NULL);
811bdd6c0eSSue Gleeson 
821bdd6c0eSSue Gleeson 	ch = kmem_zalloc(sizeof (*ch), KM_SLEEP);
831bdd6c0eSSue Gleeson 	rw_init(&ch->ch_rwlock, NULL, RW_DRIVER, NULL);
841bdd6c0eSSue Gleeson 	mutex_init(&ch->ch_reflock, NULL, MUTEX_DRIVER, NULL);
851bdd6c0eSSue Gleeson 	cv_init(&ch->ch_cv_complete, NULL, CV_DRIVER, NULL);
861bdd6c0eSSue Gleeson 	ch->ch_refcnt	= 1;
871bdd6c0eSSue Gleeson 	ch->ch_cv_waiters = 0;
881bdd6c0eSSue Gleeson 
891bdd6c0eSSue Gleeson 	ch->ch_state  = SRPT_CHANNEL_CONNECTING;
901bdd6c0eSSue Gleeson 	ch->ch_tgt    = tgt;
911bdd6c0eSSue Gleeson 	ch->ch_req_lim_delta = 0;
921bdd6c0eSSue Gleeson 	ch->ch_ti_iu_len = 0;
931bdd6c0eSSue Gleeson 
941bdd6c0eSSue Gleeson 	cq_attr.cq_size	 = srpt_send_msg_depth * 2;
951bdd6c0eSSue Gleeson 	cq_attr.cq_sched = 0;
961bdd6c0eSSue Gleeson 	cq_attr.cq_flags = IBT_CQ_NO_FLAGS;
971bdd6c0eSSue Gleeson 
981bdd6c0eSSue Gleeson 	status = ibt_alloc_cq(ioc->ioc_ibt_hdl, &cq_attr, &ch->ch_scq_hdl,
991bdd6c0eSSue Gleeson 	    &cq_real_size);
1001bdd6c0eSSue Gleeson 	if (status != IBT_SUCCESS) {
1011bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L1("ch_alloc, send CQ alloc error (%d)",
1021bdd6c0eSSue Gleeson 		    status);
1031bdd6c0eSSue Gleeson 		goto scq_alloc_err;
1041bdd6c0eSSue Gleeson 	}
1051bdd6c0eSSue Gleeson 
1061bdd6c0eSSue Gleeson 	cq_attr.cq_size	 = srpt_send_msg_depth + 1;
1071bdd6c0eSSue Gleeson 	cq_attr.cq_sched = 0;
1081bdd6c0eSSue Gleeson 	cq_attr.cq_flags = IBT_CQ_NO_FLAGS;
1091bdd6c0eSSue Gleeson 
1101bdd6c0eSSue Gleeson 	status = ibt_alloc_cq(ioc->ioc_ibt_hdl, &cq_attr, &ch->ch_rcq_hdl,
1111bdd6c0eSSue Gleeson 	    &cq_real_size);
1121bdd6c0eSSue Gleeson 	if (status != IBT_SUCCESS) {
1131bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_alloc, receive CQ alloc error (%d)",
1141bdd6c0eSSue Gleeson 		    status);
1151bdd6c0eSSue Gleeson 		goto rcq_alloc_err;
1161bdd6c0eSSue Gleeson 	}
1171bdd6c0eSSue Gleeson 
1181bdd6c0eSSue Gleeson 	ibt_set_cq_handler(ch->ch_scq_hdl, srpt_ch_scq_hdlr, ch);
1191bdd6c0eSSue Gleeson 	ibt_set_cq_handler(ch->ch_rcq_hdl, srpt_ch_rcq_hdlr, ch);
120aedf2b3bSsrivijitha dugganapalli 	(void) ibt_enable_cq_notify(ch->ch_scq_hdl, IBT_NEXT_COMPLETION);
121aedf2b3bSsrivijitha dugganapalli 	(void) ibt_enable_cq_notify(ch->ch_rcq_hdl, IBT_NEXT_COMPLETION);
1221bdd6c0eSSue Gleeson 
1231bdd6c0eSSue Gleeson 	ch_args.rc_flags   = IBT_WR_SIGNALED;
1241bdd6c0eSSue Gleeson 
1251bdd6c0eSSue Gleeson 	/* Maker certain initiator can not read/write our memory */
1261bdd6c0eSSue Gleeson 	ch_args.rc_control = 0;
1271bdd6c0eSSue Gleeson 
1281bdd6c0eSSue Gleeson 	ch_args.rc_hca_port_num = port;
1291bdd6c0eSSue Gleeson 
1301bdd6c0eSSue Gleeson 	/*
1311bdd6c0eSSue Gleeson 	 * Any SRP IU can result in a number of STMF data buffer transfers
1321bdd6c0eSSue Gleeson 	 * and those transfers themselves could span multiple initiator
1331bdd6c0eSSue Gleeson 	 * buffers.  Therefore, the number of send WQE's actually required
1341bdd6c0eSSue Gleeson 	 * can vary.  Here we assume that on average an I/O will require
1351bdd6c0eSSue Gleeson 	 * no more than SRPT_MAX_OUT_IO_PER_CMD send WQE's.  In practice
1361bdd6c0eSSue Gleeson 	 * this will prevent send work queue overrun, but we will also
1371bdd6c0eSSue Gleeson 	 * inform STMF to throttle I/O should the work queue become full.
1381bdd6c0eSSue Gleeson 	 *
1391bdd6c0eSSue Gleeson 	 * If the HCA tells us the max outstanding WRs for a channel is
1401bdd6c0eSSue Gleeson 	 * lower than our default, use the HCA value.
1411bdd6c0eSSue Gleeson 	 */
1421bdd6c0eSSue Gleeson 	ch_args.rc_sizes.cs_sq = min(ioc->ioc_attr.hca_max_chan_sz,
1431bdd6c0eSSue Gleeson 	    (srpt_send_msg_depth * SRPT_MAX_OUT_IO_PER_CMD));
1441bdd6c0eSSue Gleeson 	ch_args.rc_sizes.cs_rq =  0;
1451bdd6c0eSSue Gleeson 	ch_args.rc_sizes.cs_sq_sgl = 2;
1461bdd6c0eSSue Gleeson 	ch_args.rc_sizes.cs_rq_sgl = 0;
1471bdd6c0eSSue Gleeson 
1481bdd6c0eSSue Gleeson 	ch_args.rc_scq = ch->ch_scq_hdl;
1491bdd6c0eSSue Gleeson 	ch_args.rc_rcq = ch->ch_rcq_hdl;
1501bdd6c0eSSue Gleeson 	ch_args.rc_pd  = ioc->ioc_pd_hdl;
1511bdd6c0eSSue Gleeson 	ch_args.rc_clone_chan = NULL;
1521bdd6c0eSSue Gleeson 	ch_args.rc_srq = ioc->ioc_srq_hdl;
1531bdd6c0eSSue Gleeson 
1541bdd6c0eSSue Gleeson 	status = ibt_alloc_rc_channel(ioc->ioc_ibt_hdl, IBT_ACHAN_USES_SRQ,
1551bdd6c0eSSue Gleeson 	    &ch_args, &ch->ch_chan_hdl, &ch->ch_sizes);
1561bdd6c0eSSue Gleeson 	if (status != IBT_SUCCESS) {
1571bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_alloc, IBT channel alloc error (%d)",
1581bdd6c0eSSue Gleeson 		    status);
1591bdd6c0eSSue Gleeson 		goto qp_alloc_err;
1601bdd6c0eSSue Gleeson 	}
1611bdd6c0eSSue Gleeson 
1621bdd6c0eSSue Gleeson 	/*
1631bdd6c0eSSue Gleeson 	 * Create pool of send WQE entries to map send wqe work IDs
1641bdd6c0eSSue Gleeson 	 * to various types (specifically in error cases where OP
1651bdd6c0eSSue Gleeson 	 * is not known).
1661bdd6c0eSSue Gleeson 	 */
1671bdd6c0eSSue Gleeson 	ch->ch_num_swqe = ch->ch_sizes.cs_sq;
1681bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L2("ch_alloc, number of SWQEs = %u", ch->ch_num_swqe);
1691bdd6c0eSSue Gleeson 	ch->ch_swqe = kmem_zalloc(sizeof (srpt_swqe_t) * ch->ch_num_swqe,
1701bdd6c0eSSue Gleeson 	    KM_SLEEP);
1711bdd6c0eSSue Gleeson 	if (ch->ch_swqe == NULL) {
1721bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_alloc, SWQE alloc error");
173aedf2b3bSsrivijitha dugganapalli 		(void) ibt_free_channel(ch->ch_chan_hdl);
1741bdd6c0eSSue Gleeson 		goto qp_alloc_err;
1751bdd6c0eSSue Gleeson 	}
1761bdd6c0eSSue Gleeson 	mutex_init(&ch->ch_swqe_lock, NULL, MUTEX_DRIVER, NULL);
1771bdd6c0eSSue Gleeson 	ch->ch_head = 1;
1781bdd6c0eSSue Gleeson 	for (ch->ch_tail = 1; ch->ch_tail < ch->ch_num_swqe -1; ch->ch_tail++) {
1791bdd6c0eSSue Gleeson 		ch->ch_swqe[ch->ch_tail].sw_next = ch->ch_tail + 1;
1801bdd6c0eSSue Gleeson 	}
1811bdd6c0eSSue Gleeson 	ch->ch_swqe[ch->ch_tail].sw_next = 0;
1821bdd6c0eSSue Gleeson 
1831bdd6c0eSSue Gleeson 	ibt_set_chan_private(ch->ch_chan_hdl, ch);
1841bdd6c0eSSue Gleeson 	return (ch);
1851bdd6c0eSSue Gleeson 
1861bdd6c0eSSue Gleeson qp_alloc_err:
187aedf2b3bSsrivijitha dugganapalli 	(void) ibt_free_cq(ch->ch_rcq_hdl);
1881bdd6c0eSSue Gleeson 
1891bdd6c0eSSue Gleeson rcq_alloc_err:
190aedf2b3bSsrivijitha dugganapalli 	(void) ibt_free_cq(ch->ch_scq_hdl);
1911bdd6c0eSSue Gleeson 
1921bdd6c0eSSue Gleeson scq_alloc_err:
1931bdd6c0eSSue Gleeson 	cv_destroy(&ch->ch_cv_complete);
1941bdd6c0eSSue Gleeson 	mutex_destroy(&ch->ch_reflock);
1951bdd6c0eSSue Gleeson 	rw_destroy(&ch->ch_rwlock);
1961bdd6c0eSSue Gleeson 	kmem_free(ch, sizeof (*ch));
1971bdd6c0eSSue Gleeson 
1981bdd6c0eSSue Gleeson 	return (NULL);
1991bdd6c0eSSue Gleeson }
2001bdd6c0eSSue Gleeson 
2011bdd6c0eSSue Gleeson /*
2021bdd6c0eSSue Gleeson  * srpt_ch_add_ref()
2031bdd6c0eSSue Gleeson  */
2041bdd6c0eSSue Gleeson void
srpt_ch_add_ref(srpt_channel_t * ch)2051bdd6c0eSSue Gleeson srpt_ch_add_ref(srpt_channel_t *ch)
2061bdd6c0eSSue Gleeson {
2071bdd6c0eSSue Gleeson 	mutex_enter(&ch->ch_reflock);
2081bdd6c0eSSue Gleeson 	ch->ch_refcnt++;
2091bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L4("ch_add_ref, ch (%p), refcnt (%d)",
2101bdd6c0eSSue Gleeson 	    (void *)ch, ch->ch_refcnt);
2111bdd6c0eSSue Gleeson 	ASSERT(ch->ch_refcnt != 0);
2121bdd6c0eSSue Gleeson 	mutex_exit(&ch->ch_reflock);
2131bdd6c0eSSue Gleeson }
2141bdd6c0eSSue Gleeson 
2151bdd6c0eSSue Gleeson /*
2161bdd6c0eSSue Gleeson  * srpt_ch_release_ref()
2171bdd6c0eSSue Gleeson  *
2181bdd6c0eSSue Gleeson  * A non-zero value for wait causes thread to block until all references
2191bdd6c0eSSue Gleeson  * to channel are released.
2201bdd6c0eSSue Gleeson  */
2211bdd6c0eSSue Gleeson void
srpt_ch_release_ref(srpt_channel_t * ch,uint_t wait)2221bdd6c0eSSue Gleeson srpt_ch_release_ref(srpt_channel_t *ch, uint_t wait)
2231bdd6c0eSSue Gleeson {
2241bdd6c0eSSue Gleeson 	mutex_enter(&ch->ch_reflock);
2251bdd6c0eSSue Gleeson 
2261bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L4("ch_release_ref, ch (%p), refcnt (%d), wait (%d)",
2271bdd6c0eSSue Gleeson 	    (void *)ch, ch->ch_refcnt, wait);
2281bdd6c0eSSue Gleeson 
2291bdd6c0eSSue Gleeson 	ASSERT(ch->ch_refcnt != 0);
2301bdd6c0eSSue Gleeson 
2311bdd6c0eSSue Gleeson 	ch->ch_refcnt--;
2321bdd6c0eSSue Gleeson 
2331bdd6c0eSSue Gleeson 	if (ch->ch_refcnt != 0) {
2341bdd6c0eSSue Gleeson 		if (wait) {
2351bdd6c0eSSue Gleeson 			ch->ch_cv_waiters++;
2361bdd6c0eSSue Gleeson 			while (ch->ch_refcnt != 0) {
2371bdd6c0eSSue Gleeson 				cv_wait(&ch->ch_cv_complete, &ch->ch_reflock);
2381bdd6c0eSSue Gleeson 			}
2391bdd6c0eSSue Gleeson 			ch->ch_cv_waiters--;
2401bdd6c0eSSue Gleeson 		} else {
2411bdd6c0eSSue Gleeson 			mutex_exit(&ch->ch_reflock);
2421bdd6c0eSSue Gleeson 			return;
2431bdd6c0eSSue Gleeson 		}
2441bdd6c0eSSue Gleeson 	}
2451bdd6c0eSSue Gleeson 
2461bdd6c0eSSue Gleeson 	/*
2471bdd6c0eSSue Gleeson 	 * Last thread out frees the IB resources, locks/conditions and memory
2481bdd6c0eSSue Gleeson 	 */
2491bdd6c0eSSue Gleeson 	if (ch->ch_cv_waiters > 0) {
2501bdd6c0eSSue Gleeson 		/* we're not last, wake someone else up */
2511bdd6c0eSSue Gleeson 		cv_signal(&ch->ch_cv_complete);
2521bdd6c0eSSue Gleeson 		mutex_exit(&ch->ch_reflock);
2531bdd6c0eSSue Gleeson 		return;
2541bdd6c0eSSue Gleeson 	}
2551bdd6c0eSSue Gleeson 
2561bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L3("ch_release_ref - release resources");
2571bdd6c0eSSue Gleeson 	if (ch->ch_chan_hdl) {
2581bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L3("ch_release_ref - free channel");
259aedf2b3bSsrivijitha dugganapalli 		(void) ibt_free_channel(ch->ch_chan_hdl);
2601bdd6c0eSSue Gleeson 	}
2611bdd6c0eSSue Gleeson 
2621bdd6c0eSSue Gleeson 	if (ch->ch_scq_hdl) {
263aedf2b3bSsrivijitha dugganapalli 		(void) ibt_free_cq(ch->ch_scq_hdl);
2641bdd6c0eSSue Gleeson 	}
2651bdd6c0eSSue Gleeson 
2661bdd6c0eSSue Gleeson 	if (ch->ch_rcq_hdl) {
267aedf2b3bSsrivijitha dugganapalli 		(void) ibt_free_cq(ch->ch_rcq_hdl);
2681bdd6c0eSSue Gleeson 	}
2691bdd6c0eSSue Gleeson 
2701bdd6c0eSSue Gleeson 	/*
2711bdd6c0eSSue Gleeson 	 * There should be no IU's associated with this
2721bdd6c0eSSue Gleeson 	 * channel on the SCSI session.
2731bdd6c0eSSue Gleeson 	 */
2741bdd6c0eSSue Gleeson 	if (ch->ch_session != NULL) {
2751bdd6c0eSSue Gleeson 		ASSERT(list_is_empty(&ch->ch_session->ss_task_list));
2761bdd6c0eSSue Gleeson 
2771bdd6c0eSSue Gleeson 		/*
2781bdd6c0eSSue Gleeson 		 * Currently only have one channel per session, we will
2791bdd6c0eSSue Gleeson 		 * need to release a reference when support is added
2801bdd6c0eSSue Gleeson 		 * for multi-channel target login.
2811bdd6c0eSSue Gleeson 		 */
2821bdd6c0eSSue Gleeson 		srpt_stp_free_session(ch->ch_session);
2831bdd6c0eSSue Gleeson 		ch->ch_session = NULL;
2841bdd6c0eSSue Gleeson 	}
2851bdd6c0eSSue Gleeson 
2861bdd6c0eSSue Gleeson 	kmem_free(ch->ch_swqe, sizeof (srpt_swqe_t) * ch->ch_num_swqe);
2871bdd6c0eSSue Gleeson 	mutex_destroy(&ch->ch_swqe_lock);
2881bdd6c0eSSue Gleeson 	mutex_exit(&ch->ch_reflock);
2891bdd6c0eSSue Gleeson 	mutex_destroy(&ch->ch_reflock);
2901bdd6c0eSSue Gleeson 	rw_destroy(&ch->ch_rwlock);
2911bdd6c0eSSue Gleeson 	kmem_free(ch, sizeof (srpt_channel_t));
2921bdd6c0eSSue Gleeson }
2931bdd6c0eSSue Gleeson 
2941bdd6c0eSSue Gleeson /*
2951bdd6c0eSSue Gleeson  * srpt_ch_disconnect()
2961bdd6c0eSSue Gleeson  */
2971bdd6c0eSSue Gleeson void
srpt_ch_disconnect(srpt_channel_t * ch)2981bdd6c0eSSue Gleeson srpt_ch_disconnect(srpt_channel_t *ch)
2991bdd6c0eSSue Gleeson {
3001bdd6c0eSSue Gleeson 	ibt_status_t		status;
3011bdd6c0eSSue Gleeson 
3021bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L3("ch_disconnect, invoked for ch (%p)",
3031bdd6c0eSSue Gleeson 	    (void *)ch);
3041bdd6c0eSSue Gleeson 
3051bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_WRITER);
3061bdd6c0eSSue Gleeson 
3071bdd6c0eSSue Gleeson 	/*
3081bdd6c0eSSue Gleeson 	 * If we are already in the process of disconnecting then
3091bdd6c0eSSue Gleeson 	 * nothing need be done, CM will call-back into us when done.
3101bdd6c0eSSue Gleeson 	 */
3111bdd6c0eSSue Gleeson 	if (ch->ch_state == SRPT_CHANNEL_DISCONNECTING) {
3121bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_disconnect, called when"
3131bdd6c0eSSue Gleeson 		    " disconnect in progress");
3141bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
3151bdd6c0eSSue Gleeson 		return;
3161bdd6c0eSSue Gleeson 	}
3171bdd6c0eSSue Gleeson 	ch->ch_state = SRPT_CHANNEL_DISCONNECTING;
3181bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
3191bdd6c0eSSue Gleeson 
3201bdd6c0eSSue Gleeson 	/*
3211bdd6c0eSSue Gleeson 	 * Initiate the sending of the CM DREQ message, the private data
3221bdd6c0eSSue Gleeson 	 * should be the SRP Target logout IU.  We don't really care about
3231bdd6c0eSSue Gleeson 	 * the remote CM DREP message returned.  We issue this in an
3241bdd6c0eSSue Gleeson 	 * asynchronous manner and will cleanup when called back by CM.
3251bdd6c0eSSue Gleeson 	 */
3261bdd6c0eSSue Gleeson 	status = ibt_close_rc_channel(ch->ch_chan_hdl, IBT_NONBLOCKING,
3271bdd6c0eSSue Gleeson 	    NULL, 0, NULL, NULL, 0);
3281bdd6c0eSSue Gleeson 
3291bdd6c0eSSue Gleeson 	if (status != IBT_SUCCESS) {
3301bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_disconnect, close RC channel"
3311bdd6c0eSSue Gleeson 		    " err(%d)", status);
3321bdd6c0eSSue Gleeson 	}
3331bdd6c0eSSue Gleeson }
3341bdd6c0eSSue Gleeson 
3351bdd6c0eSSue Gleeson /*
3361bdd6c0eSSue Gleeson  * srpt_ch_cleanup()
3371bdd6c0eSSue Gleeson  */
3381bdd6c0eSSue Gleeson void
srpt_ch_cleanup(srpt_channel_t * ch)3391bdd6c0eSSue Gleeson srpt_ch_cleanup(srpt_channel_t *ch)
3401bdd6c0eSSue Gleeson {
3411bdd6c0eSSue Gleeson 	srpt_iu_t		*iu;
3421bdd6c0eSSue Gleeson 	srpt_iu_t		*next;
3431bdd6c0eSSue Gleeson 	ibt_wc_t		wc;
3441bdd6c0eSSue Gleeson 	srpt_target_port_t	*tgt;
3451bdd6c0eSSue Gleeson 	srpt_channel_t		*tgt_ch;
3461bdd6c0eSSue Gleeson 	scsi_task_t		*iutask;
3471bdd6c0eSSue Gleeson 
3481bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L3("ch_cleanup, invoked for ch(%p), state(%d)",
3491bdd6c0eSSue Gleeson 	    (void *)ch, ch->ch_state);
3501bdd6c0eSSue Gleeson 
3511bdd6c0eSSue Gleeson 	/* add a ref for the channel until we're done */
3521bdd6c0eSSue Gleeson 	srpt_ch_add_ref(ch);
3531bdd6c0eSSue Gleeson 
3541bdd6c0eSSue Gleeson 	tgt = ch->ch_tgt;
3551bdd6c0eSSue Gleeson 	ASSERT(tgt != NULL);
3561bdd6c0eSSue Gleeson 
3571bdd6c0eSSue Gleeson 	/*
3581bdd6c0eSSue Gleeson 	 * Make certain the channel is in the target ports list of
3591bdd6c0eSSue Gleeson 	 * known channels and remove it (releasing the target
3601bdd6c0eSSue Gleeson 	 * ports reference to the channel).
3611bdd6c0eSSue Gleeson 	 */
3621bdd6c0eSSue Gleeson 	mutex_enter(&tgt->tp_ch_list_lock);
3631bdd6c0eSSue Gleeson 	tgt_ch = list_head(&tgt->tp_ch_list);
3641bdd6c0eSSue Gleeson 	while (tgt_ch != NULL) {
3651bdd6c0eSSue Gleeson 		if (tgt_ch == ch) {
3661bdd6c0eSSue Gleeson 			list_remove(&tgt->tp_ch_list, tgt_ch);
3671bdd6c0eSSue Gleeson 			srpt_ch_release_ref(tgt_ch, 0);
3681bdd6c0eSSue Gleeson 			break;
3691bdd6c0eSSue Gleeson 		}
3701bdd6c0eSSue Gleeson 		tgt_ch = list_next(&tgt->tp_ch_list, tgt_ch);
3711bdd6c0eSSue Gleeson 	}
3721bdd6c0eSSue Gleeson 	mutex_exit(&tgt->tp_ch_list_lock);
3731bdd6c0eSSue Gleeson 
3741bdd6c0eSSue Gleeson 	if (tgt_ch == NULL) {
3751bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_cleanup, target channel no"
3761bdd6c0eSSue Gleeson 		    "longer known to target");
3771bdd6c0eSSue Gleeson 		srpt_ch_release_ref(ch, 0);
3781bdd6c0eSSue Gleeson 		return;
3791bdd6c0eSSue Gleeson 	}
3801bdd6c0eSSue Gleeson 
3811bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_WRITER);
3821bdd6c0eSSue Gleeson 	ch->ch_state = SRPT_CHANNEL_DISCONNECTING;
3831bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
3841bdd6c0eSSue Gleeson 
3851bdd6c0eSSue Gleeson 	/*
38628406608SSue Gleeson 	 * Don't accept any further incoming requests, and clean
38728406608SSue Gleeson 	 * up the receive queue.  The send queue is left alone
38828406608SSue Gleeson 	 * so tasks can finish and clean up (whether normally
38928406608SSue Gleeson 	 * or via abort).
3901bdd6c0eSSue Gleeson 	 */
3911bdd6c0eSSue Gleeson 	if (ch->ch_rcq_hdl) {
3921bdd6c0eSSue Gleeson 		ibt_set_cq_handler(ch->ch_rcq_hdl, NULL, NULL);
3931bdd6c0eSSue Gleeson 
3941bdd6c0eSSue Gleeson 		while (ibt_poll_cq(ch->ch_rcq_hdl, &wc, 1, NULL) ==
3951bdd6c0eSSue Gleeson 		    IBT_SUCCESS) {
3961bdd6c0eSSue Gleeson 			iu = (srpt_iu_t *)(uintptr_t)wc.wc_id;
3971bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L4("ch_cleanup, recovering"
3981bdd6c0eSSue Gleeson 			    " outstanding RX iu(%p)", (void *)iu);
3991bdd6c0eSSue Gleeson 			mutex_enter(&iu->iu_lock);
4001bdd6c0eSSue Gleeson 			srpt_ioc_repost_recv_iu(iu->iu_ioc, iu);
4011bdd6c0eSSue Gleeson 			/*
4021bdd6c0eSSue Gleeson 			 * Channel reference has not yet been added for this
4031bdd6c0eSSue Gleeson 			 * IU, so do not decrement.
4041bdd6c0eSSue Gleeson 			 */
4051bdd6c0eSSue Gleeson 			mutex_exit(&iu->iu_lock);
4061bdd6c0eSSue Gleeson 		}
4071bdd6c0eSSue Gleeson 	}
4081bdd6c0eSSue Gleeson 
4091bdd6c0eSSue Gleeson 	/*
4101bdd6c0eSSue Gleeson 	 * Go through the list of outstanding IU for the channel's SCSI
4111bdd6c0eSSue Gleeson 	 * session and for each either abort or complete an abort.
4121bdd6c0eSSue Gleeson 	 */
4131bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_READER);
4141bdd6c0eSSue Gleeson 	if (ch->ch_session != NULL) {
4151bdd6c0eSSue Gleeson 		rw_enter(&ch->ch_session->ss_rwlock, RW_READER);
4161bdd6c0eSSue Gleeson 		iu = list_head(&ch->ch_session->ss_task_list);
4171bdd6c0eSSue Gleeson 		while (iu != NULL) {
4181bdd6c0eSSue Gleeson 			next = list_next(&ch->ch_session->ss_task_list, iu);
4191bdd6c0eSSue Gleeson 
4201bdd6c0eSSue Gleeson 			mutex_enter(&iu->iu_lock);
4211bdd6c0eSSue Gleeson 			if (ch == iu->iu_ch) {
4221bdd6c0eSSue Gleeson 				if (iu->iu_stmf_task == NULL) {
4231bdd6c0eSSue Gleeson 					cmn_err(CE_NOTE,
4241bdd6c0eSSue Gleeson 					    "ch_cleanup, NULL stmf task");
4251bdd6c0eSSue Gleeson 					ASSERT(0);
4261bdd6c0eSSue Gleeson 				}
4271bdd6c0eSSue Gleeson 				iutask = iu->iu_stmf_task;
4281bdd6c0eSSue Gleeson 			} else {
4291bdd6c0eSSue Gleeson 				iutask = NULL;
4301bdd6c0eSSue Gleeson 			}
4311bdd6c0eSSue Gleeson 			mutex_exit(&iu->iu_lock);
4321bdd6c0eSSue Gleeson 
4331bdd6c0eSSue Gleeson 			if (iutask != NULL) {
4341bdd6c0eSSue Gleeson 				SRPT_DPRINTF_L4("ch_cleanup, aborting "
4351bdd6c0eSSue Gleeson 				    "task(%p)", (void *)iutask);
4361bdd6c0eSSue Gleeson 				stmf_abort(STMF_QUEUE_TASK_ABORT, iutask,
4371bdd6c0eSSue Gleeson 				    STMF_ABORTED, NULL);
4381bdd6c0eSSue Gleeson 			}
4391bdd6c0eSSue Gleeson 			iu = next;
4401bdd6c0eSSue Gleeson 		}
4411bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_session->ss_rwlock);
4421bdd6c0eSSue Gleeson 	}
4431bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
4441bdd6c0eSSue Gleeson 
4451bdd6c0eSSue Gleeson 	srpt_ch_release_ref(ch, 0);
4461bdd6c0eSSue Gleeson }
4471bdd6c0eSSue Gleeson 
4481bdd6c0eSSue Gleeson /*
4491bdd6c0eSSue Gleeson  * srpt_ch_rsp_comp()
4501bdd6c0eSSue Gleeson  *
4511bdd6c0eSSue Gleeson  * Process a completion for an IB SEND message.  A SEND completion
4521bdd6c0eSSue Gleeson  * is for a SRP response packet sent back to the initiator.  It
4531bdd6c0eSSue Gleeson  * will not have a STMF SCSI task associated with it if it was
4541bdd6c0eSSue Gleeson  * sent for a rejected IU, or was a task management abort response.
4551bdd6c0eSSue Gleeson  */
4561bdd6c0eSSue Gleeson static void
srpt_ch_rsp_comp(srpt_channel_t * ch,srpt_iu_t * iu,ibt_wc_status_t wc_status)4571bdd6c0eSSue Gleeson srpt_ch_rsp_comp(srpt_channel_t *ch, srpt_iu_t *iu,
4581bdd6c0eSSue Gleeson 	ibt_wc_status_t wc_status)
4591bdd6c0eSSue Gleeson {
4606527ba51SSue Gleeson 	stmf_status_t	st = STMF_SUCCESS;
4616527ba51SSue Gleeson 
4621bdd6c0eSSue Gleeson 	ASSERT(iu->iu_ch == ch);
4631bdd6c0eSSue Gleeson 
4641bdd6c0eSSue Gleeson 	/*
4656527ba51SSue Gleeson 	 * Process the completion regardless whether it's a failure or
4666527ba51SSue Gleeson 	 * success.  At this point, we've processed as far as we can and
4676527ba51SSue Gleeson 	 * just need to complete the associated task.
4681bdd6c0eSSue Gleeson 	 */
4696527ba51SSue Gleeson 
4701bdd6c0eSSue Gleeson 	if (wc_status != IBT_SUCCESS) {
4711bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_rsp_comp, WC status err(%d)",
4721bdd6c0eSSue Gleeson 		    wc_status);
4736527ba51SSue Gleeson 
4746527ba51SSue Gleeson 		st = STMF_FAILURE;
4751bdd6c0eSSue Gleeson 
4761bdd6c0eSSue Gleeson 		if (wc_status != IBT_WC_WR_FLUSHED_ERR) {
4771bdd6c0eSSue Gleeson 			srpt_ch_disconnect(ch);
4781bdd6c0eSSue Gleeson 		}
4791bdd6c0eSSue Gleeson 	}
4801bdd6c0eSSue Gleeson 
4811bdd6c0eSSue Gleeson 	/*
4821bdd6c0eSSue Gleeson 	 * If the IU response completion is not associated with
4831bdd6c0eSSue Gleeson 	 * with a SCSI task, release the IU to return the resource
4841bdd6c0eSSue Gleeson 	 * and the reference to the channel it holds.
4851bdd6c0eSSue Gleeson 	 */
4861bdd6c0eSSue Gleeson 	mutex_enter(&iu->iu_lock);
4871bdd6c0eSSue Gleeson 	atomic_dec_32(&iu->iu_sq_posted_cnt);
4881bdd6c0eSSue Gleeson 
4891bdd6c0eSSue Gleeson 	if (iu->iu_stmf_task == NULL) {
4901bdd6c0eSSue Gleeson 		srpt_ioc_repost_recv_iu(iu->iu_ioc, iu);
4911bdd6c0eSSue Gleeson 		mutex_exit(&iu->iu_lock);
4921bdd6c0eSSue Gleeson 		srpt_ch_release_ref(ch, 0);
4931bdd6c0eSSue Gleeson 		return;
4941bdd6c0eSSue Gleeson 	}
4951bdd6c0eSSue Gleeson 
4961bdd6c0eSSue Gleeson 	/*
4971bdd6c0eSSue Gleeson 	 * We should not get a SEND completion where the task has already
4981bdd6c0eSSue Gleeson 	 * completed aborting and STMF has been informed.
4991bdd6c0eSSue Gleeson 	 */
5001bdd6c0eSSue Gleeson 	ASSERT((iu->iu_flags & SRPT_IU_ABORTED) == 0);
5011bdd6c0eSSue Gleeson 
5021bdd6c0eSSue Gleeson 	/*
5031bdd6c0eSSue Gleeson 	 * Let STMF know we are done.
5041bdd6c0eSSue Gleeson 	 */
5051bdd6c0eSSue Gleeson 	mutex_exit(&iu->iu_lock);
5061bdd6c0eSSue Gleeson 
5076527ba51SSue Gleeson 	stmf_send_status_done(iu->iu_stmf_task, st, STMF_IOF_LPORT_DONE);
5081bdd6c0eSSue Gleeson }
5091bdd6c0eSSue Gleeson 
5101bdd6c0eSSue Gleeson /*
5111bdd6c0eSSue Gleeson  * srpt_ch_data_comp()
5121bdd6c0eSSue Gleeson  *
5131bdd6c0eSSue Gleeson  * Process an IB completion for a RDMA operation.  This completion
5141bdd6c0eSSue Gleeson  * should be associated with the last RDMA operation for any
5151bdd6c0eSSue Gleeson  * data buffer transfer.
5161bdd6c0eSSue Gleeson  */
5171bdd6c0eSSue Gleeson static void
srpt_ch_data_comp(srpt_channel_t * ch,stmf_data_buf_t * stmf_dbuf,ibt_wc_status_t wc_status)5181bdd6c0eSSue Gleeson srpt_ch_data_comp(srpt_channel_t *ch, stmf_data_buf_t *stmf_dbuf,
5191bdd6c0eSSue Gleeson 	ibt_wc_status_t wc_status)
5201bdd6c0eSSue Gleeson {
5211bdd6c0eSSue Gleeson 	srpt_ds_dbuf_t		*dbuf;
5221bdd6c0eSSue Gleeson 	srpt_iu_t		*iu;
5231bdd6c0eSSue Gleeson 	stmf_status_t		status;
5241bdd6c0eSSue Gleeson 
5251bdd6c0eSSue Gleeson 	ASSERT(stmf_dbuf != NULL);
5261bdd6c0eSSue Gleeson 
5271bdd6c0eSSue Gleeson 	dbuf = (srpt_ds_dbuf_t *)stmf_dbuf->db_port_private;
5281bdd6c0eSSue Gleeson 
5291bdd6c0eSSue Gleeson 	ASSERT(dbuf != NULL);
5301bdd6c0eSSue Gleeson 
5311bdd6c0eSSue Gleeson 	iu = dbuf->db_iu;
5321bdd6c0eSSue Gleeson 
5331bdd6c0eSSue Gleeson 	ASSERT(iu != NULL);
5341bdd6c0eSSue Gleeson 	ASSERT(iu->iu_ch == ch);
5351bdd6c0eSSue Gleeson 
5361bdd6c0eSSue Gleeson 	/*
5371bdd6c0eSSue Gleeson 	 * If work completion indicates non-flush failure, then
5381bdd6c0eSSue Gleeson 	 * start a channel disconnect (asynchronous) and release
5391bdd6c0eSSue Gleeson 	 * the reference to the IU.  The task will be cleaned
5401bdd6c0eSSue Gleeson 	 * up with STMF during channel shutdown processing.
5411bdd6c0eSSue Gleeson 	 */
5421bdd6c0eSSue Gleeson 	if (wc_status != IBT_SUCCESS) {
5431bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_data_comp, WC status err(%d)",
5441bdd6c0eSSue Gleeson 		    wc_status);
5451bdd6c0eSSue Gleeson 		if (wc_status != IBT_WC_WR_FLUSHED_ERR) {
5461bdd6c0eSSue Gleeson 			srpt_ch_disconnect(ch);
5471bdd6c0eSSue Gleeson 		}
5481bdd6c0eSSue Gleeson 		atomic_dec_32(&iu->iu_sq_posted_cnt);
5491bdd6c0eSSue Gleeson 		return;
5501bdd6c0eSSue Gleeson 	}
5511bdd6c0eSSue Gleeson 
5521bdd6c0eSSue Gleeson 	/*
5531bdd6c0eSSue Gleeson 	 * If STMF has requested this task be aborted, then if this is the
5541bdd6c0eSSue Gleeson 	 * last I/O operation outstanding, notify STMF the task has been
5551bdd6c0eSSue Gleeson 	 *  aborted and ignore the completion.
5561bdd6c0eSSue Gleeson 	 */
5571bdd6c0eSSue Gleeson 	mutex_enter(&iu->iu_lock);
5581bdd6c0eSSue Gleeson 	atomic_dec_32(&iu->iu_sq_posted_cnt);
5591bdd6c0eSSue Gleeson 
5601bdd6c0eSSue Gleeson 	if ((iu->iu_flags & SRPT_IU_STMF_ABORTING) != 0) {
5611bdd6c0eSSue Gleeson 		scsi_task_t	*abort_task = iu->iu_stmf_task;
5621bdd6c0eSSue Gleeson 
5631bdd6c0eSSue Gleeson 		mutex_exit(&iu->iu_lock);
5641bdd6c0eSSue Gleeson 		stmf_abort(STMF_REQUEUE_TASK_ABORT_LPORT, abort_task,
5651bdd6c0eSSue Gleeson 		    STMF_ABORTED, NULL);
5661bdd6c0eSSue Gleeson 		return;
5671bdd6c0eSSue Gleeson 	}
5681bdd6c0eSSue Gleeson 
5691bdd6c0eSSue Gleeson 	/*
5701bdd6c0eSSue Gleeson 	 * We should not get an RDMA completion where the task has already
5711bdd6c0eSSue Gleeson 	 * completed aborting and STMF has been informed.
5721bdd6c0eSSue Gleeson 	 */
5731bdd6c0eSSue Gleeson 	ASSERT((iu->iu_flags & SRPT_IU_ABORTED) == 0);
5741bdd6c0eSSue Gleeson 
5751bdd6c0eSSue Gleeson 	/*
5761bdd6c0eSSue Gleeson 	 * Good completion for last RDMA op associated with a data buffer
5771bdd6c0eSSue Gleeson 	 * I/O, if specified initiate status otherwise let STMF know we are
5781bdd6c0eSSue Gleeson 	 * done.
5791bdd6c0eSSue Gleeson 	 */
5801bdd6c0eSSue Gleeson 	stmf_dbuf->db_xfer_status = STMF_SUCCESS;
5811bdd6c0eSSue Gleeson 	mutex_exit(&iu->iu_lock);
5827830165bSCharles Ting 
5837830165bSCharles Ting 	DTRACE_SRP_8(xfer__done, srpt_channel_t, ch,
5847830165bSCharles Ting 	    ibt_wr_ds_t, &(dbuf->db_sge), srpt_iu_t, iu,
5857830165bSCharles Ting 	    ibt_send_wr_t, 0, uint32_t, stmf_dbuf->db_data_size,
5867830165bSCharles Ting 	    uint32_t, 0, uint32_t, 0,
5877830165bSCharles Ting 	    uint32_t, (stmf_dbuf->db_flags & DB_DIRECTION_TO_RPORT) ? 1 : 0);
5887830165bSCharles Ting 
5891bdd6c0eSSue Gleeson 	if ((stmf_dbuf->db_flags & DB_SEND_STATUS_GOOD) != 0) {
5901bdd6c0eSSue Gleeson 		status = srpt_stp_send_status(dbuf->db_iu->iu_stmf_task, 0);
5911bdd6c0eSSue Gleeson 		if (status == STMF_SUCCESS) {
5921bdd6c0eSSue Gleeson 			return;
5931bdd6c0eSSue Gleeson 		}
5941bdd6c0eSSue Gleeson 		stmf_dbuf->db_xfer_status = STMF_FAILURE;
5951bdd6c0eSSue Gleeson 	}
5961bdd6c0eSSue Gleeson 	stmf_data_xfer_done(dbuf->db_iu->iu_stmf_task, stmf_dbuf, 0);
5971bdd6c0eSSue Gleeson }
5981bdd6c0eSSue Gleeson 
5991bdd6c0eSSue Gleeson /*
6001bdd6c0eSSue Gleeson  * srpt_ch_scq_hdlr()
6011bdd6c0eSSue Gleeson  */
6021bdd6c0eSSue Gleeson static void
srpt_ch_scq_hdlr(ibt_cq_hdl_t cq_hdl,void * arg)6031bdd6c0eSSue Gleeson srpt_ch_scq_hdlr(ibt_cq_hdl_t cq_hdl, void *arg)
6041bdd6c0eSSue Gleeson {
6051bdd6c0eSSue Gleeson 	ibt_status_t		status;
6061bdd6c0eSSue Gleeson 	srpt_channel_t		*ch = arg;
6071bdd6c0eSSue Gleeson 	ibt_wc_t		wc[SRPT_SEND_WC_POLL_SIZE];
6081bdd6c0eSSue Gleeson 	ibt_wc_t		*wcp;
6091bdd6c0eSSue Gleeson 	int			i;
6101bdd6c0eSSue Gleeson 	uint32_t		cq_rearmed = 0;
6111bdd6c0eSSue Gleeson 	uint32_t		entries;
6121bdd6c0eSSue Gleeson 	srpt_swqe_t		*swqe;
6131bdd6c0eSSue Gleeson 
6141bdd6c0eSSue Gleeson 	ASSERT(ch != NULL);
6151bdd6c0eSSue Gleeson 
6161bdd6c0eSSue Gleeson 	/* Reference channel for the duration of this call */
6171bdd6c0eSSue Gleeson 	srpt_ch_add_ref(ch);
6181bdd6c0eSSue Gleeson 
6191bdd6c0eSSue Gleeson 	for (;;) {
6201bdd6c0eSSue Gleeson 		status = ibt_poll_cq(cq_hdl, &wc[0], SRPT_SEND_WC_POLL_SIZE,
6211bdd6c0eSSue Gleeson 		    &entries);
62228406608SSue Gleeson 
62328406608SSue Gleeson 		if (status != IBT_SUCCESS) {
62428406608SSue Gleeson 			if (status != IBT_CQ_EMPTY) {
62528406608SSue Gleeson 				/*
62628406608SSue Gleeson 				 * This error should not happen. It indicates
62728406608SSue Gleeson 				 * something abnormal has gone wrong and means
62828406608SSue Gleeson 				 * either a hardware or programming logic error.
62928406608SSue Gleeson 				 */
63028406608SSue Gleeson 				SRPT_DPRINTF_L2(
63128406608SSue Gleeson 				    "ch_scq_hdlr, unexpected CQ err(%d)",
63228406608SSue Gleeson 				    status);
63328406608SSue Gleeson 				srpt_ch_disconnect(ch);
63428406608SSue Gleeson 			}
63528406608SSue Gleeson 
6361bdd6c0eSSue Gleeson 			/*
63728406608SSue Gleeson 			 * If we have not rearmed the CQ do so now and poll to
63828406608SSue Gleeson 			 * eliminate race; otherwise we are done.
6391bdd6c0eSSue Gleeson 			 */
6401bdd6c0eSSue Gleeson 			if (cq_rearmed == 0) {
641aedf2b3bSsrivijitha dugganapalli 				(void) ibt_enable_cq_notify(ch->ch_scq_hdl,
6421bdd6c0eSSue Gleeson 				    IBT_NEXT_COMPLETION);
6431bdd6c0eSSue Gleeson 				cq_rearmed = 1;
6441bdd6c0eSSue Gleeson 				continue;
6451bdd6c0eSSue Gleeson 			} else {
6461bdd6c0eSSue Gleeson 				break;
6471bdd6c0eSSue Gleeson 			}
6481bdd6c0eSSue Gleeson 		}
6491bdd6c0eSSue Gleeson 
6501bdd6c0eSSue Gleeson 		for (wcp = wc, i = 0; i < entries; i++, wcp++) {
6511bdd6c0eSSue Gleeson 
6521bdd6c0eSSue Gleeson 			/*
6531bdd6c0eSSue Gleeson 			 * A zero work ID indicates this CQE is associated
6541bdd6c0eSSue Gleeson 			 * with an intermediate post of a RDMA data transfer
6551bdd6c0eSSue Gleeson 			 * operation.  Since intermediate data requests are
6561bdd6c0eSSue Gleeson 			 * unsignaled, we should only get these if there was
6571bdd6c0eSSue Gleeson 			 * an error.  No action is required.
6581bdd6c0eSSue Gleeson 			 */
6591bdd6c0eSSue Gleeson 			if (wcp->wc_id == 0) {
6601bdd6c0eSSue Gleeson 				continue;
6611bdd6c0eSSue Gleeson 			}
6621bdd6c0eSSue Gleeson 			swqe = ch->ch_swqe + wcp->wc_id;
6631bdd6c0eSSue Gleeson 
6641bdd6c0eSSue Gleeson 			switch (swqe->sw_type) {
6651bdd6c0eSSue Gleeson 			case SRPT_SWQE_TYPE_RESP:
6661bdd6c0eSSue Gleeson 				srpt_ch_rsp_comp(ch, (srpt_iu_t *)
6671bdd6c0eSSue Gleeson 				    swqe->sw_addr, wcp->wc_status);
6681bdd6c0eSSue Gleeson 				break;
6691bdd6c0eSSue Gleeson 
6701bdd6c0eSSue Gleeson 			case SRPT_SWQE_TYPE_DATA:
6711bdd6c0eSSue Gleeson 				srpt_ch_data_comp(ch, (stmf_data_buf_t *)
6721bdd6c0eSSue Gleeson 				    swqe->sw_addr, wcp->wc_status);
6731bdd6c0eSSue Gleeson 				break;
6741bdd6c0eSSue Gleeson 
6751bdd6c0eSSue Gleeson 			default:
6761bdd6c0eSSue Gleeson 				SRPT_DPRINTF_L2("ch_scq_hdlr, bad type(%d)",
6771bdd6c0eSSue Gleeson 				    swqe->sw_type);
6781bdd6c0eSSue Gleeson 				ASSERT(0);
6791bdd6c0eSSue Gleeson 			}
6801bdd6c0eSSue Gleeson 
6811bdd6c0eSSue Gleeson 			srpt_ch_free_swqe_wrid(ch, wcp->wc_id);
6821bdd6c0eSSue Gleeson 		}
6831bdd6c0eSSue Gleeson 	}
6841bdd6c0eSSue Gleeson 
6851bdd6c0eSSue Gleeson 	srpt_ch_release_ref(ch, 0);
6861bdd6c0eSSue Gleeson }
6871bdd6c0eSSue Gleeson 
6881bdd6c0eSSue Gleeson /*
6891bdd6c0eSSue Gleeson  * srpt_ch_rcq_hdlr()
6901bdd6c0eSSue Gleeson  */
6911bdd6c0eSSue Gleeson static void
srpt_ch_rcq_hdlr(ibt_cq_hdl_t cq_hdl,void * arg)6921bdd6c0eSSue Gleeson srpt_ch_rcq_hdlr(ibt_cq_hdl_t cq_hdl, void *arg)
6931bdd6c0eSSue Gleeson {
6941bdd6c0eSSue Gleeson 	ibt_status_t		status;
6951bdd6c0eSSue Gleeson 	srpt_channel_t		*ch = arg;
6961bdd6c0eSSue Gleeson 	ibt_wc_t		wc[SRPT_RECV_WC_POLL_SIZE];
6971bdd6c0eSSue Gleeson 	ibt_wc_t		*wcp;
6981bdd6c0eSSue Gleeson 	int			i;
6991bdd6c0eSSue Gleeson 	uint32_t		entries;
7001bdd6c0eSSue Gleeson 	srpt_iu_t		*iu;
7011bdd6c0eSSue Gleeson 	uint_t			cq_rearmed = 0;
7021bdd6c0eSSue Gleeson 
7031bdd6c0eSSue Gleeson 	/*
7041bdd6c0eSSue Gleeson 	 * The channel object will exists while the CQ handler call-back
7051bdd6c0eSSue Gleeson 	 * is installed.
7061bdd6c0eSSue Gleeson 	 */
7071bdd6c0eSSue Gleeson 	ASSERT(ch != NULL);
7081bdd6c0eSSue Gleeson 	srpt_ch_add_ref(ch);
7091bdd6c0eSSue Gleeson 
7101bdd6c0eSSue Gleeson 	/*
7111bdd6c0eSSue Gleeson 	 * If we know a channel disconnect has started do nothing
7121bdd6c0eSSue Gleeson 	 * and let channel cleanup code recover resources from the CQ.
7131bdd6c0eSSue Gleeson 	 * We are not concerned about races with the state transition
7141bdd6c0eSSue Gleeson 	 * since the code will do the correct thing either way. This
7151bdd6c0eSSue Gleeson 	 * is simply to circumvent rearming the CQ, and it will
7161bdd6c0eSSue Gleeson 	 * catch the state next time.
7171bdd6c0eSSue Gleeson 	 */
7181bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_READER);
7191bdd6c0eSSue Gleeson 	if (ch->ch_state == SRPT_CHANNEL_DISCONNECTING) {
7201bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_rcq_hdlr, channel disconnecting");
7211bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
7221bdd6c0eSSue Gleeson 		srpt_ch_release_ref(ch, 0);
7231bdd6c0eSSue Gleeson 		return;
7241bdd6c0eSSue Gleeson 	}
7251bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
7261bdd6c0eSSue Gleeson 
7271bdd6c0eSSue Gleeson 	for (;;) {
7281bdd6c0eSSue Gleeson 		status = ibt_poll_cq(cq_hdl, &wc[0], SRPT_RECV_WC_POLL_SIZE,
7291bdd6c0eSSue Gleeson 		    &entries);
73028406608SSue Gleeson 
73128406608SSue Gleeson 		if (status != IBT_SUCCESS) {
73228406608SSue Gleeson 			if (status != IBT_CQ_EMPTY) {
73328406608SSue Gleeson 				/*
73428406608SSue Gleeson 				 * This error should not happen. It indicates
73528406608SSue Gleeson 				 * something abnormal has gone wrong and means
73628406608SSue Gleeson 				 * either a hardware or programming logic error.
73728406608SSue Gleeson 				 */
73828406608SSue Gleeson 				SRPT_DPRINTF_L2(
73928406608SSue Gleeson 				    "ch_rcq_hdlr, unexpected CQ err(%d)",
74028406608SSue Gleeson 				    status);
74128406608SSue Gleeson 				srpt_ch_disconnect(ch);
74228406608SSue Gleeson 				break;
74328406608SSue Gleeson 			}
74428406608SSue Gleeson 
7451bdd6c0eSSue Gleeson 			/*
74628406608SSue Gleeson 			 * If we have not rearmed the CQ do so now and poll to
74728406608SSue Gleeson 			 * eliminate race; otherwise we are done.
7481bdd6c0eSSue Gleeson 			 */
7491bdd6c0eSSue Gleeson 			if (cq_rearmed == 0) {
750aedf2b3bSsrivijitha dugganapalli 				(void) ibt_enable_cq_notify(ch->ch_rcq_hdl,
7511bdd6c0eSSue Gleeson 				    IBT_NEXT_COMPLETION);
7521bdd6c0eSSue Gleeson 				cq_rearmed = 1;
7531bdd6c0eSSue Gleeson 				continue;
7541bdd6c0eSSue Gleeson 			} else {
7551bdd6c0eSSue Gleeson 				break;
7561bdd6c0eSSue Gleeson 			}
7571bdd6c0eSSue Gleeson 		}
7581bdd6c0eSSue Gleeson 
7591bdd6c0eSSue Gleeson 		for (wcp = wc, i = 0; i < entries; i++, wcp++) {
7601bdd6c0eSSue Gleeson 
7611bdd6c0eSSue Gleeson 			/*
7621bdd6c0eSSue Gleeson 			 *  Check wc_status before proceeding.  If the
7631bdd6c0eSSue Gleeson 			 *  status indicates a channel problem, stop processing.
7641bdd6c0eSSue Gleeson 			 */
7651bdd6c0eSSue Gleeson 			if (wcp->wc_status != IBT_WC_SUCCESS) {
7661bdd6c0eSSue Gleeson 				if (wcp->wc_status == IBT_WC_WR_FLUSHED_ERR) {
7671bdd6c0eSSue Gleeson 					SRPT_DPRINTF_L2(
7681bdd6c0eSSue Gleeson 					    "ch_rcq, unexpected"
7691bdd6c0eSSue Gleeson 					    " wc_status err(%d)",
7701bdd6c0eSSue Gleeson 					    wcp->wc_status);
7711bdd6c0eSSue Gleeson 					srpt_ch_disconnect(ch);
7721bdd6c0eSSue Gleeson 					goto done;
7731bdd6c0eSSue Gleeson 				} else {
7741bdd6c0eSSue Gleeson 					/* skip IUs with errors */
7751bdd6c0eSSue Gleeson 					SRPT_DPRINTF_L2(
7761bdd6c0eSSue Gleeson 					    "ch_rcq, ERROR comp(%d)",
7771bdd6c0eSSue Gleeson 					    wcp->wc_status);
7781bdd6c0eSSue Gleeson 					/* XXX - verify not leaking IUs */
7791bdd6c0eSSue Gleeson 					continue;
7801bdd6c0eSSue Gleeson 				}
7811bdd6c0eSSue Gleeson 			}
7821bdd6c0eSSue Gleeson 
7831bdd6c0eSSue Gleeson 			iu = (srpt_iu_t *)(uintptr_t)wcp->wc_id;
7841bdd6c0eSSue Gleeson 			ASSERT(iu != NULL);
7851bdd6c0eSSue Gleeson 
7861bdd6c0eSSue Gleeson 			/*
7871bdd6c0eSSue Gleeson 			 * Process the IU.
7881bdd6c0eSSue Gleeson 			 */
7891bdd6c0eSSue Gleeson 			ASSERT(wcp->wc_type == IBT_WRC_RECV);
7901bdd6c0eSSue Gleeson 			srpt_ch_process_iu(ch, iu);
7911bdd6c0eSSue Gleeson 		}
7921bdd6c0eSSue Gleeson 	}
7931bdd6c0eSSue Gleeson 
7941bdd6c0eSSue Gleeson done:
7951bdd6c0eSSue Gleeson 	srpt_ch_release_ref(ch, 0);
7961bdd6c0eSSue Gleeson }
7971bdd6c0eSSue Gleeson 
7981bdd6c0eSSue Gleeson /*
7991bdd6c0eSSue Gleeson  * srpt_ch_srp_cmd()
8001bdd6c0eSSue Gleeson  */
8011bdd6c0eSSue Gleeson static int
srpt_ch_srp_cmd(srpt_channel_t * ch,srpt_iu_t * iu)8021bdd6c0eSSue Gleeson srpt_ch_srp_cmd(srpt_channel_t *ch, srpt_iu_t *iu)
8031bdd6c0eSSue Gleeson {
8041bdd6c0eSSue Gleeson 	srp_cmd_req_t		*cmd = (srp_cmd_req_t *)iu->iu_buf;
8051bdd6c0eSSue Gleeson 	srp_indirect_desc_t	*i_desc;
8061bdd6c0eSSue Gleeson 	uint_t			i_di_cnt;
8071bdd6c0eSSue Gleeson 	uint_t			i_do_cnt;
8081bdd6c0eSSue Gleeson 	uint8_t			do_fmt;
8091bdd6c0eSSue Gleeson 	uint8_t			di_fmt;
8101bdd6c0eSSue Gleeson 	uint32_t		*cur_desc_off;
8111bdd6c0eSSue Gleeson 	int			i;
8121bdd6c0eSSue Gleeson 	ibt_status_t		status;
8131bdd6c0eSSue Gleeson 	uint8_t			addlen;
8141bdd6c0eSSue Gleeson 
815191c289bSCharles Ting 
816191c289bSCharles Ting 	DTRACE_SRP_2(task__command, srpt_channel_t, ch, srp_cmd_req_t, cmd);
8171bdd6c0eSSue Gleeson 	iu->iu_ch  = ch;
8181bdd6c0eSSue Gleeson 	iu->iu_tag = cmd->cr_tag;
8191bdd6c0eSSue Gleeson 
8201bdd6c0eSSue Gleeson 	/*
8211bdd6c0eSSue Gleeson 	 * The SRP specification and SAM require support for bi-directional
8221bdd6c0eSSue Gleeson 	 * data transfer, so we create a single buffer descriptor list that
8231bdd6c0eSSue Gleeson 	 * in the IU buffer that covers the data-in and data-out buffers.
8241bdd6c0eSSue Gleeson 	 * In practice we will just see unidirectional transfers with either
8251bdd6c0eSSue Gleeson 	 * data-in or data out descriptors.  If we were to take that as fact,
8261bdd6c0eSSue Gleeson 	 * we could reduce overhead slightly.
8271bdd6c0eSSue Gleeson 	 */
8281bdd6c0eSSue Gleeson 
8291bdd6c0eSSue Gleeson 	/*
8301bdd6c0eSSue Gleeson 	 * additional length is a 6-bit number in 4-byte words, so multiply by 4
8311bdd6c0eSSue Gleeson 	 * to get bytes.
8321bdd6c0eSSue Gleeson 	 */
8331bdd6c0eSSue Gleeson 	addlen = cmd->cr_add_cdb_len & 0x3f;	/* mask off 6 bits */
8341bdd6c0eSSue Gleeson 
8351bdd6c0eSSue Gleeson 	cur_desc_off = (uint32_t *)(void *)&cmd->cr_add_data;
8361bdd6c0eSSue Gleeson 	cur_desc_off  += addlen;		/* 32-bit arithmetic */
8371bdd6c0eSSue Gleeson 	iu->iu_num_rdescs = 0;
8381bdd6c0eSSue Gleeson 	iu->iu_rdescs = (srp_direct_desc_t *)(void *)cur_desc_off;
8391bdd6c0eSSue Gleeson 
8401bdd6c0eSSue Gleeson 	/*
8411bdd6c0eSSue Gleeson 	 * Examine buffer description for Data In (i.e. data flows
8421bdd6c0eSSue Gleeson 	 * to the initiator).
8431bdd6c0eSSue Gleeson 	 */
8441bdd6c0eSSue Gleeson 	i_do_cnt = i_di_cnt = 0;
8451bdd6c0eSSue Gleeson 	di_fmt = cmd->cr_buf_fmt >> 4;
8461bdd6c0eSSue Gleeson 	if (di_fmt == SRP_DATA_DESC_DIRECT) {
8471bdd6c0eSSue Gleeson 		iu->iu_num_rdescs = 1;
8481bdd6c0eSSue Gleeson 		cur_desc_off = (uint32_t *)(void *)&iu->iu_rdescs[1];
8491bdd6c0eSSue Gleeson 	} else if (di_fmt == SRP_DATA_DESC_INDIRECT) {
8501bdd6c0eSSue Gleeson 		i_desc = (srp_indirect_desc_t *)iu->iu_rdescs;
8511bdd6c0eSSue Gleeson 		i_di_cnt  = b2h32(i_desc->id_table.dd_len) /
8521bdd6c0eSSue Gleeson 		    sizeof (srp_direct_desc_t);
8531bdd6c0eSSue Gleeson 
8541bdd6c0eSSue Gleeson 		/*
8551bdd6c0eSSue Gleeson 		 * Some initiators like OFED occasionally use the wrong counts,
8561bdd6c0eSSue Gleeson 		 * so check total to allow for this.  NOTE: we do not support
8571bdd6c0eSSue Gleeson 		 * reading of the descriptor table from the initiator, so if
8581bdd6c0eSSue Gleeson 		 * not all descriptors are in the IU we drop the task.
8591bdd6c0eSSue Gleeson 		 */
8601bdd6c0eSSue Gleeson 		if (i_di_cnt > (cmd->cr_dicnt + cmd->cr_docnt)) {
8611bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L2("ch_srp_cmd, remote RDMA of"
8621bdd6c0eSSue Gleeson 			    " descriptors not supported");
8631bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L2("ch_srp_cmd, sizeof entry (%d),"
8641bdd6c0eSSue Gleeson 			    " i_di_cnt(%d), cr_dicnt(%d)",
8651bdd6c0eSSue Gleeson 			    (uint_t)sizeof (srp_direct_desc_t),
8661bdd6c0eSSue Gleeson 			    i_di_cnt, cmd->cr_dicnt);
8671bdd6c0eSSue Gleeson 			iu->iu_rdescs = NULL;
8681bdd6c0eSSue Gleeson 			return (1);
8691bdd6c0eSSue Gleeson 		}
8701bdd6c0eSSue Gleeson 		bcopy(&i_desc->id_desc[0], iu->iu_rdescs,
8711bdd6c0eSSue Gleeson 		    sizeof (srp_direct_desc_t) * i_di_cnt);
8721bdd6c0eSSue Gleeson 		iu->iu_num_rdescs += i_di_cnt;
8731bdd6c0eSSue Gleeson 		cur_desc_off = (uint32_t *)(void *)&i_desc->id_desc[i_di_cnt];
8741bdd6c0eSSue Gleeson 	}
8751bdd6c0eSSue Gleeson 
8761bdd6c0eSSue Gleeson 	/*
8771bdd6c0eSSue Gleeson 	 * Examine buffer description for Data Out (i.e. data flows
8781bdd6c0eSSue Gleeson 	 * from the initiator).
8791bdd6c0eSSue Gleeson 	 */
8801bdd6c0eSSue Gleeson 	do_fmt = cmd->cr_buf_fmt & 0x0F;
8811bdd6c0eSSue Gleeson 	if (do_fmt == SRP_DATA_DESC_DIRECT) {
8821bdd6c0eSSue Gleeson 		if (di_fmt == SRP_DATA_DESC_DIRECT) {
8831bdd6c0eSSue Gleeson 			bcopy(cur_desc_off, &iu->iu_rdescs[iu->iu_num_rdescs],
8841bdd6c0eSSue Gleeson 			    sizeof (srp_direct_desc_t));
8851bdd6c0eSSue Gleeson 		}
8861bdd6c0eSSue Gleeson 		iu->iu_num_rdescs++;
8871bdd6c0eSSue Gleeson 	} else if (do_fmt == SRP_DATA_DESC_INDIRECT) {
8881bdd6c0eSSue Gleeson 		i_desc = (srp_indirect_desc_t *)cur_desc_off;
8891bdd6c0eSSue Gleeson 		i_do_cnt  = b2h32(i_desc->id_table.dd_len) /
8901bdd6c0eSSue Gleeson 		    sizeof (srp_direct_desc_t);
8911bdd6c0eSSue Gleeson 
8921bdd6c0eSSue Gleeson 		/*
8931bdd6c0eSSue Gleeson 		 * Some initiators like OFED occasionally use the wrong counts,
8941bdd6c0eSSue Gleeson 		 * so check total to allow for this.  NOTE: we do not support
8951bdd6c0eSSue Gleeson 		 * reading of the descriptor table from the initiator, so if
8961bdd6c0eSSue Gleeson 		 * not all descriptors are in the IU we drop the task.
8971bdd6c0eSSue Gleeson 		 */
8981bdd6c0eSSue Gleeson 		if ((i_di_cnt + i_do_cnt) > (cmd->cr_dicnt + cmd->cr_docnt)) {
8991bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L2("ch_srp_cmd, remote RDMA of"
9001bdd6c0eSSue Gleeson 			    " descriptors not supported");
9011bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L2("ch_srp_cmd, sizeof entry (%d),"
9021bdd6c0eSSue Gleeson 			    " i_do_cnt(%d), cr_docnt(%d)",
9031bdd6c0eSSue Gleeson 			    (uint_t)sizeof (srp_direct_desc_t),
9041bdd6c0eSSue Gleeson 			    i_do_cnt, cmd->cr_docnt);
9051bdd6c0eSSue Gleeson 			iu->iu_rdescs = 0;
9061bdd6c0eSSue Gleeson 			return (1);
9071bdd6c0eSSue Gleeson 		}
9081bdd6c0eSSue Gleeson 		bcopy(&i_desc->id_desc[0], &iu->iu_rdescs[iu->iu_num_rdescs],
9091bdd6c0eSSue Gleeson 		    sizeof (srp_direct_desc_t) * i_do_cnt);
9101bdd6c0eSSue Gleeson 		iu->iu_num_rdescs += i_do_cnt;
9111bdd6c0eSSue Gleeson 	}
9121bdd6c0eSSue Gleeson 
9131bdd6c0eSSue Gleeson 	iu->iu_tot_xfer_len = 0;
9141bdd6c0eSSue Gleeson 	for (i = 0; i < iu->iu_num_rdescs; i++) {
9151bdd6c0eSSue Gleeson 		iu->iu_rdescs[i].dd_vaddr = b2h64(iu->iu_rdescs[i].dd_vaddr);
9161bdd6c0eSSue Gleeson 		iu->iu_rdescs[i].dd_hdl   = b2h32(iu->iu_rdescs[i].dd_hdl);
9171bdd6c0eSSue Gleeson 		iu->iu_rdescs[i].dd_len   = b2h32(iu->iu_rdescs[i].dd_len);
9181bdd6c0eSSue Gleeson 		iu->iu_tot_xfer_len += iu->iu_rdescs[i].dd_len;
9191bdd6c0eSSue Gleeson 	}
9201bdd6c0eSSue Gleeson 
9211bdd6c0eSSue Gleeson #ifdef DEBUG
9221bdd6c0eSSue Gleeson 	if (srpt_errlevel >= SRPT_LOG_L4) {
9231bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L4("ch_srp_cmd, iu->iu_tot_xfer_len (%d)",
9241bdd6c0eSSue Gleeson 		    iu->iu_tot_xfer_len);
9251bdd6c0eSSue Gleeson 		for (i = 0; i < iu->iu_num_rdescs; i++) {
9261bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L4("ch_srp_cmd, rdescs[%d].dd_vaddr"
9271bdd6c0eSSue Gleeson 			    " (0x%08llx)",
9281bdd6c0eSSue Gleeson 			    i, (u_longlong_t)iu->iu_rdescs[i].dd_vaddr);
9291bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L4("ch_srp_cmd, rdescs[%d].dd_hdl"
9301bdd6c0eSSue Gleeson 			    " (0x%08x)", i, iu->iu_rdescs[i].dd_hdl);
9311bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L4("ch_srp_cmd, rdescs[%d].dd_len (%d)",
9321bdd6c0eSSue Gleeson 			    i, iu->iu_rdescs[i].dd_len);
9331bdd6c0eSSue Gleeson 		}
9341bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L4("ch_srp_cmd, LUN (0x%08lx)",
9351bdd6c0eSSue Gleeson 		    (unsigned long int) *((uint64_t *)(void *) cmd->cr_lun));
9361bdd6c0eSSue Gleeson 	}
9371bdd6c0eSSue Gleeson #endif
9381bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_READER);
9391bdd6c0eSSue Gleeson 
9401bdd6c0eSSue Gleeson 	if (ch->ch_state == SRPT_CHANNEL_DISCONNECTING) {
9411bdd6c0eSSue Gleeson 		/*
9421bdd6c0eSSue Gleeson 		 * The channel has begun disconnecting, so ignore the
9431bdd6c0eSSue Gleeson 		 * the command returning the IU resources.
9441bdd6c0eSSue Gleeson 		 */
9451bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
9461bdd6c0eSSue Gleeson 		return (1);
9471bdd6c0eSSue Gleeson 	}
9481bdd6c0eSSue Gleeson 
9491bdd6c0eSSue Gleeson 	/*
9501bdd6c0eSSue Gleeson 	 * Once a SCSI task is allocated and assigned to the IU, it
9511bdd6c0eSSue Gleeson 	 * owns those IU resources, which will be held until STMF
9521bdd6c0eSSue Gleeson 	 * is notified the task is done (from a lport perspective).
9531bdd6c0eSSue Gleeson 	 */
9541bdd6c0eSSue Gleeson 	iu->iu_stmf_task = stmf_task_alloc(ch->ch_tgt->tp_lport,
9551bdd6c0eSSue Gleeson 	    ch->ch_session->ss_ss, cmd->cr_lun,
9561bdd6c0eSSue Gleeson 	    SRP_CDB_SIZE + (addlen * 4), 0);
9571bdd6c0eSSue Gleeson 	if (iu->iu_stmf_task == NULL) {
9581bdd6c0eSSue Gleeson 		/*
9591bdd6c0eSSue Gleeson 		 * Could not allocate, return status to the initiator
9601bdd6c0eSSue Gleeson 		 * indicating that we are temporarily unable to process
9611bdd6c0eSSue Gleeson 		 * commands.  If unable to send, immediately return IU
9621bdd6c0eSSue Gleeson 		 * resource.
9631bdd6c0eSSue Gleeson 		 */
9641bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_srp_cmd, SCSI task allocation failure");
9651bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
9661bdd6c0eSSue Gleeson 		mutex_enter(&iu->iu_lock);
9671bdd6c0eSSue Gleeson 		status = srpt_stp_send_response(iu, STATUS_BUSY, 0, 0, 0,
9681bdd6c0eSSue Gleeson 		    NULL, SRPT_NO_FENCE_SEND);
9691bdd6c0eSSue Gleeson 		mutex_exit(&iu->iu_lock);
9701bdd6c0eSSue Gleeson 		if (status != IBT_SUCCESS) {
9711bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L2("ch_srp_cmd, error(%d) posting error"
9721bdd6c0eSSue Gleeson 			    " response", status);
9731bdd6c0eSSue Gleeson 			return (1);
9741bdd6c0eSSue Gleeson 		} else {
9751bdd6c0eSSue Gleeson 			return (0);
9761bdd6c0eSSue Gleeson 		}
9771bdd6c0eSSue Gleeson 	}
9781bdd6c0eSSue Gleeson 
9791bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_port_private = iu;
9801bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_flags = 0;
9811bdd6c0eSSue Gleeson 
9821bdd6c0eSSue Gleeson 	if (di_fmt != 0) {
9831bdd6c0eSSue Gleeson 		iu->iu_stmf_task->task_flags |= TF_WRITE_DATA;
9841bdd6c0eSSue Gleeson 	}
9851bdd6c0eSSue Gleeson 	if (do_fmt != 0) {
9861bdd6c0eSSue Gleeson 		iu->iu_stmf_task->task_flags |= TF_READ_DATA;
9871bdd6c0eSSue Gleeson 	}
9881bdd6c0eSSue Gleeson 
9891bdd6c0eSSue Gleeson 	switch (cmd->cr_task_attr) {
9901bdd6c0eSSue Gleeson 	case SRP_TSK_ATTR_QTYPE_SIMPLE:
9911bdd6c0eSSue Gleeson 		iu->iu_stmf_task->task_flags |=	TF_ATTR_SIMPLE_QUEUE;
9921bdd6c0eSSue Gleeson 		break;
9931bdd6c0eSSue Gleeson 
9941bdd6c0eSSue Gleeson 	case SRP_TSK_ATTR_QTYPE_HEAD_OF_Q:
9951bdd6c0eSSue Gleeson 		iu->iu_stmf_task->task_flags |=	TF_ATTR_HEAD_OF_QUEUE;
9961bdd6c0eSSue Gleeson 		break;
9971bdd6c0eSSue Gleeson 
9981bdd6c0eSSue Gleeson 	case SRP_TSK_ATTR_QTYPE_ORDERED:
9991bdd6c0eSSue Gleeson 		iu->iu_stmf_task->task_flags |=	TF_ATTR_ORDERED_QUEUE;
10001bdd6c0eSSue Gleeson 		break;
10011bdd6c0eSSue Gleeson 
10021bdd6c0eSSue Gleeson 	case SRP_TSK_ATTR_QTYPE_ACA_Q_TAG:
10031bdd6c0eSSue Gleeson 		iu->iu_stmf_task->task_flags |=	TF_ATTR_ACA;
10041bdd6c0eSSue Gleeson 		break;
10051bdd6c0eSSue Gleeson 
10061bdd6c0eSSue Gleeson 	default:
10071bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_srp_cmd, reserved task attr (%d)",
10081bdd6c0eSSue Gleeson 		    cmd->cr_task_attr);
10091bdd6c0eSSue Gleeson 		iu->iu_stmf_task->task_flags |=	TF_ATTR_ORDERED_QUEUE;
10101bdd6c0eSSue Gleeson 		break;
10111bdd6c0eSSue Gleeson 	}
10121bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_additional_flags = 0;
10131bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_priority		= 0;
10141bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_mgmt_function    = TM_NONE;
10151bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_max_nbufs	= STMF_BUFS_MAX;
10161bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_expected_xfer_length = iu->iu_tot_xfer_len;
10171bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_csn_size		= 0;
10181bdd6c0eSSue Gleeson 
10191bdd6c0eSSue Gleeson 	bcopy(cmd->cr_cdb, iu->iu_stmf_task->task_cdb,
10201bdd6c0eSSue Gleeson 	    SRP_CDB_SIZE);
10211bdd6c0eSSue Gleeson 	if (addlen != 0) {
10221bdd6c0eSSue Gleeson 		bcopy(&cmd->cr_add_data,
10231bdd6c0eSSue Gleeson 		    iu->iu_stmf_task->task_cdb + SRP_CDB_SIZE,
10241bdd6c0eSSue Gleeson 		    addlen * 4);
10251bdd6c0eSSue Gleeson 	}
10261bdd6c0eSSue Gleeson 
10271bdd6c0eSSue Gleeson 	/*
10281bdd6c0eSSue Gleeson 	 * Add the IU/task to the session and post to STMF.  The task will
10291bdd6c0eSSue Gleeson 	 * remain in the session's list until STMF is informed by SRP that
10301bdd6c0eSSue Gleeson 	 * it is done with the task.
10311bdd6c0eSSue Gleeson 	 */
1032191c289bSCharles Ting 	DTRACE_SRP_3(scsi__command, srpt_channel_t, iu->iu_ch,
1033191c289bSCharles Ting 	    scsi_task_t, iu->iu_stmf_task, srp_cmd_req_t, cmd);
10341bdd6c0eSSue Gleeson 	srpt_stp_add_task(ch->ch_session, iu);
10351bdd6c0eSSue Gleeson 
10361bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L3("ch_srp_cmd, new task (%p) posted",
10371bdd6c0eSSue Gleeson 	    (void *)iu->iu_stmf_task);
10381bdd6c0eSSue Gleeson 	stmf_post_task(iu->iu_stmf_task, NULL);
10391bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
10401bdd6c0eSSue Gleeson 
10411bdd6c0eSSue Gleeson 	return (0);
10421bdd6c0eSSue Gleeson }
10431bdd6c0eSSue Gleeson 
10441bdd6c0eSSue Gleeson /*
10451bdd6c0eSSue Gleeson  * srpt_ch_task_mgmt_abort()
10461bdd6c0eSSue Gleeson  *
10471bdd6c0eSSue Gleeson  * Returns 0 on success, indicating we've sent a management response.
10481bdd6c0eSSue Gleeson  * Returns !0 to indicate failure; the IU should be reposted.
10491bdd6c0eSSue Gleeson  */
10501bdd6c0eSSue Gleeson static ibt_status_t
srpt_ch_task_mgmt_abort(srpt_channel_t * ch,srpt_iu_t * iu,uint64_t tag_to_abort)10511bdd6c0eSSue Gleeson srpt_ch_task_mgmt_abort(srpt_channel_t *ch, srpt_iu_t *iu,
10521bdd6c0eSSue Gleeson 	uint64_t tag_to_abort)
10531bdd6c0eSSue Gleeson {
10541bdd6c0eSSue Gleeson 	srpt_session_t	*session = ch->ch_session;
10551bdd6c0eSSue Gleeson 	srpt_iu_t	*ss_iu;
10561bdd6c0eSSue Gleeson 	ibt_status_t	status;
10571bdd6c0eSSue Gleeson 
10581bdd6c0eSSue Gleeson 	/*
10591bdd6c0eSSue Gleeson 	 * Locate the associated task (tag_to_abort) in the
10601bdd6c0eSSue Gleeson 	 * session's active task list.
10611bdd6c0eSSue Gleeson 	 */
10621bdd6c0eSSue Gleeson 	rw_enter(&session->ss_rwlock, RW_READER);
10631bdd6c0eSSue Gleeson 	ss_iu = list_head(&session->ss_task_list);
10641bdd6c0eSSue Gleeson 	while (ss_iu != NULL) {
10651bdd6c0eSSue Gleeson 		mutex_enter(&ss_iu->iu_lock);
10661bdd6c0eSSue Gleeson 		if ((tag_to_abort == ss_iu->iu_tag)) {
10671bdd6c0eSSue Gleeson 			mutex_exit(&ss_iu->iu_lock);
10681bdd6c0eSSue Gleeson 			break;
10691bdd6c0eSSue Gleeson 		}
10701bdd6c0eSSue Gleeson 		mutex_exit(&ss_iu->iu_lock);
10711bdd6c0eSSue Gleeson 		ss_iu = list_next(&session->ss_task_list, ss_iu);
10721bdd6c0eSSue Gleeson 	}
10731bdd6c0eSSue Gleeson 	rw_exit(&session->ss_rwlock);
10741bdd6c0eSSue Gleeson 
10751bdd6c0eSSue Gleeson 	/*
10761bdd6c0eSSue Gleeson 	 * Take appropriate action based on state of task
10771bdd6c0eSSue Gleeson 	 * to be aborted:
10781bdd6c0eSSue Gleeson 	 * 1) No longer exists - do nothing.
10791bdd6c0eSSue Gleeson 	 * 2) Previously aborted or status queued - do nothing.
10801bdd6c0eSSue Gleeson 	 * 3) Otherwise - initiate abort.
10811bdd6c0eSSue Gleeson 	 */
10821bdd6c0eSSue Gleeson 	if (ss_iu == NULL)  {
10831bdd6c0eSSue Gleeson 		goto send_mgmt_resp;
10841bdd6c0eSSue Gleeson 	}
10851bdd6c0eSSue Gleeson 
10861bdd6c0eSSue Gleeson 	mutex_enter(&ss_iu->iu_lock);
10871bdd6c0eSSue Gleeson 	if ((ss_iu->iu_flags & (SRPT_IU_STMF_ABORTING |
10881bdd6c0eSSue Gleeson 	    SRPT_IU_ABORTED | SRPT_IU_RESP_SENT)) != 0) {
10891bdd6c0eSSue Gleeson 		mutex_exit(&ss_iu->iu_lock);
10901bdd6c0eSSue Gleeson 		goto send_mgmt_resp;
10911bdd6c0eSSue Gleeson 	}
10921bdd6c0eSSue Gleeson 
10931bdd6c0eSSue Gleeson 	/*
10941bdd6c0eSSue Gleeson 	 * Set aborting flag and notify STMF of abort request.  No
10951bdd6c0eSSue Gleeson 	 * additional I/O will be queued for this IU.
10961bdd6c0eSSue Gleeson 	 */
10971bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L3("ch_task_mgmt_abort, task found");
10981bdd6c0eSSue Gleeson 	ss_iu->iu_flags |= SRPT_IU_SRP_ABORTING;
10991bdd6c0eSSue Gleeson 	mutex_exit(&ss_iu->iu_lock);
11001bdd6c0eSSue Gleeson 	stmf_abort(STMF_QUEUE_TASK_ABORT,
11011bdd6c0eSSue Gleeson 	    ss_iu->iu_stmf_task, STMF_ABORTED, NULL);
11021bdd6c0eSSue Gleeson 
11031bdd6c0eSSue Gleeson send_mgmt_resp:
11041bdd6c0eSSue Gleeson 	mutex_enter(&iu->iu_lock);
11051bdd6c0eSSue Gleeson 	status = srpt_stp_send_mgmt_response(iu, SRP_TM_SUCCESS,
11061bdd6c0eSSue Gleeson 	    SRPT_FENCE_SEND);
11071bdd6c0eSSue Gleeson 	mutex_exit(&iu->iu_lock);
11081bdd6c0eSSue Gleeson 
11091bdd6c0eSSue Gleeson 	if (status != IBT_SUCCESS) {
11101bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_task_mgmt_abort, err(%d)"
11111bdd6c0eSSue Gleeson 		    " posting abort response", status);
11121bdd6c0eSSue Gleeson 	}
11131bdd6c0eSSue Gleeson 
11141bdd6c0eSSue Gleeson 	return (status);
11151bdd6c0eSSue Gleeson }
11161bdd6c0eSSue Gleeson 
11171bdd6c0eSSue Gleeson /*
11181bdd6c0eSSue Gleeson  * srpt_ch_srp_task_mgmt()
11191bdd6c0eSSue Gleeson  */
11201bdd6c0eSSue Gleeson static int
srpt_ch_srp_task_mgmt(srpt_channel_t * ch,srpt_iu_t * iu)11211bdd6c0eSSue Gleeson srpt_ch_srp_task_mgmt(srpt_channel_t *ch, srpt_iu_t *iu)
11221bdd6c0eSSue Gleeson {
11231bdd6c0eSSue Gleeson 	srp_tsk_mgmt_t		*tsk = (srp_tsk_mgmt_t *)iu->iu_buf;
11241bdd6c0eSSue Gleeson 	uint8_t			tm_fn;
11251bdd6c0eSSue Gleeson 	ibt_status_t		status;
11261bdd6c0eSSue Gleeson 
11271bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L3("ch_srp_task_mgmt, SRP TASK MGMT func(%d)",
11281bdd6c0eSSue Gleeson 	    tsk->tm_function);
11291bdd6c0eSSue Gleeson 
1130191c289bSCharles Ting 	/*
1131191c289bSCharles Ting 	 * Both tag and lun fileds have the same corresponding offsets
1132191c289bSCharles Ting 	 * in both srp_tsk_mgmt_t and srp_cmd_req_t structures.  The
1133191c289bSCharles Ting 	 * casting will allow us to use the same dtrace translator.
1134191c289bSCharles Ting 	 */
1135191c289bSCharles Ting 	DTRACE_SRP_2(task__command, srpt_channel_t, ch,
1136191c289bSCharles Ting 	    srp_cmd_req_t, (srp_cmd_req_t *)tsk);
1137191c289bSCharles Ting 
11381bdd6c0eSSue Gleeson 	iu->iu_ch  = ch;
11391bdd6c0eSSue Gleeson 	iu->iu_tag = tsk->tm_tag;
11401bdd6c0eSSue Gleeson 
11411bdd6c0eSSue Gleeson 	/*
11421bdd6c0eSSue Gleeson 	 * Task management aborts are processed directly by the SRP driver;
11431bdd6c0eSSue Gleeson 	 * all other task management requests are handed off to STMF.
11441bdd6c0eSSue Gleeson 	 */
11451bdd6c0eSSue Gleeson 	switch (tsk->tm_function) {
11461bdd6c0eSSue Gleeson 	case SRP_TSK_MGMT_ABORT_TASK:
11471bdd6c0eSSue Gleeson 		/*
11481bdd6c0eSSue Gleeson 		 * Initiate SCSI transport protocol specific task abort
11491bdd6c0eSSue Gleeson 		 * logic.
11501bdd6c0eSSue Gleeson 		 */
11511bdd6c0eSSue Gleeson 		status = srpt_ch_task_mgmt_abort(ch, iu, tsk->tm_task_tag);
11521bdd6c0eSSue Gleeson 		if (status != IBT_SUCCESS) {
11531bdd6c0eSSue Gleeson 			/* repost this IU */
11541bdd6c0eSSue Gleeson 			return (1);
11551bdd6c0eSSue Gleeson 		} else {
11561bdd6c0eSSue Gleeson 			return (0);
11571bdd6c0eSSue Gleeson 		}
11581bdd6c0eSSue Gleeson 
11591bdd6c0eSSue Gleeson 	case SRP_TSK_MGMT_ABORT_TASK_SET:
11601bdd6c0eSSue Gleeson 		tm_fn = TM_ABORT_TASK_SET;
11611bdd6c0eSSue Gleeson 		break;
11621bdd6c0eSSue Gleeson 
11631bdd6c0eSSue Gleeson 	case SRP_TSK_MGMT_CLEAR_TASK_SET:
11641bdd6c0eSSue Gleeson 		tm_fn = TM_CLEAR_TASK_SET;
11651bdd6c0eSSue Gleeson 		break;
11661bdd6c0eSSue Gleeson 
11671bdd6c0eSSue Gleeson 	case SRP_TSK_MGMT_LUN_RESET:
11681bdd6c0eSSue Gleeson 		tm_fn = TM_LUN_RESET;
11691bdd6c0eSSue Gleeson 		break;
11701bdd6c0eSSue Gleeson 
11711bdd6c0eSSue Gleeson 	case SRP_TSK_MGMT_CLEAR_ACA:
11721bdd6c0eSSue Gleeson 		tm_fn = TM_CLEAR_ACA;
11731bdd6c0eSSue Gleeson 		break;
11741bdd6c0eSSue Gleeson 
11751bdd6c0eSSue Gleeson 	default:
11761bdd6c0eSSue Gleeson 		/*
11771bdd6c0eSSue Gleeson 		 * SRP does not support the requested task management
11781bdd6c0eSSue Gleeson 		 * function; return a not supported status in the response.
11791bdd6c0eSSue Gleeson 		 */
11801bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_srp_task_mgmt, SRP task mgmt fn(%d)"
11811bdd6c0eSSue Gleeson 		    " not supported", tsk->tm_function);
11821bdd6c0eSSue Gleeson 		mutex_enter(&iu->iu_lock);
11831bdd6c0eSSue Gleeson 		status = srpt_stp_send_mgmt_response(iu,
11841bdd6c0eSSue Gleeson 		    SRP_TM_NOT_SUPPORTED, SRPT_NO_FENCE_SEND);
11851bdd6c0eSSue Gleeson 		mutex_exit(&iu->iu_lock);
11861bdd6c0eSSue Gleeson 		if (status != IBT_SUCCESS) {
11871bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L2("ch_srp_task_mgmt, err(%d) posting"
11881bdd6c0eSSue Gleeson 			    " response", status);
11891bdd6c0eSSue Gleeson 			return (1);
11901bdd6c0eSSue Gleeson 		}
11911bdd6c0eSSue Gleeson 		return (0);
11921bdd6c0eSSue Gleeson 	}
11931bdd6c0eSSue Gleeson 
11941bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_READER);
11951bdd6c0eSSue Gleeson 	if (ch->ch_state == SRPT_CHANNEL_DISCONNECTING) {
11961bdd6c0eSSue Gleeson 		/*
11971bdd6c0eSSue Gleeson 		 * The channel has begun disconnecting, so ignore the
11981bdd6c0eSSue Gleeson 		 * the command returning the IU resources.
11991bdd6c0eSSue Gleeson 		 */
12001bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
12011bdd6c0eSSue Gleeson 		return (1);
12021bdd6c0eSSue Gleeson 	}
12031bdd6c0eSSue Gleeson 
12041bdd6c0eSSue Gleeson 	/*
12051bdd6c0eSSue Gleeson 	 * Once a SCSI mgmt task is allocated and assigned to the IU, it
12061bdd6c0eSSue Gleeson 	 * owns those IU resources, which will be held until we inform
12071bdd6c0eSSue Gleeson 	 * STMF that we are done with the task (from an lports perspective).
12081bdd6c0eSSue Gleeson 	 */
12091bdd6c0eSSue Gleeson 	iu->iu_stmf_task = stmf_task_alloc(ch->ch_tgt->tp_lport,
12101bdd6c0eSSue Gleeson 	    ch->ch_session->ss_ss, tsk->tm_lun, 0, STMF_TASK_EXT_NONE);
12111bdd6c0eSSue Gleeson 	if (iu->iu_stmf_task == NULL) {
12121bdd6c0eSSue Gleeson 		/*
12131bdd6c0eSSue Gleeson 		 * Could not allocate, return status to the initiator
12141bdd6c0eSSue Gleeson 		 * indicating that we are temporarily unable to process
12151bdd6c0eSSue Gleeson 		 * commands.  If unable to send, immediately return IU
12161bdd6c0eSSue Gleeson 		 * resource.
12171bdd6c0eSSue Gleeson 		 */
12181bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_srp_task_mgmt, SCSI task allocation"
12191bdd6c0eSSue Gleeson 		    " failure");
12201bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
12211bdd6c0eSSue Gleeson 		mutex_enter(&iu->iu_lock);
12221bdd6c0eSSue Gleeson 		status = srpt_stp_send_response(iu, STATUS_BUSY, 0, 0, 0,
12231bdd6c0eSSue Gleeson 		    NULL, SRPT_NO_FENCE_SEND);
12241bdd6c0eSSue Gleeson 		mutex_exit(&iu->iu_lock);
12251bdd6c0eSSue Gleeson 		if (status != IBT_SUCCESS) {
12261bdd6c0eSSue Gleeson 			SRPT_DPRINTF_L2("ch_srp_task_mgmt, err(%d) posting"
12271bdd6c0eSSue Gleeson 			    "busy response", status);
12281bdd6c0eSSue Gleeson 			/* repost the IU */
12291bdd6c0eSSue Gleeson 			return (1);
12301bdd6c0eSSue Gleeson 		}
12311bdd6c0eSSue Gleeson 		return (0);
12321bdd6c0eSSue Gleeson 	}
12331bdd6c0eSSue Gleeson 
12341bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_port_private = iu;
12351bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_flags = 0;
12361bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_additional_flags =
12371bdd6c0eSSue Gleeson 	    TASK_AF_NO_EXPECTED_XFER_LENGTH;
12381bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_priority = 0;
12391bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_mgmt_function = tm_fn;
12401bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_max_nbufs = STMF_BUFS_MAX;
12411bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_expected_xfer_length = 0;
12421bdd6c0eSSue Gleeson 	iu->iu_stmf_task->task_csn_size = 0;
12431bdd6c0eSSue Gleeson 
12441bdd6c0eSSue Gleeson 	/*
12451bdd6c0eSSue Gleeson 	 * Add the IU/task to the session and post to STMF.  The task will
12461bdd6c0eSSue Gleeson 	 * remain in the session's list until STMF is informed by SRP that
12471bdd6c0eSSue Gleeson 	 * it is done with the task.
12481bdd6c0eSSue Gleeson 	 */
12491bdd6c0eSSue Gleeson 	srpt_stp_add_task(ch->ch_session, iu);
12501bdd6c0eSSue Gleeson 
12511bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L3("ch_srp_task_mgmt, new mgmt task(%p) posted",
12521bdd6c0eSSue Gleeson 	    (void *)iu->iu_stmf_task);
12531bdd6c0eSSue Gleeson 	stmf_post_task(iu->iu_stmf_task, NULL);
12541bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
12551bdd6c0eSSue Gleeson 
12561bdd6c0eSSue Gleeson 	return (0);
12571bdd6c0eSSue Gleeson }
12581bdd6c0eSSue Gleeson 
12591bdd6c0eSSue Gleeson /*
12601bdd6c0eSSue Gleeson  * srpt_ch_process_iu()
12611bdd6c0eSSue Gleeson  */
12621bdd6c0eSSue Gleeson static void
srpt_ch_process_iu(srpt_channel_t * ch,srpt_iu_t * iu)12631bdd6c0eSSue Gleeson srpt_ch_process_iu(srpt_channel_t *ch, srpt_iu_t *iu)
12641bdd6c0eSSue Gleeson {
12651bdd6c0eSSue Gleeson 	srpt_iu_data_t	*iud;
12661bdd6c0eSSue Gleeson 	int		status = 1;
12671bdd6c0eSSue Gleeson 
12681bdd6c0eSSue Gleeson 	/*
12691bdd6c0eSSue Gleeson 	 * IU adds reference to channel which will represent a
12701bdd6c0eSSue Gleeson 	 * a reference by STMF.  If for whatever reason the IU
12711bdd6c0eSSue Gleeson 	 * is not handed off to STMF, then this reference will be
12721bdd6c0eSSue Gleeson 	 * released.  Otherwise, the reference will be released when
12731bdd6c0eSSue Gleeson 	 * SRP informs STMF that the associated SCSI task is done.
12741bdd6c0eSSue Gleeson 	 */
12751bdd6c0eSSue Gleeson 	srpt_ch_add_ref(ch);
12761bdd6c0eSSue Gleeson 
12771bdd6c0eSSue Gleeson 	/*
12781bdd6c0eSSue Gleeson 	 * Validate login RC channel state. Normally active, if
12791bdd6c0eSSue Gleeson 	 * not active then we need to handle a possible race between the
12801bdd6c0eSSue Gleeson 	 * receipt of a implied RTU and CM calling back to notify of the
12811bdd6c0eSSue Gleeson 	 * state transition.
12821bdd6c0eSSue Gleeson 	 */
12831bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_READER);
12841bdd6c0eSSue Gleeson 	if (ch->ch_state == SRPT_CHANNEL_DISCONNECTING) {
12851bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
12861bdd6c0eSSue Gleeson 		goto repost_iu;
12871bdd6c0eSSue Gleeson 	}
12881bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
12891bdd6c0eSSue Gleeson 
12901bdd6c0eSSue Gleeson 	iud = iu->iu_buf;
12911bdd6c0eSSue Gleeson 
12921bdd6c0eSSue Gleeson 	switch (iud->rx_iu.srp_op) {
12931bdd6c0eSSue Gleeson 	case SRP_IU_CMD:
12941bdd6c0eSSue Gleeson 		status = srpt_ch_srp_cmd(ch, iu);
12951bdd6c0eSSue Gleeson 		break;
12961bdd6c0eSSue Gleeson 
12971bdd6c0eSSue Gleeson 	case SRP_IU_TASK_MGMT:
12981bdd6c0eSSue Gleeson 		status = srpt_ch_srp_task_mgmt(ch, iu);
12991bdd6c0eSSue Gleeson 		return;
13001bdd6c0eSSue Gleeson 
13011bdd6c0eSSue Gleeson 	case SRP_IU_I_LOGOUT:
13021bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L3("ch_process_iu, SRP INITIATOR LOGOUT");
13031bdd6c0eSSue Gleeson 		/*
13041bdd6c0eSSue Gleeson 		 * Initiators should logout by issuing a CM disconnect
13051bdd6c0eSSue Gleeson 		 * request (DREQ) with the logout IU in the private data;
13061bdd6c0eSSue Gleeson 		 * however some initiators have been known to send the
13071bdd6c0eSSue Gleeson 		 * IU in-band, if this happens just initiate the logout.
13081bdd6c0eSSue Gleeson 		 * Note that we do not return a response as per the
13091bdd6c0eSSue Gleeson 		 * specification.
13101bdd6c0eSSue Gleeson 		 */
13111bdd6c0eSSue Gleeson 		srpt_stp_logout(ch);
13121bdd6c0eSSue Gleeson 		break;
13131bdd6c0eSSue Gleeson 
13141bdd6c0eSSue Gleeson 	case SRP_IU_AER_RSP:
13151bdd6c0eSSue Gleeson 	case SRP_IU_CRED_RSP:
13161bdd6c0eSSue Gleeson 	default:
13171bdd6c0eSSue Gleeson 		/*
13181bdd6c0eSSue Gleeson 		 * We don't send asynchronous events or ask for credit
13191bdd6c0eSSue Gleeson 		 * adjustments, so nothing need be done.  Log we got an
13201bdd6c0eSSue Gleeson 		 * unexpected IU but then just repost the IU to the SRQ.
13211bdd6c0eSSue Gleeson 		 */
13221bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_process_iu, invalid IU from initiator,"
13231bdd6c0eSSue Gleeson 		    " IU opcode(%d)", iud->rx_iu.srp_op);
13241bdd6c0eSSue Gleeson 		break;
13251bdd6c0eSSue Gleeson 	}
13261bdd6c0eSSue Gleeson 
13271bdd6c0eSSue Gleeson 	if (status == 0) {
13281bdd6c0eSSue Gleeson 		return;
13291bdd6c0eSSue Gleeson 	}
13301bdd6c0eSSue Gleeson 
13311bdd6c0eSSue Gleeson repost_iu:
13321bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L4("process_iu:  reposting iu %p", (void *)iu);
13331bdd6c0eSSue Gleeson 	mutex_enter(&iu->iu_lock);
13341bdd6c0eSSue Gleeson 	srpt_ioc_repost_recv_iu(iu->iu_ioc, iu);
13351bdd6c0eSSue Gleeson 	mutex_exit(&iu->iu_lock);
13361bdd6c0eSSue Gleeson 	srpt_ch_release_ref(ch, 0);
13371bdd6c0eSSue Gleeson }
13381bdd6c0eSSue Gleeson 
13391bdd6c0eSSue Gleeson /*
13401bdd6c0eSSue Gleeson  * srpt_ch_post_send
13411bdd6c0eSSue Gleeson  */
13421bdd6c0eSSue Gleeson ibt_status_t
srpt_ch_post_send(srpt_channel_t * ch,srpt_iu_t * iu,uint32_t len,uint_t fence)13431bdd6c0eSSue Gleeson srpt_ch_post_send(srpt_channel_t *ch, srpt_iu_t *iu, uint32_t len,
13441bdd6c0eSSue Gleeson 	uint_t fence)
13451bdd6c0eSSue Gleeson {
13461bdd6c0eSSue Gleeson 	ibt_status_t		status;
13471bdd6c0eSSue Gleeson 	ibt_send_wr_t		wr;
13481bdd6c0eSSue Gleeson 	ibt_wr_ds_t		ds;
13491bdd6c0eSSue Gleeson 	uint_t			posted;
13501bdd6c0eSSue Gleeson 
13511bdd6c0eSSue Gleeson 	ASSERT(ch != NULL);
13521bdd6c0eSSue Gleeson 	ASSERT(iu != NULL);
13531bdd6c0eSSue Gleeson 	ASSERT(mutex_owned(&iu->iu_lock));
13541bdd6c0eSSue Gleeson 
13551bdd6c0eSSue Gleeson 	rw_enter(&ch->ch_rwlock, RW_READER);
13561bdd6c0eSSue Gleeson 	if (ch->ch_state == SRPT_CHANNEL_DISCONNECTING) {
13571bdd6c0eSSue Gleeson 		rw_exit(&ch->ch_rwlock);
13581bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_post_send, bad ch state (%d)",
13591bdd6c0eSSue Gleeson 		    ch->ch_state);
13601bdd6c0eSSue Gleeson 		return (IBT_FAILURE);
13611bdd6c0eSSue Gleeson 	}
13621bdd6c0eSSue Gleeson 	rw_exit(&ch->ch_rwlock);
13631bdd6c0eSSue Gleeson 
13641bdd6c0eSSue Gleeson 	wr.wr_id = srpt_ch_alloc_swqe_wrid(ch, SRPT_SWQE_TYPE_RESP,
13651bdd6c0eSSue Gleeson 	    (void *)iu);
13661bdd6c0eSSue Gleeson 	if (wr.wr_id == 0) {
13671bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_post_send, queue full");
13681bdd6c0eSSue Gleeson 		return (IBT_FAILURE);
13691bdd6c0eSSue Gleeson 	}
13701bdd6c0eSSue Gleeson 
13711bdd6c0eSSue Gleeson 	atomic_inc_32(&iu->iu_sq_posted_cnt);
13721bdd6c0eSSue Gleeson 
13731bdd6c0eSSue Gleeson 	wr.wr_flags = IBT_WR_SEND_SIGNAL;
13741bdd6c0eSSue Gleeson 	if (fence == SRPT_FENCE_SEND) {
13751bdd6c0eSSue Gleeson 		wr.wr_flags |= IBT_WR_SEND_FENCE;
13761bdd6c0eSSue Gleeson 	}
13771bdd6c0eSSue Gleeson 	wr.wr_opcode = IBT_WRC_SEND;
13781bdd6c0eSSue Gleeson 	wr.wr_trans  = IBT_RC_SRV;
13791bdd6c0eSSue Gleeson 	wr.wr_nds = 1;
13801bdd6c0eSSue Gleeson 	wr.wr_sgl = &ds;
13811bdd6c0eSSue Gleeson 
13821bdd6c0eSSue Gleeson 	ds.ds_va = iu->iu_sge.ds_va;
13831bdd6c0eSSue Gleeson 	ds.ds_key = iu->iu_sge.ds_key;
13841bdd6c0eSSue Gleeson 	ds.ds_len = len;
13851bdd6c0eSSue Gleeson 
13861bdd6c0eSSue Gleeson 	SRPT_DPRINTF_L4("ch_post_send, posting SRP response to channel"
13871bdd6c0eSSue Gleeson 	    " ds.ds_va (0x%16llx), ds.ds_key (0x%08x), "
13881bdd6c0eSSue Gleeson 	    " ds.ds_len (%d)",
13891bdd6c0eSSue Gleeson 	    (u_longlong_t)ds.ds_va, ds.ds_key, ds.ds_len);
13901bdd6c0eSSue Gleeson 
13911bdd6c0eSSue Gleeson 	status = ibt_post_send(ch->ch_chan_hdl, &wr, 1, &posted);
13921bdd6c0eSSue Gleeson 	if (status != IBT_SUCCESS) {
13931bdd6c0eSSue Gleeson 		SRPT_DPRINTF_L2("ch_post_send, post_send failed (%d)",
13941bdd6c0eSSue Gleeson 		    status);
13951bdd6c0eSSue Gleeson 		atomic_dec_32(&iu->iu_sq_posted_cnt);
13961bdd6c0eSSue Gleeson 		srpt_ch_free_swqe_wrid(ch, wr.wr_id);
13971bdd6c0eSSue Gleeson 		return (status);
13981bdd6c0eSSue Gleeson 	}
13991bdd6c0eSSue Gleeson 
14001bdd6c0eSSue Gleeson 	return (IBT_SUCCESS);
14011bdd6c0eSSue Gleeson }
14021bdd6c0eSSue Gleeson 
14031bdd6c0eSSue Gleeson /*
14041bdd6c0eSSue Gleeson  * srpt_ch_alloc_swqe_wrid()
14051bdd6c0eSSue Gleeson  */
14061bdd6c0eSSue Gleeson ibt_wrid_t
srpt_ch_alloc_swqe_wrid(srpt_channel_t * ch,srpt_swqe_type_t wqe_type,void * addr)14071bdd6c0eSSue Gleeson srpt_ch_alloc_swqe_wrid(srpt_channel_t *ch,
14081bdd6c0eSSue Gleeson 	srpt_swqe_type_t wqe_type, void *addr)
14091bdd6c0eSSue Gleeson {
14101bdd6c0eSSue Gleeson 	ibt_wrid_t	wrid;
14111bdd6c0eSSue Gleeson 
14121bdd6c0eSSue Gleeson 	mutex_enter(&ch->ch_swqe_lock);
14131bdd6c0eSSue Gleeson 	if (ch->ch_head == ch->ch_tail) {
14141bdd6c0eSSue Gleeson 		mutex_exit(&ch->ch_swqe_lock);
14151bdd6c0eSSue Gleeson 		return ((ibt_wrid_t)0);
14161bdd6c0eSSue Gleeson 	}
14171bdd6c0eSSue Gleeson 	wrid = (ibt_wrid_t)ch->ch_head;
14181bdd6c0eSSue Gleeson 	ch->ch_swqe[ch->ch_head].sw_type = wqe_type;
14191bdd6c0eSSue Gleeson 	ch->ch_swqe[ch->ch_head].sw_addr = addr;
14201bdd6c0eSSue Gleeson 	ch->ch_head = ch->ch_swqe[ch->ch_head].sw_next;
14211bdd6c0eSSue Gleeson 	ch->ch_swqe_posted++;
14221bdd6c0eSSue Gleeson 	mutex_exit(&ch->ch_swqe_lock);
14231bdd6c0eSSue Gleeson 	return (wrid);
14241bdd6c0eSSue Gleeson }
14251bdd6c0eSSue Gleeson 
14261bdd6c0eSSue Gleeson /*
14271bdd6c0eSSue Gleeson  * srpt_ch_free_swqe_wrid()
14281bdd6c0eSSue Gleeson  */
14291bdd6c0eSSue Gleeson void
srpt_ch_free_swqe_wrid(srpt_channel_t * ch,ibt_wrid_t id)14301bdd6c0eSSue Gleeson srpt_ch_free_swqe_wrid(srpt_channel_t *ch, ibt_wrid_t id)
14311bdd6c0eSSue Gleeson {
14321bdd6c0eSSue Gleeson 	mutex_enter(&ch->ch_swqe_lock);
14331bdd6c0eSSue Gleeson 	ch->ch_swqe[ch->ch_tail].sw_next = id;
14341bdd6c0eSSue Gleeson 	ch->ch_tail = (uint32_t)id;
14351bdd6c0eSSue Gleeson 	ch->ch_swqe_posted--;
14361bdd6c0eSSue Gleeson 	mutex_exit(&ch->ch_swqe_lock);
14371bdd6c0eSSue Gleeson }
1438