xref: /onnv-gate/usr/src/uts/common/io/comstar/port/iscsit/iscsit.c (revision 12702:2169bee4a248)
17978SPeter.Dunlap@Sun.COM /*
27978SPeter.Dunlap@Sun.COM  * CDDL HEADER START
37978SPeter.Dunlap@Sun.COM  *
47978SPeter.Dunlap@Sun.COM  * The contents of this file are subject to the terms of the
57978SPeter.Dunlap@Sun.COM  * Common Development and Distribution License (the "License").
67978SPeter.Dunlap@Sun.COM  * You may not use this file except in compliance with the License.
77978SPeter.Dunlap@Sun.COM  *
87978SPeter.Dunlap@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97978SPeter.Dunlap@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107978SPeter.Dunlap@Sun.COM  * See the License for the specific language governing permissions
117978SPeter.Dunlap@Sun.COM  * and limitations under the License.
127978SPeter.Dunlap@Sun.COM  *
137978SPeter.Dunlap@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147978SPeter.Dunlap@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157978SPeter.Dunlap@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167978SPeter.Dunlap@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177978SPeter.Dunlap@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187978SPeter.Dunlap@Sun.COM  *
197978SPeter.Dunlap@Sun.COM  * CDDL HEADER END
207978SPeter.Dunlap@Sun.COM  */
217978SPeter.Dunlap@Sun.COM /*
2212372SPriya.Krishnan@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
237978SPeter.Dunlap@Sun.COM  */
247978SPeter.Dunlap@Sun.COM 
257978SPeter.Dunlap@Sun.COM #include <sys/cpuvar.h>
267978SPeter.Dunlap@Sun.COM #include <sys/types.h>
277978SPeter.Dunlap@Sun.COM #include <sys/conf.h>
287978SPeter.Dunlap@Sun.COM #include <sys/stat.h>
297978SPeter.Dunlap@Sun.COM #include <sys/file.h>
307978SPeter.Dunlap@Sun.COM #include <sys/ddi.h>
317978SPeter.Dunlap@Sun.COM #include <sys/sunddi.h>
327978SPeter.Dunlap@Sun.COM #include <sys/modctl.h>
337978SPeter.Dunlap@Sun.COM #include <sys/sysmacros.h>
347978SPeter.Dunlap@Sun.COM #include <sys/socket.h>
357978SPeter.Dunlap@Sun.COM #include <sys/strsubr.h>
367978SPeter.Dunlap@Sun.COM #include <sys/nvpair.h>
377978SPeter.Dunlap@Sun.COM 
387978SPeter.Dunlap@Sun.COM #include <sys/stmf.h>
397978SPeter.Dunlap@Sun.COM #include <sys/stmf_ioctl.h>
407978SPeter.Dunlap@Sun.COM #include <sys/portif.h>
417978SPeter.Dunlap@Sun.COM #include <sys/idm/idm.h>
427978SPeter.Dunlap@Sun.COM #include <sys/idm/idm_conn_sm.h>
4312571SViswanathan.Kannappan@Sun.COM 
4412571SViswanathan.Kannappan@Sun.COM #include "iscsit_isns.h"
4512571SViswanathan.Kannappan@Sun.COM #include "iscsit.h"
467978SPeter.Dunlap@Sun.COM 
477978SPeter.Dunlap@Sun.COM #define	ISCSIT_VERSION		BUILD_DATE "-1.18dev"
487978SPeter.Dunlap@Sun.COM #define	ISCSIT_NAME_VERSION	"COMSTAR ISCSIT v" ISCSIT_VERSION
497978SPeter.Dunlap@Sun.COM 
507978SPeter.Dunlap@Sun.COM /*
517978SPeter.Dunlap@Sun.COM  * DDI entry points.
527978SPeter.Dunlap@Sun.COM  */
537978SPeter.Dunlap@Sun.COM static int iscsit_drv_attach(dev_info_t *, ddi_attach_cmd_t);
547978SPeter.Dunlap@Sun.COM static int iscsit_drv_detach(dev_info_t *, ddi_detach_cmd_t);
557978SPeter.Dunlap@Sun.COM static int iscsit_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
567978SPeter.Dunlap@Sun.COM static int iscsit_drv_open(dev_t *, int, int, cred_t *);
577978SPeter.Dunlap@Sun.COM static int iscsit_drv_close(dev_t, int, int, cred_t *);
587978SPeter.Dunlap@Sun.COM static boolean_t iscsit_drv_busy(void);
597978SPeter.Dunlap@Sun.COM static int iscsit_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
607978SPeter.Dunlap@Sun.COM 
617978SPeter.Dunlap@Sun.COM extern struct mod_ops mod_miscops;
627978SPeter.Dunlap@Sun.COM 
637978SPeter.Dunlap@Sun.COM 
647978SPeter.Dunlap@Sun.COM static struct cb_ops iscsit_cb_ops = {
657978SPeter.Dunlap@Sun.COM 	iscsit_drv_open,	/* cb_open */
667978SPeter.Dunlap@Sun.COM 	iscsit_drv_close,	/* cb_close */
677978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_strategy */
687978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_print */
697978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_dump */
707978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_read */
717978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_write */
727978SPeter.Dunlap@Sun.COM 	iscsit_drv_ioctl,	/* cb_ioctl */
737978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_devmap */
747978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_mmap */
757978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_segmap */
767978SPeter.Dunlap@Sun.COM 	nochpoll,		/* cb_chpoll */
777978SPeter.Dunlap@Sun.COM 	ddi_prop_op,		/* cb_prop_op */
787978SPeter.Dunlap@Sun.COM 	NULL,			/* cb_streamtab */
797978SPeter.Dunlap@Sun.COM 	D_MP,			/* cb_flag */
807978SPeter.Dunlap@Sun.COM 	CB_REV,			/* cb_rev */
817978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_aread */
827978SPeter.Dunlap@Sun.COM 	nodev,			/* cb_awrite */
837978SPeter.Dunlap@Sun.COM };
847978SPeter.Dunlap@Sun.COM 
857978SPeter.Dunlap@Sun.COM static struct dev_ops iscsit_dev_ops = {
867978SPeter.Dunlap@Sun.COM 	DEVO_REV,		/* devo_rev */
877978SPeter.Dunlap@Sun.COM 	0,			/* devo_refcnt */
887978SPeter.Dunlap@Sun.COM 	iscsit_drv_getinfo,	/* devo_getinfo */
897978SPeter.Dunlap@Sun.COM 	nulldev,		/* devo_identify */
907978SPeter.Dunlap@Sun.COM 	nulldev,		/* devo_probe */
917978SPeter.Dunlap@Sun.COM 	iscsit_drv_attach,	/* devo_attach */
927978SPeter.Dunlap@Sun.COM 	iscsit_drv_detach,	/* devo_detach */
937978SPeter.Dunlap@Sun.COM 	nodev,			/* devo_reset */
947978SPeter.Dunlap@Sun.COM 	&iscsit_cb_ops,		/* devo_cb_ops */
957978SPeter.Dunlap@Sun.COM 	NULL,			/* devo_bus_ops */
967978SPeter.Dunlap@Sun.COM 	NULL,			/* devo_power */
978607SJames.Moore@Sun.COM 	ddi_quiesce_not_needed,	/* quiesce */
987978SPeter.Dunlap@Sun.COM };
997978SPeter.Dunlap@Sun.COM 
1007978SPeter.Dunlap@Sun.COM static struct modldrv modldrv = {
1017978SPeter.Dunlap@Sun.COM 	&mod_driverops,
1027978SPeter.Dunlap@Sun.COM 	"iSCSI Target",
1037978SPeter.Dunlap@Sun.COM 	&iscsit_dev_ops,
1047978SPeter.Dunlap@Sun.COM };
1057978SPeter.Dunlap@Sun.COM 
1067978SPeter.Dunlap@Sun.COM static struct modlinkage modlinkage = {
1077978SPeter.Dunlap@Sun.COM 	MODREV_1,
1087978SPeter.Dunlap@Sun.COM 	&modldrv,
1097978SPeter.Dunlap@Sun.COM 	NULL,
1107978SPeter.Dunlap@Sun.COM };
1117978SPeter.Dunlap@Sun.COM 
1127978SPeter.Dunlap@Sun.COM 
1137978SPeter.Dunlap@Sun.COM iscsit_global_t iscsit_global;
1147978SPeter.Dunlap@Sun.COM 
1157978SPeter.Dunlap@Sun.COM kmem_cache_t	*iscsit_status_pdu_cache;
1167978SPeter.Dunlap@Sun.COM 
1177978SPeter.Dunlap@Sun.COM boolean_t	iscsit_sm_logging = B_FALSE;
1187978SPeter.Dunlap@Sun.COM 
119*12702SPriya.Krishnan@Sun.COM kmutex_t	login_sm_session_mutex;
120*12702SPriya.Krishnan@Sun.COM 
1217978SPeter.Dunlap@Sun.COM static idm_status_t iscsit_init(dev_info_t *dip);
1227978SPeter.Dunlap@Sun.COM static idm_status_t iscsit_enable_svc(iscsit_hostinfo_t *hostinfo);
1237978SPeter.Dunlap@Sun.COM static void iscsit_disable_svc(void);
1247978SPeter.Dunlap@Sun.COM 
12512372SPriya.Krishnan@Sun.COM static int
12612372SPriya.Krishnan@Sun.COM iscsit_check_cmdsn_and_queue(idm_pdu_t *rx_pdu);
12712372SPriya.Krishnan@Sun.COM 
12812372SPriya.Krishnan@Sun.COM static void
12912372SPriya.Krishnan@Sun.COM iscsit_add_pdu_to_queue(iscsit_sess_t *ist, idm_pdu_t *rx_pdu);
13012372SPriya.Krishnan@Sun.COM 
13112372SPriya.Krishnan@Sun.COM static idm_pdu_t *
13212372SPriya.Krishnan@Sun.COM iscsit_remove_pdu_from_queue(iscsit_sess_t *ist, uint32_t cmdsn);
13312372SPriya.Krishnan@Sun.COM 
13412372SPriya.Krishnan@Sun.COM static void
13512372SPriya.Krishnan@Sun.COM iscsit_process_pdu_in_queue(iscsit_sess_t *ist);
13612372SPriya.Krishnan@Sun.COM 
13712372SPriya.Krishnan@Sun.COM static void
13812372SPriya.Krishnan@Sun.COM iscsit_rxpdu_queue_monitor_session(iscsit_sess_t *ist);
13912372SPriya.Krishnan@Sun.COM 
14012372SPriya.Krishnan@Sun.COM static void
14112372SPriya.Krishnan@Sun.COM iscsit_rxpdu_queue_monitor(void *arg);
14212372SPriya.Krishnan@Sun.COM 
14312372SPriya.Krishnan@Sun.COM static void
14412372SPriya.Krishnan@Sun.COM iscsit_post_staged_pdu(idm_pdu_t *rx_pdu);
14512372SPriya.Krishnan@Sun.COM 
14612372SPriya.Krishnan@Sun.COM static void
14712372SPriya.Krishnan@Sun.COM iscsit_post_scsi_cmd(idm_conn_t *ic, idm_pdu_t *rx_pdu);
14812372SPriya.Krishnan@Sun.COM 
1497978SPeter.Dunlap@Sun.COM static void
1507978SPeter.Dunlap@Sun.COM iscsit_op_scsi_task_mgmt(iscsit_conn_t *ict, idm_pdu_t *rx_pdu);
1517978SPeter.Dunlap@Sun.COM 
1527978SPeter.Dunlap@Sun.COM static void
1537978SPeter.Dunlap@Sun.COM iscsit_pdu_op_noop(iscsit_conn_t *ict, idm_pdu_t *rx_pdu);
1547978SPeter.Dunlap@Sun.COM 
1557978SPeter.Dunlap@Sun.COM static void
1567978SPeter.Dunlap@Sun.COM iscsit_pdu_op_login_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu);
1577978SPeter.Dunlap@Sun.COM 
1587978SPeter.Dunlap@Sun.COM void
1597978SPeter.Dunlap@Sun.COM iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu);
1607978SPeter.Dunlap@Sun.COM 
1617978SPeter.Dunlap@Sun.COM static void
1627978SPeter.Dunlap@Sun.COM iscsit_pdu_op_logout_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu);
1637978SPeter.Dunlap@Sun.COM 
1647978SPeter.Dunlap@Sun.COM int iscsit_cmd_window();
1657978SPeter.Dunlap@Sun.COM 
16612372SPriya.Krishnan@Sun.COM static  int
16712372SPriya.Krishnan@Sun.COM iscsit_sna_lt(uint32_t sn1, uint32_t sn2);
16812372SPriya.Krishnan@Sun.COM 
1697978SPeter.Dunlap@Sun.COM void
1707978SPeter.Dunlap@Sun.COM iscsit_set_cmdsn(iscsit_conn_t *ict, idm_pdu_t *rx_pdu);
1717978SPeter.Dunlap@Sun.COM 
1727978SPeter.Dunlap@Sun.COM static void
1737978SPeter.Dunlap@Sun.COM iscsit_deferred_dispatch(idm_pdu_t *rx_pdu);
1747978SPeter.Dunlap@Sun.COM 
1757978SPeter.Dunlap@Sun.COM static void
1767978SPeter.Dunlap@Sun.COM iscsit_deferred(void *rx_pdu_void);
1777978SPeter.Dunlap@Sun.COM 
1787978SPeter.Dunlap@Sun.COM static idm_status_t
1797978SPeter.Dunlap@Sun.COM iscsit_conn_accept(idm_conn_t *ic);
1807978SPeter.Dunlap@Sun.COM 
1817978SPeter.Dunlap@Sun.COM static idm_status_t
1827978SPeter.Dunlap@Sun.COM iscsit_ffp_enabled(idm_conn_t *ic);
1837978SPeter.Dunlap@Sun.COM 
1847978SPeter.Dunlap@Sun.COM static idm_status_t
1857978SPeter.Dunlap@Sun.COM iscsit_ffp_disabled(idm_conn_t *ic, idm_ffp_disable_t disable_class);
1867978SPeter.Dunlap@Sun.COM 
1877978SPeter.Dunlap@Sun.COM static idm_status_t
1887978SPeter.Dunlap@Sun.COM iscsit_conn_lost(idm_conn_t *ic);
1897978SPeter.Dunlap@Sun.COM 
1907978SPeter.Dunlap@Sun.COM static idm_status_t
1917978SPeter.Dunlap@Sun.COM iscsit_conn_destroy(idm_conn_t *ic);
1927978SPeter.Dunlap@Sun.COM 
1937978SPeter.Dunlap@Sun.COM static stmf_data_buf_t *
1947978SPeter.Dunlap@Sun.COM iscsit_dbuf_alloc(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
1957978SPeter.Dunlap@Sun.COM     uint32_t flags);
1967978SPeter.Dunlap@Sun.COM 
1977978SPeter.Dunlap@Sun.COM static void
1987978SPeter.Dunlap@Sun.COM iscsit_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf);
1997978SPeter.Dunlap@Sun.COM 
2007978SPeter.Dunlap@Sun.COM static void
2017978SPeter.Dunlap@Sun.COM iscsit_buf_xfer_cb(idm_buf_t *idb, idm_status_t status);
2027978SPeter.Dunlap@Sun.COM 
2037978SPeter.Dunlap@Sun.COM static void
2047978SPeter.Dunlap@Sun.COM iscsit_send_good_status_done(idm_pdu_t *pdu, idm_status_t status);
2057978SPeter.Dunlap@Sun.COM 
2067978SPeter.Dunlap@Sun.COM static void
2077978SPeter.Dunlap@Sun.COM iscsit_send_status_done(idm_pdu_t *pdu, idm_status_t status);
2087978SPeter.Dunlap@Sun.COM 
2097978SPeter.Dunlap@Sun.COM static stmf_status_t
2107978SPeter.Dunlap@Sun.COM iscsit_idm_to_stmf(idm_status_t idmrc);
2117978SPeter.Dunlap@Sun.COM 
2127978SPeter.Dunlap@Sun.COM static iscsit_task_t *
2137978SPeter.Dunlap@Sun.COM iscsit_task_alloc(iscsit_conn_t *ict);
2147978SPeter.Dunlap@Sun.COM 
2157978SPeter.Dunlap@Sun.COM static void
2167978SPeter.Dunlap@Sun.COM iscsit_task_free(iscsit_task_t *itask);
2177978SPeter.Dunlap@Sun.COM 
2187978SPeter.Dunlap@Sun.COM static iscsit_task_t *
2197978SPeter.Dunlap@Sun.COM iscsit_tm_task_alloc(iscsit_conn_t *ict);
2207978SPeter.Dunlap@Sun.COM 
2217978SPeter.Dunlap@Sun.COM static void
2227978SPeter.Dunlap@Sun.COM iscsit_tm_task_free(iscsit_task_t *itask);
2237978SPeter.Dunlap@Sun.COM 
2249586SPeter.Dunlap@Sun.COM static idm_status_t
2259586SPeter.Dunlap@Sun.COM iscsit_task_start(iscsit_task_t *itask);
2269586SPeter.Dunlap@Sun.COM 
2279586SPeter.Dunlap@Sun.COM static void
2289586SPeter.Dunlap@Sun.COM iscsit_task_done(iscsit_task_t *itask);
2299586SPeter.Dunlap@Sun.COM 
2307978SPeter.Dunlap@Sun.COM static int
2317978SPeter.Dunlap@Sun.COM iscsit_status_pdu_constructor(void *pdu_void, void *arg, int flags);
2327978SPeter.Dunlap@Sun.COM 
2337978SPeter.Dunlap@Sun.COM static void
2347978SPeter.Dunlap@Sun.COM iscsit_pp_cb(struct stmf_port_provider *pp, int cmd, void *arg, uint32_t flags);
2357978SPeter.Dunlap@Sun.COM 
2367978SPeter.Dunlap@Sun.COM static it_cfg_status_t
2377978SPeter.Dunlap@Sun.COM iscsit_config_merge(it_config_t *cfg);
2387978SPeter.Dunlap@Sun.COM 
2397978SPeter.Dunlap@Sun.COM static idm_status_t
2407978SPeter.Dunlap@Sun.COM iscsit_login_fail(idm_conn_t *ic);
2417978SPeter.Dunlap@Sun.COM 
2429586SPeter.Dunlap@Sun.COM static boolean_t iscsit_cmdsn_in_window(iscsit_conn_t *ict, uint32_t cmdsn);
2439586SPeter.Dunlap@Sun.COM static void iscsit_send_direct_scsi_resp(iscsit_conn_t *ict, idm_pdu_t *rx_pdu,
2449586SPeter.Dunlap@Sun.COM     uint8_t response, uint8_t cmd_status);
2459586SPeter.Dunlap@Sun.COM static void iscsit_send_task_mgmt_resp(idm_pdu_t *tm_resp_pdu,
2469586SPeter.Dunlap@Sun.COM     uint8_t tm_status);
2479586SPeter.Dunlap@Sun.COM 
24812372SPriya.Krishnan@Sun.COM /*
24912372SPriya.Krishnan@Sun.COM  * MC/S: Out-of-order commands are staged on a session-wide wait
25012372SPriya.Krishnan@Sun.COM  * queue until a system-tunable threshold is reached. A separate
25112372SPriya.Krishnan@Sun.COM  * thread is used to scan the staging queue on all the session,
25212372SPriya.Krishnan@Sun.COM  * If a delayed PDU does not arrive within a timeout, the target
25312372SPriya.Krishnan@Sun.COM  * will advance to the staged PDU that is next in sequence, skipping
25412372SPriya.Krishnan@Sun.COM  * over the missing PDU(s) to go past a hole in the sequence.
25512372SPriya.Krishnan@Sun.COM  */
25612372SPriya.Krishnan@Sun.COM volatile int rxpdu_queue_threshold = ISCSIT_RXPDU_QUEUE_THRESHOLD;
25712372SPriya.Krishnan@Sun.COM 
25812372SPriya.Krishnan@Sun.COM static kmutex_t		iscsit_rxpdu_queue_monitor_mutex;
25912372SPriya.Krishnan@Sun.COM kthread_t		*iscsit_rxpdu_queue_monitor_thr_id;
26012372SPriya.Krishnan@Sun.COM static kt_did_t		iscsit_rxpdu_queue_monitor_thr_did;
26112372SPriya.Krishnan@Sun.COM static boolean_t	iscsit_rxpdu_queue_monitor_thr_running;
26212372SPriya.Krishnan@Sun.COM static kcondvar_t	iscsit_rxpdu_queue_monitor_cv;
26312372SPriya.Krishnan@Sun.COM 
2647978SPeter.Dunlap@Sun.COM int
_init(void)2657978SPeter.Dunlap@Sun.COM _init(void)
2667978SPeter.Dunlap@Sun.COM {
2677978SPeter.Dunlap@Sun.COM 	int rc;
2687978SPeter.Dunlap@Sun.COM 
2697978SPeter.Dunlap@Sun.COM 	rw_init(&iscsit_global.global_rwlock, NULL, RW_DRIVER, NULL);
27011431SPeter.Cudhea@Sun.COM 	mutex_init(&iscsit_global.global_state_mutex, NULL,
27111431SPeter.Cudhea@Sun.COM 	    MUTEX_DRIVER, NULL);
2727978SPeter.Dunlap@Sun.COM 	iscsit_global.global_svc_state = ISE_DETACHED;
2737978SPeter.Dunlap@Sun.COM 
27412372SPriya.Krishnan@Sun.COM 	mutex_init(&iscsit_rxpdu_queue_monitor_mutex, NULL,
27512372SPriya.Krishnan@Sun.COM 	    MUTEX_DRIVER, NULL);
276*12702SPriya.Krishnan@Sun.COM 	mutex_init(&login_sm_session_mutex, NULL, MUTEX_DRIVER, NULL);
27712372SPriya.Krishnan@Sun.COM 	iscsit_rxpdu_queue_monitor_thr_id = NULL;
27812372SPriya.Krishnan@Sun.COM 	iscsit_rxpdu_queue_monitor_thr_running = B_FALSE;
27912372SPriya.Krishnan@Sun.COM 	cv_init(&iscsit_rxpdu_queue_monitor_cv, NULL, CV_DEFAULT, NULL);
28012372SPriya.Krishnan@Sun.COM 
2817978SPeter.Dunlap@Sun.COM 	if ((rc = mod_install(&modlinkage)) != 0) {
28211431SPeter.Cudhea@Sun.COM 		mutex_destroy(&iscsit_global.global_state_mutex);
2837978SPeter.Dunlap@Sun.COM 		rw_destroy(&iscsit_global.global_rwlock);
2847978SPeter.Dunlap@Sun.COM 		return (rc);
2857978SPeter.Dunlap@Sun.COM 	}
2867978SPeter.Dunlap@Sun.COM 
2877978SPeter.Dunlap@Sun.COM 	return (rc);
2887978SPeter.Dunlap@Sun.COM }
2897978SPeter.Dunlap@Sun.COM 
2907978SPeter.Dunlap@Sun.COM int
_info(struct modinfo * modinfop)2917978SPeter.Dunlap@Sun.COM _info(struct modinfo *modinfop)
2927978SPeter.Dunlap@Sun.COM {
2937978SPeter.Dunlap@Sun.COM 	return (mod_info(&modlinkage, modinfop));
2947978SPeter.Dunlap@Sun.COM }
2957978SPeter.Dunlap@Sun.COM 
2967978SPeter.Dunlap@Sun.COM int
_fini(void)2977978SPeter.Dunlap@Sun.COM _fini(void)
2987978SPeter.Dunlap@Sun.COM {
2997978SPeter.Dunlap@Sun.COM 	int rc;
3007978SPeter.Dunlap@Sun.COM 
3017978SPeter.Dunlap@Sun.COM 	rc = mod_remove(&modlinkage);
3027978SPeter.Dunlap@Sun.COM 
3037978SPeter.Dunlap@Sun.COM 	if (rc == 0) {
30412372SPriya.Krishnan@Sun.COM 		mutex_destroy(&iscsit_rxpdu_queue_monitor_mutex);
305*12702SPriya.Krishnan@Sun.COM 		mutex_destroy(&login_sm_session_mutex);
30612372SPriya.Krishnan@Sun.COM 		cv_destroy(&iscsit_rxpdu_queue_monitor_cv);
30711431SPeter.Cudhea@Sun.COM 		mutex_destroy(&iscsit_global.global_state_mutex);
3087978SPeter.Dunlap@Sun.COM 		rw_destroy(&iscsit_global.global_rwlock);
3097978SPeter.Dunlap@Sun.COM 	}
3107978SPeter.Dunlap@Sun.COM 
3117978SPeter.Dunlap@Sun.COM 	return (rc);
3127978SPeter.Dunlap@Sun.COM }
3137978SPeter.Dunlap@Sun.COM 
3147978SPeter.Dunlap@Sun.COM /*
3157978SPeter.Dunlap@Sun.COM  * DDI entry points.
3167978SPeter.Dunlap@Sun.COM  */
3177978SPeter.Dunlap@Sun.COM 
3187978SPeter.Dunlap@Sun.COM /* ARGSUSED */
3197978SPeter.Dunlap@Sun.COM static int
iscsit_drv_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)3207978SPeter.Dunlap@Sun.COM iscsit_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
3217978SPeter.Dunlap@Sun.COM     void **result)
3227978SPeter.Dunlap@Sun.COM {
3237978SPeter.Dunlap@Sun.COM 	ulong_t instance = getminor((dev_t)arg);
3247978SPeter.Dunlap@Sun.COM 
3257978SPeter.Dunlap@Sun.COM 	switch (cmd) {
3267978SPeter.Dunlap@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
3277978SPeter.Dunlap@Sun.COM 		*result = iscsit_global.global_dip;
3287978SPeter.Dunlap@Sun.COM 		return (DDI_SUCCESS);
3297978SPeter.Dunlap@Sun.COM 
3307978SPeter.Dunlap@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
3317978SPeter.Dunlap@Sun.COM 		*result = (void *)instance;
3327978SPeter.Dunlap@Sun.COM 		return (DDI_SUCCESS);
3337978SPeter.Dunlap@Sun.COM 
3347978SPeter.Dunlap@Sun.COM 	default:
3357978SPeter.Dunlap@Sun.COM 		break;
3367978SPeter.Dunlap@Sun.COM 	}
3377978SPeter.Dunlap@Sun.COM 
3387978SPeter.Dunlap@Sun.COM 	return (DDI_FAILURE);
3397978SPeter.Dunlap@Sun.COM }
3407978SPeter.Dunlap@Sun.COM 
3417978SPeter.Dunlap@Sun.COM static int
iscsit_drv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3427978SPeter.Dunlap@Sun.COM iscsit_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3437978SPeter.Dunlap@Sun.COM {
3447978SPeter.Dunlap@Sun.COM 	if (cmd != DDI_ATTACH) {
3457978SPeter.Dunlap@Sun.COM 		return (DDI_FAILURE);
3467978SPeter.Dunlap@Sun.COM 	}
3477978SPeter.Dunlap@Sun.COM 
3487978SPeter.Dunlap@Sun.COM 	if (ddi_get_instance(dip) != 0) {
3497978SPeter.Dunlap@Sun.COM 		/* we only allow instance 0 to attach */
3507978SPeter.Dunlap@Sun.COM 		return (DDI_FAILURE);
3517978SPeter.Dunlap@Sun.COM 	}
3527978SPeter.Dunlap@Sun.COM 
3537978SPeter.Dunlap@Sun.COM 	/* create the minor node */
3547978SPeter.Dunlap@Sun.COM 	if (ddi_create_minor_node(dip, ISCSIT_MODNAME, S_IFCHR, 0,
3557978SPeter.Dunlap@Sun.COM 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
3567978SPeter.Dunlap@Sun.COM 		cmn_err(CE_WARN, "iscsit_drv_attach: "
3577978SPeter.Dunlap@Sun.COM 		    "failed creating minor node");
3587978SPeter.Dunlap@Sun.COM 		return (DDI_FAILURE);
3597978SPeter.Dunlap@Sun.COM 	}
3607978SPeter.Dunlap@Sun.COM 
3617978SPeter.Dunlap@Sun.COM 	if (iscsit_init(dip) != IDM_STATUS_SUCCESS) {
3627978SPeter.Dunlap@Sun.COM 		cmn_err(CE_WARN, "iscsit_drv_attach: "
3637978SPeter.Dunlap@Sun.COM 		    "failed to initialize");
3647978SPeter.Dunlap@Sun.COM 		ddi_remove_minor_node(dip, NULL);
3657978SPeter.Dunlap@Sun.COM 		return (DDI_FAILURE);
3667978SPeter.Dunlap@Sun.COM 	}
3677978SPeter.Dunlap@Sun.COM 
3687978SPeter.Dunlap@Sun.COM 	iscsit_global.global_svc_state = ISE_DISABLED;
3697978SPeter.Dunlap@Sun.COM 	iscsit_global.global_dip = dip;
3707978SPeter.Dunlap@Sun.COM 
3717978SPeter.Dunlap@Sun.COM 	return (DDI_SUCCESS);
3727978SPeter.Dunlap@Sun.COM }
3737978SPeter.Dunlap@Sun.COM 
3747978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
3757978SPeter.Dunlap@Sun.COM static int
iscsit_drv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3767978SPeter.Dunlap@Sun.COM iscsit_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3777978SPeter.Dunlap@Sun.COM {
3787978SPeter.Dunlap@Sun.COM 	if (cmd != DDI_DETACH)
3797978SPeter.Dunlap@Sun.COM 		return (DDI_FAILURE);
3807978SPeter.Dunlap@Sun.COM 
38111431SPeter.Cudhea@Sun.COM 	/*
38211431SPeter.Cudhea@Sun.COM 	 * drv_detach is called in a context that owns the
38311431SPeter.Cudhea@Sun.COM 	 * device node for the /dev/pseudo device.  If this thread blocks
38411431SPeter.Cudhea@Sun.COM 	 * for any resource, other threads that need the /dev/pseudo device
38511431SPeter.Cudhea@Sun.COM 	 * may end up in a deadlock with this thread.Hence, we use a
38611431SPeter.Cudhea@Sun.COM 	 * separate lock just for the structures that drv_detach needs
38711431SPeter.Cudhea@Sun.COM 	 * to access.
38811431SPeter.Cudhea@Sun.COM 	 */
38911431SPeter.Cudhea@Sun.COM 	mutex_enter(&iscsit_global.global_state_mutex);
3907978SPeter.Dunlap@Sun.COM 	if (iscsit_drv_busy()) {
39111431SPeter.Cudhea@Sun.COM 		mutex_exit(&iscsit_global.global_state_mutex);
3927978SPeter.Dunlap@Sun.COM 		return (EBUSY);
3937978SPeter.Dunlap@Sun.COM 	}
3947978SPeter.Dunlap@Sun.COM 
3957978SPeter.Dunlap@Sun.COM 	iscsit_global.global_dip = NULL;
3967978SPeter.Dunlap@Sun.COM 	ddi_remove_minor_node(dip, NULL);
3977978SPeter.Dunlap@Sun.COM 
3987978SPeter.Dunlap@Sun.COM 	ldi_ident_release(iscsit_global.global_li);
3997978SPeter.Dunlap@Sun.COM 	iscsit_global.global_svc_state = ISE_DETACHED;
4007978SPeter.Dunlap@Sun.COM 
40111431SPeter.Cudhea@Sun.COM 	mutex_exit(&iscsit_global.global_state_mutex);
4027978SPeter.Dunlap@Sun.COM 
4037978SPeter.Dunlap@Sun.COM 	return (DDI_SUCCESS);
4047978SPeter.Dunlap@Sun.COM }
4057978SPeter.Dunlap@Sun.COM 
4067978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
4077978SPeter.Dunlap@Sun.COM static int
iscsit_drv_open(dev_t * devp,int flag,int otyp,cred_t * credp)4087978SPeter.Dunlap@Sun.COM iscsit_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4097978SPeter.Dunlap@Sun.COM {
4107978SPeter.Dunlap@Sun.COM 	return (0);
4117978SPeter.Dunlap@Sun.COM }
4127978SPeter.Dunlap@Sun.COM 
4137978SPeter.Dunlap@Sun.COM /* ARGSUSED */
4147978SPeter.Dunlap@Sun.COM static int
iscsit_drv_close(dev_t dev,int flag,int otyp,cred_t * credp)4157978SPeter.Dunlap@Sun.COM iscsit_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
4167978SPeter.Dunlap@Sun.COM {
4177978SPeter.Dunlap@Sun.COM 	return (0);
4187978SPeter.Dunlap@Sun.COM }
4197978SPeter.Dunlap@Sun.COM 
4207978SPeter.Dunlap@Sun.COM static boolean_t
iscsit_drv_busy(void)4217978SPeter.Dunlap@Sun.COM iscsit_drv_busy(void)
4227978SPeter.Dunlap@Sun.COM {
42311431SPeter.Cudhea@Sun.COM 	ASSERT(MUTEX_HELD(&iscsit_global.global_state_mutex));
42411431SPeter.Cudhea@Sun.COM 
4257978SPeter.Dunlap@Sun.COM 	switch (iscsit_global.global_svc_state) {
4267978SPeter.Dunlap@Sun.COM 	case ISE_DISABLED:
4277978SPeter.Dunlap@Sun.COM 	case ISE_DETACHED:
4287978SPeter.Dunlap@Sun.COM 		return (B_FALSE);
4297978SPeter.Dunlap@Sun.COM 	default:
4307978SPeter.Dunlap@Sun.COM 		return (B_TRUE);
4317978SPeter.Dunlap@Sun.COM 	}
4327978SPeter.Dunlap@Sun.COM 	/* NOTREACHED */
4337978SPeter.Dunlap@Sun.COM }
4347978SPeter.Dunlap@Sun.COM 
4357978SPeter.Dunlap@Sun.COM /* ARGSUSED */
4367978SPeter.Dunlap@Sun.COM static int
iscsit_drv_ioctl(dev_t drv,int cmd,intptr_t argp,int flag,cred_t * cred,int * retval)4377978SPeter.Dunlap@Sun.COM iscsit_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred,
4387978SPeter.Dunlap@Sun.COM     int *retval)
4397978SPeter.Dunlap@Sun.COM {
4407978SPeter.Dunlap@Sun.COM 	iscsit_ioc_set_config_t		setcfg;
4417978SPeter.Dunlap@Sun.COM 	iscsit_ioc_set_config32_t	setcfg32;
44210522SPeter.Cudhea@Sun.COM 	char				*cfg_pnvlist = NULL;
44310522SPeter.Cudhea@Sun.COM 	nvlist_t			*cfg_nvlist = NULL;
44410522SPeter.Cudhea@Sun.COM 	it_config_t			*cfg = NULL;
4457978SPeter.Dunlap@Sun.COM 	idm_status_t			idmrc;
4467978SPeter.Dunlap@Sun.COM 	int				rc = 0;
4477978SPeter.Dunlap@Sun.COM 
4487978SPeter.Dunlap@Sun.COM 	if (drv_priv(cred) != 0) {
4497978SPeter.Dunlap@Sun.COM 		return (EPERM);
4507978SPeter.Dunlap@Sun.COM 	}
4517978SPeter.Dunlap@Sun.COM 
45211431SPeter.Cudhea@Sun.COM 	mutex_enter(&iscsit_global.global_state_mutex);
4537978SPeter.Dunlap@Sun.COM 
4547978SPeter.Dunlap@Sun.COM 	/*
4557978SPeter.Dunlap@Sun.COM 	 * Validate ioctl requests against global service state
4567978SPeter.Dunlap@Sun.COM 	 */
4577978SPeter.Dunlap@Sun.COM 	switch (iscsit_global.global_svc_state) {
4587978SPeter.Dunlap@Sun.COM 	case ISE_ENABLED:
4597978SPeter.Dunlap@Sun.COM 		if (cmd == ISCSIT_IOC_DISABLE_SVC) {
4607978SPeter.Dunlap@Sun.COM 			iscsit_global.global_svc_state = ISE_DISABLING;
4617978SPeter.Dunlap@Sun.COM 		} else if (cmd == ISCSIT_IOC_ENABLE_SVC) {
4627978SPeter.Dunlap@Sun.COM 			/* Already enabled */
46311431SPeter.Cudhea@Sun.COM 			mutex_exit(&iscsit_global.global_state_mutex);
4647978SPeter.Dunlap@Sun.COM 			return (0);
4657978SPeter.Dunlap@Sun.COM 		} else {
4667978SPeter.Dunlap@Sun.COM 			iscsit_global.global_svc_state = ISE_BUSY;
4677978SPeter.Dunlap@Sun.COM 		}
4687978SPeter.Dunlap@Sun.COM 		break;
4697978SPeter.Dunlap@Sun.COM 	case ISE_DISABLED:
4707978SPeter.Dunlap@Sun.COM 		if (cmd == ISCSIT_IOC_ENABLE_SVC) {
4717978SPeter.Dunlap@Sun.COM 			iscsit_global.global_svc_state = ISE_ENABLING;
4727978SPeter.Dunlap@Sun.COM 		} else if (cmd == ISCSIT_IOC_DISABLE_SVC) {
4737978SPeter.Dunlap@Sun.COM 			/* Already disabled */
47411431SPeter.Cudhea@Sun.COM 			mutex_exit(&iscsit_global.global_state_mutex);
4757978SPeter.Dunlap@Sun.COM 			return (0);
4767978SPeter.Dunlap@Sun.COM 		} else {
4777978SPeter.Dunlap@Sun.COM 			rc = EFAULT;
4787978SPeter.Dunlap@Sun.COM 		}
4797978SPeter.Dunlap@Sun.COM 		break;
48011431SPeter.Cudhea@Sun.COM 	case ISE_BUSY:
4817978SPeter.Dunlap@Sun.COM 	case ISE_ENABLING:
4827978SPeter.Dunlap@Sun.COM 	case ISE_DISABLING:
4837978SPeter.Dunlap@Sun.COM 		rc = EAGAIN;
4847978SPeter.Dunlap@Sun.COM 		break;
4857978SPeter.Dunlap@Sun.COM 	case ISE_DETACHED:
4867978SPeter.Dunlap@Sun.COM 	default:
4877978SPeter.Dunlap@Sun.COM 		rc = EFAULT;
4887978SPeter.Dunlap@Sun.COM 		break;
4897978SPeter.Dunlap@Sun.COM 	}
4907978SPeter.Dunlap@Sun.COM 
49111431SPeter.Cudhea@Sun.COM 	mutex_exit(&iscsit_global.global_state_mutex);
4927978SPeter.Dunlap@Sun.COM 	if (rc != 0)
4937978SPeter.Dunlap@Sun.COM 		return (rc);
4947978SPeter.Dunlap@Sun.COM 
4957978SPeter.Dunlap@Sun.COM 	/* Handle ioctl request (enable/disable have already been handled) */
4967978SPeter.Dunlap@Sun.COM 	switch (cmd) {
4977978SPeter.Dunlap@Sun.COM 	case ISCSIT_IOC_SET_CONFIG:
49810522SPeter.Cudhea@Sun.COM 		/* Any errors must set state back to ISE_ENABLED */
4997978SPeter.Dunlap@Sun.COM 		switch (ddi_model_convert_from(flag & FMODELS)) {
5007978SPeter.Dunlap@Sun.COM 		case DDI_MODEL_ILP32:
5017978SPeter.Dunlap@Sun.COM 			if (ddi_copyin((void *)argp, &setcfg32,
50210522SPeter.Cudhea@Sun.COM 			    sizeof (iscsit_ioc_set_config32_t), flag) != 0) {
50310522SPeter.Cudhea@Sun.COM 				rc = EFAULT;
50410522SPeter.Cudhea@Sun.COM 				goto cleanup;
50510522SPeter.Cudhea@Sun.COM 			}
5067978SPeter.Dunlap@Sun.COM 
5077978SPeter.Dunlap@Sun.COM 			setcfg.set_cfg_pnvlist =
5087978SPeter.Dunlap@Sun.COM 			    (char *)((uintptr_t)setcfg32.set_cfg_pnvlist);
5097978SPeter.Dunlap@Sun.COM 			setcfg.set_cfg_vers = setcfg32.set_cfg_vers;
5107978SPeter.Dunlap@Sun.COM 			setcfg.set_cfg_pnvlist_len =
5117978SPeter.Dunlap@Sun.COM 			    setcfg32.set_cfg_pnvlist_len;
5127978SPeter.Dunlap@Sun.COM 			break;
5137978SPeter.Dunlap@Sun.COM 		case DDI_MODEL_NONE:
5147978SPeter.Dunlap@Sun.COM 			if (ddi_copyin((void *)argp, &setcfg,
51510522SPeter.Cudhea@Sun.COM 			    sizeof (iscsit_ioc_set_config_t), flag) != 0) {
51610522SPeter.Cudhea@Sun.COM 				rc = EFAULT;
51710522SPeter.Cudhea@Sun.COM 				goto cleanup;
51810522SPeter.Cudhea@Sun.COM 			}
5197978SPeter.Dunlap@Sun.COM 			break;
52010855SCharles.Ting@Sun.COM 		default:
52110855SCharles.Ting@Sun.COM 			rc = EFAULT;
52210855SCharles.Ting@Sun.COM 			goto cleanup;
5237978SPeter.Dunlap@Sun.COM 		}
5247978SPeter.Dunlap@Sun.COM 
5257978SPeter.Dunlap@Sun.COM 		/* Check API version */
5267978SPeter.Dunlap@Sun.COM 		if (setcfg.set_cfg_vers != ISCSIT_API_VERS0) {
52710522SPeter.Cudhea@Sun.COM 			rc = EINVAL;
52810522SPeter.Cudhea@Sun.COM 			goto cleanup;
5297978SPeter.Dunlap@Sun.COM 		}
5307978SPeter.Dunlap@Sun.COM 
5317978SPeter.Dunlap@Sun.COM 		/* Config is in packed nvlist format so unpack it */
5327978SPeter.Dunlap@Sun.COM 		cfg_pnvlist = kmem_alloc(setcfg.set_cfg_pnvlist_len,
5337978SPeter.Dunlap@Sun.COM 		    KM_SLEEP);
5347978SPeter.Dunlap@Sun.COM 		ASSERT(cfg_pnvlist != NULL);
5357978SPeter.Dunlap@Sun.COM 
5367978SPeter.Dunlap@Sun.COM 		if (ddi_copyin(setcfg.set_cfg_pnvlist, cfg_pnvlist,
5377978SPeter.Dunlap@Sun.COM 		    setcfg.set_cfg_pnvlist_len, flag) != 0) {
53810522SPeter.Cudhea@Sun.COM 			rc = EFAULT;
53910522SPeter.Cudhea@Sun.COM 			goto cleanup;
5407978SPeter.Dunlap@Sun.COM 		}
5417978SPeter.Dunlap@Sun.COM 
54210522SPeter.Cudhea@Sun.COM 		rc = nvlist_unpack(cfg_pnvlist, setcfg.set_cfg_pnvlist_len,
54310522SPeter.Cudhea@Sun.COM 		    &cfg_nvlist, KM_SLEEP);
54410522SPeter.Cudhea@Sun.COM 		if (rc != 0) {
54510522SPeter.Cudhea@Sun.COM 			goto cleanup;
5467978SPeter.Dunlap@Sun.COM 		}
5477978SPeter.Dunlap@Sun.COM 
5487978SPeter.Dunlap@Sun.COM 		/* Translate nvlist */
54910522SPeter.Cudhea@Sun.COM 		rc = it_nv_to_config(cfg_nvlist, &cfg);
55010522SPeter.Cudhea@Sun.COM 		if (rc != 0) {
5517978SPeter.Dunlap@Sun.COM 			cmn_err(CE_WARN, "Configuration is invalid");
55210522SPeter.Cudhea@Sun.COM 			goto cleanup;
5537978SPeter.Dunlap@Sun.COM 		}
5547978SPeter.Dunlap@Sun.COM 
5557978SPeter.Dunlap@Sun.COM 		/* Update config */
55610522SPeter.Cudhea@Sun.COM 		rc = iscsit_config_merge(cfg);
55710522SPeter.Cudhea@Sun.COM 		/* FALLTHROUGH */
55810522SPeter.Cudhea@Sun.COM 
55910522SPeter.Cudhea@Sun.COM cleanup:
56010522SPeter.Cudhea@Sun.COM 		if (cfg)
56110522SPeter.Cudhea@Sun.COM 			it_config_free_cmn(cfg);
56210522SPeter.Cudhea@Sun.COM 		if (cfg_pnvlist)
5637978SPeter.Dunlap@Sun.COM 			kmem_free(cfg_pnvlist, setcfg.set_cfg_pnvlist_len);
56410522SPeter.Cudhea@Sun.COM 		if (cfg_nvlist)
5657978SPeter.Dunlap@Sun.COM 			nvlist_free(cfg_nvlist);
5667978SPeter.Dunlap@Sun.COM 
5677978SPeter.Dunlap@Sun.COM 		/*
5687978SPeter.Dunlap@Sun.COM 		 * Now that the reconfig is complete set our state back to
5697978SPeter.Dunlap@Sun.COM 		 * enabled.
5707978SPeter.Dunlap@Sun.COM 		 */
57111431SPeter.Cudhea@Sun.COM 		mutex_enter(&iscsit_global.global_state_mutex);
5727978SPeter.Dunlap@Sun.COM 		iscsit_global.global_svc_state = ISE_ENABLED;
57311431SPeter.Cudhea@Sun.COM 		mutex_exit(&iscsit_global.global_state_mutex);
5747978SPeter.Dunlap@Sun.COM 		break;
5757978SPeter.Dunlap@Sun.COM 	case ISCSIT_IOC_ENABLE_SVC: {
5767978SPeter.Dunlap@Sun.COM 		iscsit_hostinfo_t hostinfo;
5777978SPeter.Dunlap@Sun.COM 
5787978SPeter.Dunlap@Sun.COM 		if (ddi_copyin((void *)argp, &hostinfo.length,
5797978SPeter.Dunlap@Sun.COM 		    sizeof (hostinfo.length), flag) != 0) {
58011431SPeter.Cudhea@Sun.COM 			mutex_enter(&iscsit_global.global_state_mutex);
5817978SPeter.Dunlap@Sun.COM 			iscsit_global.global_svc_state = ISE_DISABLED;
58211431SPeter.Cudhea@Sun.COM 			mutex_exit(&iscsit_global.global_state_mutex);
5837978SPeter.Dunlap@Sun.COM 			return (EFAULT);
5847978SPeter.Dunlap@Sun.COM 		}
5857978SPeter.Dunlap@Sun.COM 
5867978SPeter.Dunlap@Sun.COM 		if (hostinfo.length > sizeof (hostinfo.fqhn))
5877978SPeter.Dunlap@Sun.COM 			hostinfo.length = sizeof (hostinfo.fqhn);
5887978SPeter.Dunlap@Sun.COM 
5897978SPeter.Dunlap@Sun.COM 		if (ddi_copyin((void *)((caddr_t)argp +
5907978SPeter.Dunlap@Sun.COM 		    sizeof (hostinfo.length)), &hostinfo.fqhn,
5917978SPeter.Dunlap@Sun.COM 		    hostinfo.length, flag) != 0) {
59211431SPeter.Cudhea@Sun.COM 			mutex_enter(&iscsit_global.global_state_mutex);
5937978SPeter.Dunlap@Sun.COM 			iscsit_global.global_svc_state = ISE_DISABLED;
59411431SPeter.Cudhea@Sun.COM 			mutex_exit(&iscsit_global.global_state_mutex);
5957978SPeter.Dunlap@Sun.COM 			return (EFAULT);
5967978SPeter.Dunlap@Sun.COM 		}
5977978SPeter.Dunlap@Sun.COM 
5987978SPeter.Dunlap@Sun.COM 		idmrc = iscsit_enable_svc(&hostinfo);
59911431SPeter.Cudhea@Sun.COM 		mutex_enter(&iscsit_global.global_state_mutex);
6007978SPeter.Dunlap@Sun.COM 		if (idmrc == IDM_STATUS_SUCCESS) {
6017978SPeter.Dunlap@Sun.COM 			iscsit_global.global_svc_state = ISE_ENABLED;
6027978SPeter.Dunlap@Sun.COM 		} else {
6037978SPeter.Dunlap@Sun.COM 			rc = EIO;
6047978SPeter.Dunlap@Sun.COM 			iscsit_global.global_svc_state = ISE_DISABLED;
6057978SPeter.Dunlap@Sun.COM 		}
60611431SPeter.Cudhea@Sun.COM 		mutex_exit(&iscsit_global.global_state_mutex);
6077978SPeter.Dunlap@Sun.COM 		break;
6087978SPeter.Dunlap@Sun.COM 	}
6097978SPeter.Dunlap@Sun.COM 	case ISCSIT_IOC_DISABLE_SVC:
6107978SPeter.Dunlap@Sun.COM 		iscsit_disable_svc();
61111431SPeter.Cudhea@Sun.COM 		mutex_enter(&iscsit_global.global_state_mutex);
6127978SPeter.Dunlap@Sun.COM 		iscsit_global.global_svc_state = ISE_DISABLED;
61311431SPeter.Cudhea@Sun.COM 		mutex_exit(&iscsit_global.global_state_mutex);
6147978SPeter.Dunlap@Sun.COM 		break;
61510522SPeter.Cudhea@Sun.COM 
6167978SPeter.Dunlap@Sun.COM 	default:
6177978SPeter.Dunlap@Sun.COM 		rc = EINVAL;
61811431SPeter.Cudhea@Sun.COM 		mutex_enter(&iscsit_global.global_state_mutex);
61910522SPeter.Cudhea@Sun.COM 		iscsit_global.global_svc_state = ISE_ENABLED;
62011431SPeter.Cudhea@Sun.COM 		mutex_exit(&iscsit_global.global_state_mutex);
6217978SPeter.Dunlap@Sun.COM 	}
6227978SPeter.Dunlap@Sun.COM 
6237978SPeter.Dunlap@Sun.COM 	return (rc);
6247978SPeter.Dunlap@Sun.COM }
6257978SPeter.Dunlap@Sun.COM 
6267978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_init(dev_info_t * dip)6277978SPeter.Dunlap@Sun.COM iscsit_init(dev_info_t *dip)
6287978SPeter.Dunlap@Sun.COM {
6297978SPeter.Dunlap@Sun.COM 	int			rc;
6307978SPeter.Dunlap@Sun.COM 
6317978SPeter.Dunlap@Sun.COM 	rc = ldi_ident_from_dip(dip, &iscsit_global.global_li);
6327978SPeter.Dunlap@Sun.COM 	ASSERT(rc == 0);  /* Failure indicates invalid argument */
6337978SPeter.Dunlap@Sun.COM 
6347978SPeter.Dunlap@Sun.COM 	iscsit_global.global_svc_state = ISE_DISABLED;
6357978SPeter.Dunlap@Sun.COM 
6367978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
6377978SPeter.Dunlap@Sun.COM }
6387978SPeter.Dunlap@Sun.COM 
6397978SPeter.Dunlap@Sun.COM /*
6407978SPeter.Dunlap@Sun.COM  * iscsit_enable_svc
6417978SPeter.Dunlap@Sun.COM  *
6427978SPeter.Dunlap@Sun.COM  * registers all the configured targets and target portals with STMF
6437978SPeter.Dunlap@Sun.COM  */
6447978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_enable_svc(iscsit_hostinfo_t * hostinfo)6457978SPeter.Dunlap@Sun.COM iscsit_enable_svc(iscsit_hostinfo_t *hostinfo)
6467978SPeter.Dunlap@Sun.COM {
6477978SPeter.Dunlap@Sun.COM 	stmf_port_provider_t	*pp;
6487978SPeter.Dunlap@Sun.COM 	stmf_dbuf_store_t	*dbuf_store;
6497978SPeter.Dunlap@Sun.COM 	boolean_t		did_iscsit_isns_init;
6507978SPeter.Dunlap@Sun.COM 	idm_status_t		retval = IDM_STATUS_SUCCESS;
6517978SPeter.Dunlap@Sun.COM 
6527978SPeter.Dunlap@Sun.COM 	ASSERT(iscsit_global.global_svc_state == ISE_ENABLING);
6537978SPeter.Dunlap@Sun.COM 
6547978SPeter.Dunlap@Sun.COM 	/*
6557978SPeter.Dunlap@Sun.COM 	 * Make sure that can tell if we have partially allocated
6567978SPeter.Dunlap@Sun.COM 	 * in case we need to exit and tear down anything allocated.
6577978SPeter.Dunlap@Sun.COM 	 */
6587978SPeter.Dunlap@Sun.COM 	iscsit_global.global_tsih_pool = NULL;
6597978SPeter.Dunlap@Sun.COM 	iscsit_global.global_dbuf_store = NULL;
6607978SPeter.Dunlap@Sun.COM 	iscsit_status_pdu_cache = NULL;
6617978SPeter.Dunlap@Sun.COM 	pp = NULL;
6627978SPeter.Dunlap@Sun.COM 	iscsit_global.global_pp = NULL;
6637978SPeter.Dunlap@Sun.COM 	iscsit_global.global_default_tpg = NULL;
6647978SPeter.Dunlap@Sun.COM 	did_iscsit_isns_init = B_FALSE;
6657978SPeter.Dunlap@Sun.COM 	iscsit_global.global_dispatch_taskq = NULL;
6667978SPeter.Dunlap@Sun.COM 
6677978SPeter.Dunlap@Sun.COM 	/* Setup remaining fields in iscsit_global_t */
6687978SPeter.Dunlap@Sun.COM 	idm_refcnt_init(&iscsit_global.global_refcnt,
6697978SPeter.Dunlap@Sun.COM 	    &iscsit_global);
6707978SPeter.Dunlap@Sun.COM 
6717978SPeter.Dunlap@Sun.COM 	avl_create(&iscsit_global.global_discovery_sessions,
6727978SPeter.Dunlap@Sun.COM 	    iscsit_sess_avl_compare, sizeof (iscsit_sess_t),
6737978SPeter.Dunlap@Sun.COM 	    offsetof(iscsit_sess_t, ist_tgt_ln));
6747978SPeter.Dunlap@Sun.COM 
6757978SPeter.Dunlap@Sun.COM 	avl_create(&iscsit_global.global_target_list,
6767978SPeter.Dunlap@Sun.COM 	    iscsit_tgt_avl_compare, sizeof (iscsit_tgt_t),
6777978SPeter.Dunlap@Sun.COM 	    offsetof(iscsit_tgt_t, target_global_ln));
6787978SPeter.Dunlap@Sun.COM 
6797978SPeter.Dunlap@Sun.COM 	list_create(&iscsit_global.global_deleted_target_list,
6807978SPeter.Dunlap@Sun.COM 	    sizeof (iscsit_tgt_t),
6817978SPeter.Dunlap@Sun.COM 	    offsetof(iscsit_tgt_t, target_global_deleted_ln));
6827978SPeter.Dunlap@Sun.COM 
6837978SPeter.Dunlap@Sun.COM 	avl_create(&iscsit_global.global_tpg_list,
6847978SPeter.Dunlap@Sun.COM 	    iscsit_tpg_avl_compare, sizeof (iscsit_tpg_t),
6857978SPeter.Dunlap@Sun.COM 	    offsetof(iscsit_tpg_t, tpg_global_ln));
6867978SPeter.Dunlap@Sun.COM 
6877978SPeter.Dunlap@Sun.COM 	avl_create(&iscsit_global.global_ini_list,
6887978SPeter.Dunlap@Sun.COM 	    iscsit_ini_avl_compare, sizeof (iscsit_ini_t),
6897978SPeter.Dunlap@Sun.COM 	    offsetof(iscsit_ini_t, ini_global_ln));
6907978SPeter.Dunlap@Sun.COM 
6917978SPeter.Dunlap@Sun.COM 	iscsit_global.global_tsih_pool = vmem_create("iscsit_tsih_pool",
6927978SPeter.Dunlap@Sun.COM 	    (void *)1, ISCSI_MAX_TSIH, 1, NULL, NULL, NULL, 0,
6937978SPeter.Dunlap@Sun.COM 	    VM_SLEEP | VMC_IDENTIFIER);
6947978SPeter.Dunlap@Sun.COM 
6957978SPeter.Dunlap@Sun.COM 	/*
6967978SPeter.Dunlap@Sun.COM 	 * Setup STMF dbuf store.  Our buffers are bound to a specific
6977978SPeter.Dunlap@Sun.COM 	 * connection so we really can't let STMF cache buffers for us.
6987978SPeter.Dunlap@Sun.COM 	 * Consequently we'll just allocate one global buffer store.
6997978SPeter.Dunlap@Sun.COM 	 */
7007978SPeter.Dunlap@Sun.COM 	dbuf_store = stmf_alloc(STMF_STRUCT_DBUF_STORE, 0, 0);
7017978SPeter.Dunlap@Sun.COM 	if (dbuf_store == NULL) {
7027978SPeter.Dunlap@Sun.COM 		retval = IDM_STATUS_FAIL;
7037978SPeter.Dunlap@Sun.COM 		goto tear_down_and_return;
7047978SPeter.Dunlap@Sun.COM 	}
7057978SPeter.Dunlap@Sun.COM 	dbuf_store->ds_alloc_data_buf = iscsit_dbuf_alloc;
7067978SPeter.Dunlap@Sun.COM 	dbuf_store->ds_free_data_buf = iscsit_dbuf_free;
7077978SPeter.Dunlap@Sun.COM 	dbuf_store->ds_port_private = NULL;
7087978SPeter.Dunlap@Sun.COM 	iscsit_global.global_dbuf_store = dbuf_store;
7097978SPeter.Dunlap@Sun.COM 
7107978SPeter.Dunlap@Sun.COM 	/* Status PDU cache */
7117978SPeter.Dunlap@Sun.COM 	iscsit_status_pdu_cache = kmem_cache_create("iscsit_status_pdu_cache",
7127978SPeter.Dunlap@Sun.COM 	    sizeof (idm_pdu_t) + sizeof (iscsi_scsi_rsp_hdr_t), 8,
7137978SPeter.Dunlap@Sun.COM 	    &iscsit_status_pdu_constructor,
7147978SPeter.Dunlap@Sun.COM 	    NULL, NULL, NULL, NULL, KM_SLEEP);
7157978SPeter.Dunlap@Sun.COM 
7167978SPeter.Dunlap@Sun.COM 	/* Default TPG and portal */
7177978SPeter.Dunlap@Sun.COM 	iscsit_global.global_default_tpg = iscsit_tpg_createdefault();
7187978SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_default_tpg == NULL) {
7197978SPeter.Dunlap@Sun.COM 		retval = IDM_STATUS_FAIL;
7207978SPeter.Dunlap@Sun.COM 		goto tear_down_and_return;
7217978SPeter.Dunlap@Sun.COM 	}
7227978SPeter.Dunlap@Sun.COM 
7237978SPeter.Dunlap@Sun.COM 	/* initialize isns client */
7247978SPeter.Dunlap@Sun.COM 	(void) iscsit_isns_init(hostinfo);
7257978SPeter.Dunlap@Sun.COM 	did_iscsit_isns_init = B_TRUE;
7267978SPeter.Dunlap@Sun.COM 
7277978SPeter.Dunlap@Sun.COM 	/* Register port provider */
7287978SPeter.Dunlap@Sun.COM 	pp = stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
7297978SPeter.Dunlap@Sun.COM 	if (pp == NULL) {
7307978SPeter.Dunlap@Sun.COM 		retval = IDM_STATUS_FAIL;
7317978SPeter.Dunlap@Sun.COM 		goto tear_down_and_return;
7327978SPeter.Dunlap@Sun.COM 	}
7337978SPeter.Dunlap@Sun.COM 
7347978SPeter.Dunlap@Sun.COM 	pp->pp_portif_rev = PORTIF_REV_1;
7357978SPeter.Dunlap@Sun.COM 	pp->pp_instance = 0;
7367978SPeter.Dunlap@Sun.COM 	pp->pp_name = ISCSIT_MODNAME;
7377978SPeter.Dunlap@Sun.COM 	pp->pp_cb = iscsit_pp_cb;
7387978SPeter.Dunlap@Sun.COM 
7397978SPeter.Dunlap@Sun.COM 	iscsit_global.global_pp = pp;
7407978SPeter.Dunlap@Sun.COM 
7417978SPeter.Dunlap@Sun.COM 
7427978SPeter.Dunlap@Sun.COM 	if (stmf_register_port_provider(pp) != STMF_SUCCESS) {
7437978SPeter.Dunlap@Sun.COM 		retval = IDM_STATUS_FAIL;
7447978SPeter.Dunlap@Sun.COM 		goto tear_down_and_return;
7457978SPeter.Dunlap@Sun.COM 	}
7467978SPeter.Dunlap@Sun.COM 
7477978SPeter.Dunlap@Sun.COM 	iscsit_global.global_dispatch_taskq = taskq_create("iscsit_dispatch",
7487978SPeter.Dunlap@Sun.COM 	    1, minclsyspri, 16, 16, TASKQ_PREPOPULATE);
7497978SPeter.Dunlap@Sun.COM 
75012372SPriya.Krishnan@Sun.COM 	/* Scan staged PDUs, meaningful in MC/S situations */
75112372SPriya.Krishnan@Sun.COM 	iscsit_rxpdu_queue_monitor_start();
75212372SPriya.Krishnan@Sun.COM 
7537978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
7547978SPeter.Dunlap@Sun.COM 
7557978SPeter.Dunlap@Sun.COM tear_down_and_return:
7567978SPeter.Dunlap@Sun.COM 
7577978SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_dispatch_taskq) {
7587978SPeter.Dunlap@Sun.COM 		taskq_destroy(iscsit_global.global_dispatch_taskq);
7597978SPeter.Dunlap@Sun.COM 		iscsit_global.global_dispatch_taskq = NULL;
7607978SPeter.Dunlap@Sun.COM 	}
7617978SPeter.Dunlap@Sun.COM 
7627978SPeter.Dunlap@Sun.COM 	if (did_iscsit_isns_init)
7637978SPeter.Dunlap@Sun.COM 		iscsit_isns_fini();
7647978SPeter.Dunlap@Sun.COM 
7657978SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_default_tpg) {
7667978SPeter.Dunlap@Sun.COM 		iscsit_tpg_destroydefault(iscsit_global.global_default_tpg);
7677978SPeter.Dunlap@Sun.COM 		iscsit_global.global_default_tpg = NULL;
7687978SPeter.Dunlap@Sun.COM 	}
7697978SPeter.Dunlap@Sun.COM 
7707978SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_pp)
7717978SPeter.Dunlap@Sun.COM 		iscsit_global.global_pp = NULL;
7727978SPeter.Dunlap@Sun.COM 
7737978SPeter.Dunlap@Sun.COM 	if (pp)
7747978SPeter.Dunlap@Sun.COM 		stmf_free(pp);
7757978SPeter.Dunlap@Sun.COM 
7767978SPeter.Dunlap@Sun.COM 	if (iscsit_status_pdu_cache) {
7777978SPeter.Dunlap@Sun.COM 		kmem_cache_destroy(iscsit_status_pdu_cache);
7787978SPeter.Dunlap@Sun.COM 		iscsit_status_pdu_cache = NULL;
7797978SPeter.Dunlap@Sun.COM 	}
7807978SPeter.Dunlap@Sun.COM 
7817978SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_dbuf_store) {
7827978SPeter.Dunlap@Sun.COM 		stmf_free(iscsit_global.global_dbuf_store);
7837978SPeter.Dunlap@Sun.COM 		iscsit_global.global_dbuf_store = NULL;
7847978SPeter.Dunlap@Sun.COM 	}
7857978SPeter.Dunlap@Sun.COM 
7867978SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_tsih_pool) {
7877978SPeter.Dunlap@Sun.COM 		vmem_destroy(iscsit_global.global_tsih_pool);
7887978SPeter.Dunlap@Sun.COM 		iscsit_global.global_tsih_pool = NULL;
7897978SPeter.Dunlap@Sun.COM 	}
7907978SPeter.Dunlap@Sun.COM 
7917978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_ini_list);
7927978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_tpg_list);
7937978SPeter.Dunlap@Sun.COM 	list_destroy(&iscsit_global.global_deleted_target_list);
7947978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_target_list);
7957978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_discovery_sessions);
7967978SPeter.Dunlap@Sun.COM 
7977978SPeter.Dunlap@Sun.COM 	idm_refcnt_destroy(&iscsit_global.global_refcnt);
7987978SPeter.Dunlap@Sun.COM 
7997978SPeter.Dunlap@Sun.COM 	return (retval);
8007978SPeter.Dunlap@Sun.COM }
8017978SPeter.Dunlap@Sun.COM 
8027978SPeter.Dunlap@Sun.COM /*
8037978SPeter.Dunlap@Sun.COM  * iscsit_disable_svc
8047978SPeter.Dunlap@Sun.COM  *
8057978SPeter.Dunlap@Sun.COM  * clean up all existing connections and deregister targets from STMF
8067978SPeter.Dunlap@Sun.COM  */
8077978SPeter.Dunlap@Sun.COM static void
iscsit_disable_svc(void)8087978SPeter.Dunlap@Sun.COM iscsit_disable_svc(void)
8097978SPeter.Dunlap@Sun.COM {
8107978SPeter.Dunlap@Sun.COM 	iscsit_sess_t	*sess;
8117978SPeter.Dunlap@Sun.COM 
8127978SPeter.Dunlap@Sun.COM 	ASSERT(iscsit_global.global_svc_state == ISE_DISABLING);
8137978SPeter.Dunlap@Sun.COM 
81412372SPriya.Krishnan@Sun.COM 	iscsit_rxpdu_queue_monitor_stop();
81512372SPriya.Krishnan@Sun.COM 
8167978SPeter.Dunlap@Sun.COM 	/* tear down discovery sessions */
8177978SPeter.Dunlap@Sun.COM 	for (sess = avl_first(&iscsit_global.global_discovery_sessions);
8187978SPeter.Dunlap@Sun.COM 	    sess != NULL;
8197978SPeter.Dunlap@Sun.COM 	    sess = AVL_NEXT(&iscsit_global.global_discovery_sessions, sess))
8207978SPeter.Dunlap@Sun.COM 		iscsit_sess_close(sess);
8217978SPeter.Dunlap@Sun.COM 
8227978SPeter.Dunlap@Sun.COM 	/*
8237978SPeter.Dunlap@Sun.COM 	 * Passing NULL to iscsit_config_merge tells it to go to an empty
8247978SPeter.Dunlap@Sun.COM 	 * config.
8257978SPeter.Dunlap@Sun.COM 	 */
8267978SPeter.Dunlap@Sun.COM 	(void) iscsit_config_merge(NULL);
8277978SPeter.Dunlap@Sun.COM 
8287978SPeter.Dunlap@Sun.COM 	/*
8297978SPeter.Dunlap@Sun.COM 	 * Wait until there are no more global references
8307978SPeter.Dunlap@Sun.COM 	 */
8317978SPeter.Dunlap@Sun.COM 	idm_refcnt_wait_ref(&iscsit_global.global_refcnt);
8327978SPeter.Dunlap@Sun.COM 	idm_refcnt_destroy(&iscsit_global.global_refcnt);
8337978SPeter.Dunlap@Sun.COM 
8347978SPeter.Dunlap@Sun.COM 	/*
8357978SPeter.Dunlap@Sun.COM 	 * Default TPG must be destroyed after global_refcnt is 0.
8367978SPeter.Dunlap@Sun.COM 	 */
8377978SPeter.Dunlap@Sun.COM 	iscsit_tpg_destroydefault(iscsit_global.global_default_tpg);
8387978SPeter.Dunlap@Sun.COM 
8397978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_discovery_sessions);
8407978SPeter.Dunlap@Sun.COM 	list_destroy(&iscsit_global.global_deleted_target_list);
8417978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_target_list);
8427978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_tpg_list);
8437978SPeter.Dunlap@Sun.COM 	avl_destroy(&iscsit_global.global_ini_list);
8447978SPeter.Dunlap@Sun.COM 
8457978SPeter.Dunlap@Sun.COM 	taskq_destroy(iscsit_global.global_dispatch_taskq);
8467978SPeter.Dunlap@Sun.COM 
8477978SPeter.Dunlap@Sun.COM 	iscsit_isns_fini();
8487978SPeter.Dunlap@Sun.COM 
8497978SPeter.Dunlap@Sun.COM 	stmf_free(iscsit_global.global_dbuf_store);
8507978SPeter.Dunlap@Sun.COM 	iscsit_global.global_dbuf_store = NULL;
8517978SPeter.Dunlap@Sun.COM 
8527978SPeter.Dunlap@Sun.COM 	(void) stmf_deregister_port_provider(iscsit_global.global_pp);
8537978SPeter.Dunlap@Sun.COM 	stmf_free(iscsit_global.global_pp);
8547978SPeter.Dunlap@Sun.COM 	iscsit_global.global_pp = NULL;
8557978SPeter.Dunlap@Sun.COM 
8567978SPeter.Dunlap@Sun.COM 	kmem_cache_destroy(iscsit_status_pdu_cache);
8577978SPeter.Dunlap@Sun.COM 	iscsit_status_pdu_cache = NULL;
8587978SPeter.Dunlap@Sun.COM 
8597978SPeter.Dunlap@Sun.COM 	vmem_destroy(iscsit_global.global_tsih_pool);
8607978SPeter.Dunlap@Sun.COM 	iscsit_global.global_tsih_pool = NULL;
8617978SPeter.Dunlap@Sun.COM }
8627978SPeter.Dunlap@Sun.COM 
8637978SPeter.Dunlap@Sun.COM void
iscsit_global_hold()8647978SPeter.Dunlap@Sun.COM iscsit_global_hold()
8657978SPeter.Dunlap@Sun.COM {
86611431SPeter.Cudhea@Sun.COM 	/*
86711431SPeter.Cudhea@Sun.COM 	 * To take out a global hold, we must either own the global
86811431SPeter.Cudhea@Sun.COM 	 * state mutex or we must be running inside of an ioctl that
86911431SPeter.Cudhea@Sun.COM 	 * has set the global state to ISE_BUSY, ISE_DISABLING, or
87011431SPeter.Cudhea@Sun.COM 	 * ISE_ENABLING.  We don't track the "owner" for these flags,
87111431SPeter.Cudhea@Sun.COM 	 * so just checking if they are set is enough for now.
87211431SPeter.Cudhea@Sun.COM 	 */
87311431SPeter.Cudhea@Sun.COM 	ASSERT((iscsit_global.global_svc_state == ISE_ENABLING) ||
87411431SPeter.Cudhea@Sun.COM 	    (iscsit_global.global_svc_state == ISE_DISABLING) ||
87511431SPeter.Cudhea@Sun.COM 	    (iscsit_global.global_svc_state == ISE_BUSY) ||
87611431SPeter.Cudhea@Sun.COM 	    MUTEX_HELD(&iscsit_global.global_state_mutex));
87711431SPeter.Cudhea@Sun.COM 
8787978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&iscsit_global.global_refcnt);
8797978SPeter.Dunlap@Sun.COM }
8807978SPeter.Dunlap@Sun.COM 
8817978SPeter.Dunlap@Sun.COM void
iscsit_global_rele()8827978SPeter.Dunlap@Sun.COM iscsit_global_rele()
8837978SPeter.Dunlap@Sun.COM {
8847978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele(&iscsit_global.global_refcnt);
8857978SPeter.Dunlap@Sun.COM }
8867978SPeter.Dunlap@Sun.COM 
8877978SPeter.Dunlap@Sun.COM void
iscsit_global_wait_ref()8887978SPeter.Dunlap@Sun.COM iscsit_global_wait_ref()
8897978SPeter.Dunlap@Sun.COM {
8907978SPeter.Dunlap@Sun.COM 	idm_refcnt_wait_ref(&iscsit_global.global_refcnt);
8917978SPeter.Dunlap@Sun.COM }
8927978SPeter.Dunlap@Sun.COM 
8937978SPeter.Dunlap@Sun.COM /*
8947978SPeter.Dunlap@Sun.COM  * IDM callbacks
8957978SPeter.Dunlap@Sun.COM  */
8967978SPeter.Dunlap@Sun.COM 
8977978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
8987978SPeter.Dunlap@Sun.COM void
iscsit_rx_pdu(idm_conn_t * ic,idm_pdu_t * rx_pdu)8997978SPeter.Dunlap@Sun.COM iscsit_rx_pdu(idm_conn_t *ic, idm_pdu_t *rx_pdu)
9007978SPeter.Dunlap@Sun.COM {
9017978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict = ic->ic_handle;
9027978SPeter.Dunlap@Sun.COM 	switch (IDM_PDU_OPCODE(rx_pdu)) {
9037978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_CMD:
9047978SPeter.Dunlap@Sun.COM 		ASSERT(0); /* Shouldn't happen */
9057978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
9067978SPeter.Dunlap@Sun.COM 		break;
9077978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SNACK_CMD:
9087978SPeter.Dunlap@Sun.COM 		/*
9097978SPeter.Dunlap@Sun.COM 		 * We'll need to handle this when we support ERL1/2.  For
9107978SPeter.Dunlap@Sun.COM 		 * now we treat it as a protocol error.
9117978SPeter.Dunlap@Sun.COM 		 */
9127978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
9137978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
9147978SPeter.Dunlap@Sun.COM 		break;
9157978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
91612372SPriya.Krishnan@Sun.COM 		if (iscsit_check_cmdsn_and_queue(rx_pdu)) {
91712372SPriya.Krishnan@Sun.COM 			iscsit_set_cmdsn(ict, rx_pdu);
91812372SPriya.Krishnan@Sun.COM 			iscsit_op_scsi_task_mgmt(ict, rx_pdu);
91912372SPriya.Krishnan@Sun.COM 		}
9207978SPeter.Dunlap@Sun.COM 		break;
9217978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_NOOP_OUT:
9227978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGIN_CMD:
9237978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_TEXT_CMD:
9247978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGOUT_CMD:
9257978SPeter.Dunlap@Sun.COM 		/*
9267978SPeter.Dunlap@Sun.COM 		 * If/when we switch to userland processing these PDU's
9277978SPeter.Dunlap@Sun.COM 		 * will be handled by iscsitd.
9287978SPeter.Dunlap@Sun.COM 		 */
9297978SPeter.Dunlap@Sun.COM 		iscsit_deferred_dispatch(rx_pdu);
9307978SPeter.Dunlap@Sun.COM 		break;
9317978SPeter.Dunlap@Sun.COM 	default:
9327978SPeter.Dunlap@Sun.COM 		/* Protocol error */
9337978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
9347978SPeter.Dunlap@Sun.COM 		idm_conn_event(ic, CE_TRANSPORT_FAIL, NULL);
9357978SPeter.Dunlap@Sun.COM 		break;
9367978SPeter.Dunlap@Sun.COM 	}
9377978SPeter.Dunlap@Sun.COM }
9387978SPeter.Dunlap@Sun.COM 
9397978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
9407978SPeter.Dunlap@Sun.COM void
iscsit_rx_pdu_error(idm_conn_t * ic,idm_pdu_t * rx_pdu,idm_status_t status)9417978SPeter.Dunlap@Sun.COM iscsit_rx_pdu_error(idm_conn_t *ic, idm_pdu_t *rx_pdu, idm_status_t status)
9427978SPeter.Dunlap@Sun.COM {
9437978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
9447978SPeter.Dunlap@Sun.COM }
9457978SPeter.Dunlap@Sun.COM 
9467978SPeter.Dunlap@Sun.COM void
iscsit_task_aborted(idm_task_t * idt,idm_status_t status)9477978SPeter.Dunlap@Sun.COM iscsit_task_aborted(idm_task_t *idt, idm_status_t status)
9487978SPeter.Dunlap@Sun.COM {
9497978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask = idt->idt_private;
9507978SPeter.Dunlap@Sun.COM 
9517978SPeter.Dunlap@Sun.COM 	switch (status) {
9527978SPeter.Dunlap@Sun.COM 	case IDM_STATUS_SUSPENDED:
9537978SPeter.Dunlap@Sun.COM 		break;
9547978SPeter.Dunlap@Sun.COM 	case IDM_STATUS_ABORTED:
9557978SPeter.Dunlap@Sun.COM 		mutex_enter(&itask->it_mutex);
9567978SPeter.Dunlap@Sun.COM 		itask->it_aborted = B_TRUE;
9578062SPeter.Dunlap@Sun.COM 		/*
9588062SPeter.Dunlap@Sun.COM 		 * We rely on the fact that STMF tracks outstanding
9598062SPeter.Dunlap@Sun.COM 		 * buffer transfers and will free all of our buffers
9608062SPeter.Dunlap@Sun.COM 		 * before freeing the task so we don't need to
9618062SPeter.Dunlap@Sun.COM 		 * explicitly free the buffers from iscsit/idm
9628062SPeter.Dunlap@Sun.COM 		 */
9637978SPeter.Dunlap@Sun.COM 		if (itask->it_stmf_abort) {
9647978SPeter.Dunlap@Sun.COM 			mutex_exit(&itask->it_mutex);
9657978SPeter.Dunlap@Sun.COM 			/*
9669586SPeter.Dunlap@Sun.COM 			 * Task is no longer active
9679586SPeter.Dunlap@Sun.COM 			 */
9689586SPeter.Dunlap@Sun.COM 			iscsit_task_done(itask);
9699586SPeter.Dunlap@Sun.COM 
9709586SPeter.Dunlap@Sun.COM 			/*
9718062SPeter.Dunlap@Sun.COM 			 * STMF has already asked for this task to be aborted
9728062SPeter.Dunlap@Sun.COM 			 *
9737978SPeter.Dunlap@Sun.COM 			 * STMF specification is wrong... says to return
9747978SPeter.Dunlap@Sun.COM 			 * STMF_ABORTED, the code actually looks for
9757978SPeter.Dunlap@Sun.COM 			 * STMF_ABORT_SUCCESS.
9767978SPeter.Dunlap@Sun.COM 			 */
9777978SPeter.Dunlap@Sun.COM 			stmf_task_lport_aborted(itask->it_stmf_task,
9787978SPeter.Dunlap@Sun.COM 			    STMF_ABORT_SUCCESS, STMF_IOF_LPORT_DONE);
9797978SPeter.Dunlap@Sun.COM 			return;
9807978SPeter.Dunlap@Sun.COM 		} else {
9817978SPeter.Dunlap@Sun.COM 			mutex_exit(&itask->it_mutex);
9827978SPeter.Dunlap@Sun.COM 			/*
9838062SPeter.Dunlap@Sun.COM 			 * Tell STMF to stop processing the task.
9847978SPeter.Dunlap@Sun.COM 			 */
9857978SPeter.Dunlap@Sun.COM 			stmf_abort(STMF_QUEUE_TASK_ABORT, itask->it_stmf_task,
9867978SPeter.Dunlap@Sun.COM 			    STMF_ABORTED, NULL);
9877978SPeter.Dunlap@Sun.COM 			return;
9887978SPeter.Dunlap@Sun.COM 		}
9897978SPeter.Dunlap@Sun.COM 		/*NOTREACHED*/
9907978SPeter.Dunlap@Sun.COM 	default:
9917978SPeter.Dunlap@Sun.COM 		ASSERT(0);
9927978SPeter.Dunlap@Sun.COM 	}
9937978SPeter.Dunlap@Sun.COM }
9947978SPeter.Dunlap@Sun.COM 
9957978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
9967978SPeter.Dunlap@Sun.COM idm_status_t
iscsit_client_notify(idm_conn_t * ic,idm_client_notify_t icn,uintptr_t data)9977978SPeter.Dunlap@Sun.COM iscsit_client_notify(idm_conn_t *ic, idm_client_notify_t icn,
9987978SPeter.Dunlap@Sun.COM     uintptr_t data)
9997978SPeter.Dunlap@Sun.COM {
10007978SPeter.Dunlap@Sun.COM 	idm_status_t rc = IDM_STATUS_SUCCESS;
10017978SPeter.Dunlap@Sun.COM 
10027978SPeter.Dunlap@Sun.COM 	/*
10037978SPeter.Dunlap@Sun.COM 	 * IDM client notifications will never occur at interrupt level
10047978SPeter.Dunlap@Sun.COM 	 * since they are generated from the connection state machine which
10057978SPeter.Dunlap@Sun.COM 	 * running on taskq threads.
10067978SPeter.Dunlap@Sun.COM 	 *
10077978SPeter.Dunlap@Sun.COM 	 */
10087978SPeter.Dunlap@Sun.COM 	switch (icn) {
10097978SPeter.Dunlap@Sun.COM 	case CN_CONNECT_ACCEPT:
10107978SPeter.Dunlap@Sun.COM 		rc = iscsit_conn_accept(ic); /* No data */
10117978SPeter.Dunlap@Sun.COM 		break;
10127978SPeter.Dunlap@Sun.COM 	case CN_FFP_ENABLED:
10137978SPeter.Dunlap@Sun.COM 		rc = iscsit_ffp_enabled(ic); /* No data */
10147978SPeter.Dunlap@Sun.COM 		break;
10157978SPeter.Dunlap@Sun.COM 	case CN_FFP_DISABLED:
10167978SPeter.Dunlap@Sun.COM 		/*
10177978SPeter.Dunlap@Sun.COM 		 * Data indicates whether this was the result of an
10187978SPeter.Dunlap@Sun.COM 		 * explicit logout request.
10197978SPeter.Dunlap@Sun.COM 		 */
10207978SPeter.Dunlap@Sun.COM 		rc = iscsit_ffp_disabled(ic, (idm_ffp_disable_t)data);
10217978SPeter.Dunlap@Sun.COM 		break;
10227978SPeter.Dunlap@Sun.COM 	case CN_CONNECT_LOST:
10237978SPeter.Dunlap@Sun.COM 		rc = iscsit_conn_lost(ic);
10247978SPeter.Dunlap@Sun.COM 		break;
10257978SPeter.Dunlap@Sun.COM 	case CN_CONNECT_DESTROY:
10267978SPeter.Dunlap@Sun.COM 		rc = iscsit_conn_destroy(ic);
10277978SPeter.Dunlap@Sun.COM 		break;
10287978SPeter.Dunlap@Sun.COM 	case CN_LOGIN_FAIL:
10297978SPeter.Dunlap@Sun.COM 		/*
10307978SPeter.Dunlap@Sun.COM 		 * Force the login state machine to completion
10317978SPeter.Dunlap@Sun.COM 		 */
10327978SPeter.Dunlap@Sun.COM 		rc = iscsit_login_fail(ic);
10337978SPeter.Dunlap@Sun.COM 		break;
10347978SPeter.Dunlap@Sun.COM 	default:
10357978SPeter.Dunlap@Sun.COM 		rc = IDM_STATUS_REJECT;
10367978SPeter.Dunlap@Sun.COM 		break;
10377978SPeter.Dunlap@Sun.COM 	}
10387978SPeter.Dunlap@Sun.COM 
10397978SPeter.Dunlap@Sun.COM 	return (rc);
10407978SPeter.Dunlap@Sun.COM }
10417978SPeter.Dunlap@Sun.COM 
104211081SPriya.Krishnan@Sun.COM /*
104311081SPriya.Krishnan@Sun.COM  * iscsit_update_statsn is invoked for all the PDUs which have the StatSN
104411081SPriya.Krishnan@Sun.COM  * field in the header. The StatSN is incremented if the IDM_PDU_ADVANCE_STATSN
104511081SPriya.Krishnan@Sun.COM  * flag is set in the pdu flags field. The StatSN is connection-wide and is
104611081SPriya.Krishnan@Sun.COM  * protected by the mutex ict_statsn_mutex. For Data-In PDUs, if the flag
104711081SPriya.Krishnan@Sun.COM  * IDM_TASK_PHASECOLLAPSE_REQ is set, the status (phase-collapse) is also filled
104811081SPriya.Krishnan@Sun.COM  */
104911081SPriya.Krishnan@Sun.COM void
iscsit_update_statsn(idm_task_t * idm_task,idm_pdu_t * pdu)105011081SPriya.Krishnan@Sun.COM iscsit_update_statsn(idm_task_t *idm_task, idm_pdu_t *pdu)
105111081SPriya.Krishnan@Sun.COM {
105211081SPriya.Krishnan@Sun.COM 	iscsi_scsi_rsp_hdr_t *rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
105311081SPriya.Krishnan@Sun.COM 	iscsit_conn_t *ict = (iscsit_conn_t *)pdu->isp_ic->ic_handle;
105411081SPriya.Krishnan@Sun.COM 	iscsit_task_t *itask = NULL;
105511081SPriya.Krishnan@Sun.COM 	scsi_task_t *task = NULL;
105611081SPriya.Krishnan@Sun.COM 
105711081SPriya.Krishnan@Sun.COM 	mutex_enter(&ict->ict_statsn_mutex);
105811081SPriya.Krishnan@Sun.COM 	rsp->statsn = htonl(ict->ict_statsn);
105911081SPriya.Krishnan@Sun.COM 	if (pdu->isp_flags & IDM_PDU_ADVANCE_STATSN)
106011081SPriya.Krishnan@Sun.COM 		ict->ict_statsn++;
106111081SPriya.Krishnan@Sun.COM 	mutex_exit(&ict->ict_statsn_mutex);
106211081SPriya.Krishnan@Sun.COM 
106311081SPriya.Krishnan@Sun.COM 	/*
106411081SPriya.Krishnan@Sun.COM 	 * The last SCSI Data PDU passed for a command may also contain the
106511081SPriya.Krishnan@Sun.COM 	 * status if the status indicates termination with no expections, i.e.
106611081SPriya.Krishnan@Sun.COM 	 * no sense data or response involved. If the command completes with
106711081SPriya.Krishnan@Sun.COM 	 * an error, then the response and sense data will be sent in a
106811081SPriya.Krishnan@Sun.COM 	 * separate iSCSI Response PDU.
106911081SPriya.Krishnan@Sun.COM 	 */
107011081SPriya.Krishnan@Sun.COM 	if ((idm_task) && (idm_task->idt_flags & IDM_TASK_PHASECOLLAPSE_REQ)) {
107111081SPriya.Krishnan@Sun.COM 		itask = idm_task->idt_private;
107211081SPriya.Krishnan@Sun.COM 		task = itask->it_stmf_task;
107311081SPriya.Krishnan@Sun.COM 
107411081SPriya.Krishnan@Sun.COM 		rsp->cmd_status = task->task_scsi_status;
107511081SPriya.Krishnan@Sun.COM 		rsp->flags	|= ISCSI_FLAG_DATA_STATUS;
107611081SPriya.Krishnan@Sun.COM 		if (task->task_status_ctrl & TASK_SCTRL_OVER) {
107711081SPriya.Krishnan@Sun.COM 			rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
107811081SPriya.Krishnan@Sun.COM 		} else if (task->task_status_ctrl & TASK_SCTRL_UNDER) {
107911081SPriya.Krishnan@Sun.COM 			rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
108011081SPriya.Krishnan@Sun.COM 		}
108111081SPriya.Krishnan@Sun.COM 		rsp->residual_count = htonl(task->task_resid);
108211611SPriya.Krishnan@Sun.COM 
108311611SPriya.Krishnan@Sun.COM 		/*
108411611SPriya.Krishnan@Sun.COM 		 * Removing the task from the session task list
108511611SPriya.Krishnan@Sun.COM 		 * just before the status is sent in the last
108611611SPriya.Krishnan@Sun.COM 		 * Data PDU transfer
108711611SPriya.Krishnan@Sun.COM 		 */
108811611SPriya.Krishnan@Sun.COM 		iscsit_task_done(itask);
108911081SPriya.Krishnan@Sun.COM 	}
109011081SPriya.Krishnan@Sun.COM }
10917978SPeter.Dunlap@Sun.COM 
10927978SPeter.Dunlap@Sun.COM void
iscsit_build_hdr(idm_task_t * idm_task,idm_pdu_t * pdu,uint8_t opcode)10937978SPeter.Dunlap@Sun.COM iscsit_build_hdr(idm_task_t *idm_task, idm_pdu_t *pdu, uint8_t opcode)
10947978SPeter.Dunlap@Sun.COM {
10957978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask = idm_task->idt_private;
10967978SPeter.Dunlap@Sun.COM 	iscsi_data_rsp_hdr_t *dh = (iscsi_data_rsp_hdr_t *)pdu->isp_hdr;
10977978SPeter.Dunlap@Sun.COM 
10987978SPeter.Dunlap@Sun.COM 	/*
109912372SPriya.Krishnan@Sun.COM 	 * We acquired iscsit_sess_t.ist_sn_mutex in iscsit_xfer_scsi_data
11007978SPeter.Dunlap@Sun.COM 	 */
110112372SPriya.Krishnan@Sun.COM 	ASSERT(MUTEX_HELD(&itask->it_ict->ict_sess->ist_sn_mutex));
11027978SPeter.Dunlap@Sun.COM 	/*
110312661SPriya.Krishnan@Sun.COM 	 * On incoming data, the target transfer tag and Lun is only
110412661SPriya.Krishnan@Sun.COM 	 * provided by the target if the A bit is set, Since the target
110512661SPriya.Krishnan@Sun.COM 	 * does not currently support Error Recovery Level 1, the A
110612661SPriya.Krishnan@Sun.COM 	 * bit is never set.
11077978SPeter.Dunlap@Sun.COM 	 */
11087978SPeter.Dunlap@Sun.COM 	dh->opcode = opcode;
11097978SPeter.Dunlap@Sun.COM 	dh->itt = itask->it_itt;
111012661SPriya.Krishnan@Sun.COM 	dh->ttt = ((opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_DATA_RSP) ?
111112661SPriya.Krishnan@Sun.COM 	    ISCSI_RSVD_TASK_TAG : itask->it_ttt;
111211081SPriya.Krishnan@Sun.COM 
11137978SPeter.Dunlap@Sun.COM 	dh->expcmdsn = htonl(itask->it_ict->ict_sess->ist_expcmdsn);
11147978SPeter.Dunlap@Sun.COM 	dh->maxcmdsn = htonl(itask->it_ict->ict_sess->ist_maxcmdsn);
11157978SPeter.Dunlap@Sun.COM 
11167978SPeter.Dunlap@Sun.COM 	/*
11177978SPeter.Dunlap@Sun.COM 	 * IDM must set:
11187978SPeter.Dunlap@Sun.COM 	 *
11197978SPeter.Dunlap@Sun.COM 	 * data.flags and rtt.flags
11207978SPeter.Dunlap@Sun.COM 	 * data.dlength
11217978SPeter.Dunlap@Sun.COM 	 * data.datasn
11227978SPeter.Dunlap@Sun.COM 	 * data.offset
112311081SPriya.Krishnan@Sun.COM 	 * statsn, residual_count and cmd_status (for phase collapse)
11247978SPeter.Dunlap@Sun.COM 	 * rtt.rttsn
11257978SPeter.Dunlap@Sun.COM 	 * rtt.data_offset
11267978SPeter.Dunlap@Sun.COM 	 * rtt.data_length
11277978SPeter.Dunlap@Sun.COM 	 */
11287978SPeter.Dunlap@Sun.COM }
11297978SPeter.Dunlap@Sun.COM 
11309586SPeter.Dunlap@Sun.COM void
iscsit_keepalive(idm_conn_t * ic)11319586SPeter.Dunlap@Sun.COM iscsit_keepalive(idm_conn_t *ic)
11329586SPeter.Dunlap@Sun.COM {
11339586SPeter.Dunlap@Sun.COM 	idm_pdu_t		*nop_in_pdu;
11349586SPeter.Dunlap@Sun.COM 	iscsi_nop_in_hdr_t	*nop_in;
11359586SPeter.Dunlap@Sun.COM 	iscsit_conn_t		*ict = ic->ic_handle;
11369586SPeter.Dunlap@Sun.COM 
11379586SPeter.Dunlap@Sun.COM 	/*
11389586SPeter.Dunlap@Sun.COM 	 * IDM noticed the connection has been idle for too long so it's
11399586SPeter.Dunlap@Sun.COM 	 * time to provoke some activity.  Build and transmit an iSCSI
11409586SPeter.Dunlap@Sun.COM 	 * nop-in PDU -- when the initiator responds it will be counted
11419586SPeter.Dunlap@Sun.COM 	 * as "activity" and keep the connection alive.
11429586SPeter.Dunlap@Sun.COM 	 *
11439586SPeter.Dunlap@Sun.COM 	 * We don't actually care about the response here at the iscsit level
11449586SPeter.Dunlap@Sun.COM 	 * so we will just throw it away without looking at it when it arrives.
11459586SPeter.Dunlap@Sun.COM 	 */
11469586SPeter.Dunlap@Sun.COM 	nop_in_pdu = idm_pdu_alloc(sizeof (*nop_in), 0);
11479586SPeter.Dunlap@Sun.COM 	idm_pdu_init(nop_in_pdu, ic, NULL, NULL);
11489586SPeter.Dunlap@Sun.COM 	nop_in = (iscsi_nop_in_hdr_t *)nop_in_pdu->isp_hdr;
11499586SPeter.Dunlap@Sun.COM 	bzero(nop_in, sizeof (*nop_in));
11509586SPeter.Dunlap@Sun.COM 	nop_in->opcode = ISCSI_OP_NOOP_IN;
11519586SPeter.Dunlap@Sun.COM 	nop_in->flags = ISCSI_FLAG_FINAL;
11529586SPeter.Dunlap@Sun.COM 	nop_in->itt = ISCSI_RSVD_TASK_TAG;
11539586SPeter.Dunlap@Sun.COM 	/*
115411081SPriya.Krishnan@Sun.COM 	 * When the target sends a NOP-In as a Ping, the target transfer tag
115511081SPriya.Krishnan@Sun.COM 	 * is set to a valid (not reserved) value and the initiator task tag
115611081SPriya.Krishnan@Sun.COM 	 * is set to ISCSI_RSVD_TASK_TAG (0xffffffff). In this case the StatSN
115711081SPriya.Krishnan@Sun.COM 	 * will always contain the next sequence number but the StatSN for the
115811081SPriya.Krishnan@Sun.COM 	 * connection is not advanced after this PDU is sent.
115911081SPriya.Krishnan@Sun.COM 	 */
116011081SPriya.Krishnan@Sun.COM 	nop_in_pdu->isp_flags |= IDM_PDU_SET_STATSN;
116111081SPriya.Krishnan@Sun.COM 	/*
11629586SPeter.Dunlap@Sun.COM 	 * This works because we don't currently allocate ttt's anywhere else
11639586SPeter.Dunlap@Sun.COM 	 * in iscsit so as long as we stay out of IDM's range we are safe.
11649586SPeter.Dunlap@Sun.COM 	 * If we need to allocate ttt's for other PDU's in the future this will
11659586SPeter.Dunlap@Sun.COM 	 * need to be improved.
11669586SPeter.Dunlap@Sun.COM 	 */
11679586SPeter.Dunlap@Sun.COM 	mutex_enter(&ict->ict_mutex);
11689586SPeter.Dunlap@Sun.COM 	nop_in->ttt = ict->ict_keepalive_ttt;
11699586SPeter.Dunlap@Sun.COM 	ict->ict_keepalive_ttt++;
11709586SPeter.Dunlap@Sun.COM 	if (ict->ict_keepalive_ttt == ISCSI_RSVD_TASK_TAG)
11719586SPeter.Dunlap@Sun.COM 		ict->ict_keepalive_ttt = IDM_TASKIDS_MAX;
11729586SPeter.Dunlap@Sun.COM 	mutex_exit(&ict->ict_mutex);
11739586SPeter.Dunlap@Sun.COM 
11749586SPeter.Dunlap@Sun.COM 	iscsit_pdu_tx(nop_in_pdu);
11759586SPeter.Dunlap@Sun.COM }
11769586SPeter.Dunlap@Sun.COM 
11777978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_conn_accept(idm_conn_t * ic)11787978SPeter.Dunlap@Sun.COM iscsit_conn_accept(idm_conn_t *ic)
11797978SPeter.Dunlap@Sun.COM {
11807978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict;
11817978SPeter.Dunlap@Sun.COM 
11827978SPeter.Dunlap@Sun.COM 	/*
11839162SPeter.Dunlap@Sun.COM 	 * We need to get a global hold here to ensure that the service
11849162SPeter.Dunlap@Sun.COM 	 * doesn't get shutdown prior to establishing a session. This
11859162SPeter.Dunlap@Sun.COM 	 * gets released in iscsit_conn_destroy().
11869162SPeter.Dunlap@Sun.COM 	 */
118711431SPeter.Cudhea@Sun.COM 	mutex_enter(&iscsit_global.global_state_mutex);
11889373SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_svc_state != ISE_ENABLED) {
118911431SPeter.Cudhea@Sun.COM 		mutex_exit(&iscsit_global.global_state_mutex);
11909373SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
11919373SPeter.Dunlap@Sun.COM 	}
11929162SPeter.Dunlap@Sun.COM 	iscsit_global_hold();
119311431SPeter.Cudhea@Sun.COM 	mutex_exit(&iscsit_global.global_state_mutex);
11949162SPeter.Dunlap@Sun.COM 
11959162SPeter.Dunlap@Sun.COM 	/*
11967978SPeter.Dunlap@Sun.COM 	 * Allocate an associated iscsit structure to represent this
11977978SPeter.Dunlap@Sun.COM 	 * connection.  We shouldn't really create a session until we
11987978SPeter.Dunlap@Sun.COM 	 * get the first login PDU.
11997978SPeter.Dunlap@Sun.COM 	 */
12007978SPeter.Dunlap@Sun.COM 	ict = kmem_zalloc(sizeof (*ict), KM_SLEEP);
12017978SPeter.Dunlap@Sun.COM 
12027978SPeter.Dunlap@Sun.COM 	ict->ict_ic = ic;
12037978SPeter.Dunlap@Sun.COM 	ict->ict_statsn = 1;
12049586SPeter.Dunlap@Sun.COM 	ict->ict_keepalive_ttt = IDM_TASKIDS_MAX; /* Avoid IDM TT range */
12057978SPeter.Dunlap@Sun.COM 	ic->ic_handle = ict;
12067978SPeter.Dunlap@Sun.COM 	mutex_init(&ict->ict_mutex, NULL, MUTEX_DRIVER, NULL);
120711081SPriya.Krishnan@Sun.COM 	mutex_init(&ict->ict_statsn_mutex, NULL, MUTEX_DRIVER, NULL);
12087978SPeter.Dunlap@Sun.COM 	idm_refcnt_init(&ict->ict_refcnt, ict);
12097978SPeter.Dunlap@Sun.COM 
12107978SPeter.Dunlap@Sun.COM 	/*
12117978SPeter.Dunlap@Sun.COM 	 * Initialize login state machine
12127978SPeter.Dunlap@Sun.COM 	 */
12137978SPeter.Dunlap@Sun.COM 	if (iscsit_login_sm_init(ict) != IDM_STATUS_SUCCESS) {
12149373SPeter.Dunlap@Sun.COM 		iscsit_global_rele();
12159601SJames.Moore@Sun.COM 		/*
12169601SJames.Moore@Sun.COM 		 * Cleanup the ict after idm notifies us about this failure
12179601SJames.Moore@Sun.COM 		 */
12187978SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_FAIL);
12197978SPeter.Dunlap@Sun.COM 	}
12207978SPeter.Dunlap@Sun.COM 
12217978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
12227978SPeter.Dunlap@Sun.COM }
12237978SPeter.Dunlap@Sun.COM 
12247978SPeter.Dunlap@Sun.COM idm_status_t
iscsit_conn_reinstate(iscsit_conn_t * reinstate_ict,iscsit_conn_t * new_ict)12257978SPeter.Dunlap@Sun.COM iscsit_conn_reinstate(iscsit_conn_t *reinstate_ict, iscsit_conn_t *new_ict)
12267978SPeter.Dunlap@Sun.COM {
12277978SPeter.Dunlap@Sun.COM 	idm_status_t	result;
12287978SPeter.Dunlap@Sun.COM 
12297978SPeter.Dunlap@Sun.COM 	/*
12307978SPeter.Dunlap@Sun.COM 	 * Note in new connection state that this connection is
12317978SPeter.Dunlap@Sun.COM 	 * reinstating an existing connection.
12327978SPeter.Dunlap@Sun.COM 	 */
12337978SPeter.Dunlap@Sun.COM 	new_ict->ict_reinstating = B_TRUE;
12347978SPeter.Dunlap@Sun.COM 	new_ict->ict_reinstate_conn = reinstate_ict;
12357978SPeter.Dunlap@Sun.COM 	new_ict->ict_statsn = reinstate_ict->ict_statsn;
12367978SPeter.Dunlap@Sun.COM 
12377978SPeter.Dunlap@Sun.COM 	/*
12387978SPeter.Dunlap@Sun.COM 	 * Now generate connection state machine event to existing connection
12397978SPeter.Dunlap@Sun.COM 	 * so that it starts the cleanup process.
12407978SPeter.Dunlap@Sun.COM 	 */
12417978SPeter.Dunlap@Sun.COM 	result = idm_conn_reinstate_event(reinstate_ict->ict_ic,
12427978SPeter.Dunlap@Sun.COM 	    new_ict->ict_ic);
12437978SPeter.Dunlap@Sun.COM 
12447978SPeter.Dunlap@Sun.COM 	return (result);
12457978SPeter.Dunlap@Sun.COM }
12467978SPeter.Dunlap@Sun.COM 
12477978SPeter.Dunlap@Sun.COM void
iscsit_conn_hold(iscsit_conn_t * ict)12487978SPeter.Dunlap@Sun.COM iscsit_conn_hold(iscsit_conn_t *ict)
12497978SPeter.Dunlap@Sun.COM {
12507978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&ict->ict_refcnt);
12517978SPeter.Dunlap@Sun.COM }
12527978SPeter.Dunlap@Sun.COM 
12537978SPeter.Dunlap@Sun.COM void
iscsit_conn_rele(iscsit_conn_t * ict)12547978SPeter.Dunlap@Sun.COM iscsit_conn_rele(iscsit_conn_t *ict)
12557978SPeter.Dunlap@Sun.COM {
12567978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele(&ict->ict_refcnt);
12577978SPeter.Dunlap@Sun.COM }
12587978SPeter.Dunlap@Sun.COM 
12597978SPeter.Dunlap@Sun.COM void
iscsit_conn_dispatch_hold(iscsit_conn_t * ict)12607978SPeter.Dunlap@Sun.COM iscsit_conn_dispatch_hold(iscsit_conn_t *ict)
12617978SPeter.Dunlap@Sun.COM {
12627978SPeter.Dunlap@Sun.COM 	idm_refcnt_hold(&ict->ict_dispatch_refcnt);
12637978SPeter.Dunlap@Sun.COM }
12647978SPeter.Dunlap@Sun.COM 
12657978SPeter.Dunlap@Sun.COM void
iscsit_conn_dispatch_rele(iscsit_conn_t * ict)12667978SPeter.Dunlap@Sun.COM iscsit_conn_dispatch_rele(iscsit_conn_t *ict)
12677978SPeter.Dunlap@Sun.COM {
12687978SPeter.Dunlap@Sun.COM 	idm_refcnt_rele(&ict->ict_dispatch_refcnt);
12697978SPeter.Dunlap@Sun.COM }
12707978SPeter.Dunlap@Sun.COM 
12717978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_login_fail(idm_conn_t * ic)12727978SPeter.Dunlap@Sun.COM iscsit_login_fail(idm_conn_t *ic)
12737978SPeter.Dunlap@Sun.COM {
12747978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict = ic->ic_handle;
12757978SPeter.Dunlap@Sun.COM 
12767978SPeter.Dunlap@Sun.COM 	/* Generate login state machine event */
12777978SPeter.Dunlap@Sun.COM 	iscsit_login_sm_event(ict, ILE_LOGIN_CONN_ERROR, NULL);
12787978SPeter.Dunlap@Sun.COM 
12797978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
12807978SPeter.Dunlap@Sun.COM }
12817978SPeter.Dunlap@Sun.COM 
12827978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_ffp_enabled(idm_conn_t * ic)12837978SPeter.Dunlap@Sun.COM iscsit_ffp_enabled(idm_conn_t *ic)
12847978SPeter.Dunlap@Sun.COM {
12857978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict = ic->ic_handle;
12867978SPeter.Dunlap@Sun.COM 
12877978SPeter.Dunlap@Sun.COM 	/* Generate session state machine event */
12887978SPeter.Dunlap@Sun.COM 	iscsit_sess_sm_event(ict->ict_sess, SE_CONN_LOGGED_IN, ict);
12897978SPeter.Dunlap@Sun.COM 
12907978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
12917978SPeter.Dunlap@Sun.COM }
12927978SPeter.Dunlap@Sun.COM 
12937978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_ffp_disabled(idm_conn_t * ic,idm_ffp_disable_t disable_class)12947978SPeter.Dunlap@Sun.COM iscsit_ffp_disabled(idm_conn_t *ic, idm_ffp_disable_t disable_class)
12957978SPeter.Dunlap@Sun.COM {
12967978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict = ic->ic_handle;
12977978SPeter.Dunlap@Sun.COM 
12987978SPeter.Dunlap@Sun.COM 	/* Generate session state machine event */
12997978SPeter.Dunlap@Sun.COM 	switch (disable_class) {
13007978SPeter.Dunlap@Sun.COM 	case FD_CONN_FAIL:
13017978SPeter.Dunlap@Sun.COM 		iscsit_sess_sm_event(ict->ict_sess, SE_CONN_FFP_FAIL, ict);
13027978SPeter.Dunlap@Sun.COM 		break;
13037978SPeter.Dunlap@Sun.COM 	case FD_CONN_LOGOUT:
13047978SPeter.Dunlap@Sun.COM 		iscsit_sess_sm_event(ict->ict_sess, SE_CONN_FFP_DISABLE, ict);
13057978SPeter.Dunlap@Sun.COM 		break;
13067978SPeter.Dunlap@Sun.COM 	case FD_SESS_LOGOUT:
13077978SPeter.Dunlap@Sun.COM 		iscsit_sess_sm_event(ict->ict_sess, SE_SESSION_CLOSE, ict);
13087978SPeter.Dunlap@Sun.COM 		break;
13097978SPeter.Dunlap@Sun.COM 	default:
13107978SPeter.Dunlap@Sun.COM 		ASSERT(0);
13117978SPeter.Dunlap@Sun.COM 	}
13127978SPeter.Dunlap@Sun.COM 
13137978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
13147978SPeter.Dunlap@Sun.COM }
13157978SPeter.Dunlap@Sun.COM 
13167978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_conn_lost(idm_conn_t * ic)13177978SPeter.Dunlap@Sun.COM iscsit_conn_lost(idm_conn_t *ic)
13187978SPeter.Dunlap@Sun.COM {
131912372SPriya.Krishnan@Sun.COM 	iscsit_conn_t	*ict	= ic->ic_handle;
132012372SPriya.Krishnan@Sun.COM 	iscsit_sess_t	*ist	= ict->ict_sess;
132112372SPriya.Krishnan@Sun.COM 	iscsit_cbuf_t	*cbuf;
132212372SPriya.Krishnan@Sun.COM 	idm_pdu_t	*rx_pdu;
132312372SPriya.Krishnan@Sun.COM 	int i;
13247978SPeter.Dunlap@Sun.COM 
13257978SPeter.Dunlap@Sun.COM 	mutex_enter(&ict->ict_mutex);
13267978SPeter.Dunlap@Sun.COM 	ict->ict_lost = B_TRUE;
13277978SPeter.Dunlap@Sun.COM 	mutex_exit(&ict->ict_mutex);
132812372SPriya.Krishnan@Sun.COM 	/*
132912372SPriya.Krishnan@Sun.COM 	 * scrub the staging queue for all PDUs on this connection
133012372SPriya.Krishnan@Sun.COM 	 */
133112372SPriya.Krishnan@Sun.COM 	if (ist != NULL) {
133212372SPriya.Krishnan@Sun.COM 		mutex_enter(&ist->ist_sn_mutex);
133312372SPriya.Krishnan@Sun.COM 		for (cbuf = ist->ist_rxpdu_queue, i = 0;
133412372SPriya.Krishnan@Sun.COM 		    ((cbuf->cb_num_elems > 0) && (i < ISCSIT_RXPDU_QUEUE_LEN));
133512372SPriya.Krishnan@Sun.COM 		    i++) {
133612372SPriya.Krishnan@Sun.COM 			if (((rx_pdu = cbuf->cb_buffer[i]) != NULL) &&
133712372SPriya.Krishnan@Sun.COM 			    (rx_pdu->isp_ic == ic)) {
133812372SPriya.Krishnan@Sun.COM 				/* conn is lost, drop the pdu */
133912372SPriya.Krishnan@Sun.COM 				DTRACE_PROBE3(scrubbing__staging__queue,
134012372SPriya.Krishnan@Sun.COM 				    iscsit_sess_t *, ist, idm_conn_t *, ic,
134112372SPriya.Krishnan@Sun.COM 				    idm_pdu_t *, rx_pdu);
134212372SPriya.Krishnan@Sun.COM 				idm_pdu_complete(rx_pdu, IDM_STATUS_FAIL);
134312372SPriya.Krishnan@Sun.COM 				cbuf->cb_buffer[i] = NULL;
134412372SPriya.Krishnan@Sun.COM 				cbuf->cb_num_elems--;
134512372SPriya.Krishnan@Sun.COM 				iscsit_conn_dispatch_rele(ict);
134612372SPriya.Krishnan@Sun.COM 			}
134712372SPriya.Krishnan@Sun.COM 		}
134812372SPriya.Krishnan@Sun.COM 		mutex_exit(&ist->ist_sn_mutex);
134912372SPriya.Krishnan@Sun.COM 	}
13507978SPeter.Dunlap@Sun.COM 	/*
13517978SPeter.Dunlap@Sun.COM 	 * Make sure there aren't any PDU's transitioning from the receive
13527978SPeter.Dunlap@Sun.COM 	 * handler to the dispatch taskq.
13537978SPeter.Dunlap@Sun.COM 	 */
13547978SPeter.Dunlap@Sun.COM 	idm_refcnt_wait_ref(&ict->ict_dispatch_refcnt);
13557978SPeter.Dunlap@Sun.COM 
13567978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
13577978SPeter.Dunlap@Sun.COM }
13587978SPeter.Dunlap@Sun.COM 
13597978SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_conn_destroy(idm_conn_t * ic)13607978SPeter.Dunlap@Sun.COM iscsit_conn_destroy(idm_conn_t *ic)
13617978SPeter.Dunlap@Sun.COM {
13627978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict = ic->ic_handle;
13637978SPeter.Dunlap@Sun.COM 
13647978SPeter.Dunlap@Sun.COM 	mutex_enter(&ict->ict_mutex);
13657978SPeter.Dunlap@Sun.COM 	ict->ict_destroyed = B_TRUE;
13667978SPeter.Dunlap@Sun.COM 	mutex_exit(&ict->ict_mutex);
13677978SPeter.Dunlap@Sun.COM 
13687978SPeter.Dunlap@Sun.COM 	/* Generate session state machine event */
13697978SPeter.Dunlap@Sun.COM 	if (ict->ict_sess != NULL) {
13707978SPeter.Dunlap@Sun.COM 		/*
13717978SPeter.Dunlap@Sun.COM 		 * Session state machine will call iscsit_conn_destroy_done()
13727978SPeter.Dunlap@Sun.COM 		 * when it has removed references to this connection.
13737978SPeter.Dunlap@Sun.COM 		 */
13747978SPeter.Dunlap@Sun.COM 		iscsit_sess_sm_event(ict->ict_sess, SE_CONN_FAIL, ict);
13757978SPeter.Dunlap@Sun.COM 	}
13767978SPeter.Dunlap@Sun.COM 
137712579SPriya.Krishnan@Sun.COM 	idm_refcnt_wait_ref(&ict->ict_refcnt);
137812579SPriya.Krishnan@Sun.COM 	/*
137912579SPriya.Krishnan@Sun.COM 	 * The session state machine does not need to post
138012579SPriya.Krishnan@Sun.COM 	 * events to IDM any longer, so it is safe to set
138112579SPriya.Krishnan@Sun.COM 	 * the idm connection reference to NULL
138212579SPriya.Krishnan@Sun.COM 	 */
13837978SPeter.Dunlap@Sun.COM 	ict->ict_ic = NULL;
13847978SPeter.Dunlap@Sun.COM 
13859601SJames.Moore@Sun.COM 	/* Reap the login state machine */
13869601SJames.Moore@Sun.COM 	iscsit_login_sm_fini(ict);
13879601SJames.Moore@Sun.COM 
13889601SJames.Moore@Sun.COM 	/* Clean up any text command remnants */
13899601SJames.Moore@Sun.COM 	iscsit_text_cmd_fini(ict);
13909601SJames.Moore@Sun.COM 
13917978SPeter.Dunlap@Sun.COM 	mutex_destroy(&ict->ict_mutex);
13927978SPeter.Dunlap@Sun.COM 	idm_refcnt_destroy(&ict->ict_refcnt);
13937978SPeter.Dunlap@Sun.COM 	kmem_free(ict, sizeof (*ict));
13947978SPeter.Dunlap@Sun.COM 
13959162SPeter.Dunlap@Sun.COM 	iscsit_global_rele();
13969162SPeter.Dunlap@Sun.COM 
13977978SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_SUCCESS);
13987978SPeter.Dunlap@Sun.COM }
13997978SPeter.Dunlap@Sun.COM 
140012579SPriya.Krishnan@Sun.COM void
iscsit_conn_logout(iscsit_conn_t * ict)140112579SPriya.Krishnan@Sun.COM iscsit_conn_logout(iscsit_conn_t *ict)
140212579SPriya.Krishnan@Sun.COM {
140312579SPriya.Krishnan@Sun.COM 	/*
140412579SPriya.Krishnan@Sun.COM 	 * If the iscsi connection is active, then
140512579SPriya.Krishnan@Sun.COM 	 * logout the IDM connection by sending a
140612579SPriya.Krishnan@Sun.COM 	 * CE_LOGOUT_SESSION_SUCCESS, else, no action
140712579SPriya.Krishnan@Sun.COM 	 * needs to be taken because the connection
140812579SPriya.Krishnan@Sun.COM 	 * is already in the teardown process.
140912579SPriya.Krishnan@Sun.COM 	 */
141012579SPriya.Krishnan@Sun.COM 	mutex_enter(&ict->ict_mutex);
141112579SPriya.Krishnan@Sun.COM 	if (ict->ict_lost == B_FALSE && ict->ict_destroyed == B_FALSE) {
141212579SPriya.Krishnan@Sun.COM 		idm_conn_event(ict->ict_ic, CE_LOGOUT_SESSION_SUCCESS, NULL);
141312579SPriya.Krishnan@Sun.COM 	}
141412579SPriya.Krishnan@Sun.COM 	mutex_exit(&ict->ict_mutex);
141512579SPriya.Krishnan@Sun.COM }
141612579SPriya.Krishnan@Sun.COM 
14177978SPeter.Dunlap@Sun.COM /*
14187978SPeter.Dunlap@Sun.COM  * STMF-related functions
14197978SPeter.Dunlap@Sun.COM  *
14207978SPeter.Dunlap@Sun.COM  * iSCSI to STMF mapping
14217978SPeter.Dunlap@Sun.COM  *
14227978SPeter.Dunlap@Sun.COM  * Session == ?
14237978SPeter.Dunlap@Sun.COM  * Connection == bound to local port but not itself a local port
14247978SPeter.Dunlap@Sun.COM  * Target
14257978SPeter.Dunlap@Sun.COM  * Target portal (group?) == local port (really but we're not going to do this)
14267978SPeter.Dunlap@Sun.COM  *	iscsit needs to map connections to local ports (whatever we decide
14277978SPeter.Dunlap@Sun.COM  * 	they are)
14287978SPeter.Dunlap@Sun.COM  * Target == ?
14297978SPeter.Dunlap@Sun.COM  */
14307978SPeter.Dunlap@Sun.COM 
14317978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
14327978SPeter.Dunlap@Sun.COM static stmf_data_buf_t *
iscsit_dbuf_alloc(scsi_task_t * task,uint32_t size,uint32_t * pminsize,uint32_t flags)14337978SPeter.Dunlap@Sun.COM iscsit_dbuf_alloc(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
14347978SPeter.Dunlap@Sun.COM     uint32_t flags)
14357978SPeter.Dunlap@Sun.COM {
14367978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask = task->task_port_private;
14377978SPeter.Dunlap@Sun.COM 	idm_buf_t *idm_buffer;
14387978SPeter.Dunlap@Sun.COM 	iscsit_buf_t	*ibuf;
14397978SPeter.Dunlap@Sun.COM 	stmf_data_buf_t *result;
144010325SPriya.Krishnan@Sun.COM 	uint32_t	bsize;
14417978SPeter.Dunlap@Sun.COM 
14427978SPeter.Dunlap@Sun.COM 	/*
144310502SPriya.Krishnan@Sun.COM 	 * If the requested size is larger than MaxBurstLength and the
144410502SPriya.Krishnan@Sun.COM 	 * given pminsize is also larger than MaxBurstLength, then the
144510502SPriya.Krishnan@Sun.COM 	 * allocation fails (dbuf = NULL) and pminsize is modified to
144610502SPriya.Krishnan@Sun.COM 	 * be equal to MaxBurstLength. stmf/sbd then should re-invoke
144710502SPriya.Krishnan@Sun.COM 	 * this function with the corrected values for transfer.
14487978SPeter.Dunlap@Sun.COM 	 */
144910325SPriya.Krishnan@Sun.COM 	ASSERT(pminsize);
145010502SPriya.Krishnan@Sun.COM 	if (size <= itask->it_ict->ict_op.op_max_burst_length) {
145110502SPriya.Krishnan@Sun.COM 		bsize = size;
145210502SPriya.Krishnan@Sun.COM 	} else if (*pminsize <= itask->it_ict->ict_op.op_max_burst_length) {
145310502SPriya.Krishnan@Sun.COM 		bsize = itask->it_ict->ict_op.op_max_burst_length;
145410502SPriya.Krishnan@Sun.COM 	} else {
14557978SPeter.Dunlap@Sun.COM 		*pminsize = itask->it_ict->ict_op.op_max_burst_length;
14567978SPeter.Dunlap@Sun.COM 		return (NULL);
14577978SPeter.Dunlap@Sun.COM 	}
14587978SPeter.Dunlap@Sun.COM 
14597978SPeter.Dunlap@Sun.COM 	/* Alloc buffer */
146010325SPriya.Krishnan@Sun.COM 	idm_buffer = idm_buf_alloc(itask->it_ict->ict_ic, NULL, bsize);
14617978SPeter.Dunlap@Sun.COM 	if (idm_buffer != NULL) {
14627978SPeter.Dunlap@Sun.COM 		result = stmf_alloc(STMF_STRUCT_DATA_BUF,
14637978SPeter.Dunlap@Sun.COM 		    sizeof (iscsit_buf_t), 0);
14647978SPeter.Dunlap@Sun.COM 		if (result != NULL) {
14657978SPeter.Dunlap@Sun.COM 			/* Fill in stmf_data_buf_t */
14667978SPeter.Dunlap@Sun.COM 			ibuf = result->db_port_private;
14677978SPeter.Dunlap@Sun.COM 			ibuf->ibuf_idm_buf = idm_buffer;
14687978SPeter.Dunlap@Sun.COM 			ibuf->ibuf_stmf_buf = result;
14697978SPeter.Dunlap@Sun.COM 			ibuf->ibuf_is_immed = B_FALSE;
14707978SPeter.Dunlap@Sun.COM 			result->db_flags = DB_DONT_CACHE;
147110325SPriya.Krishnan@Sun.COM 			result->db_buf_size = bsize;
147210325SPriya.Krishnan@Sun.COM 			result->db_data_size = bsize;
14737978SPeter.Dunlap@Sun.COM 			result->db_sglist_length = 1;
14747978SPeter.Dunlap@Sun.COM 			result->db_sglist[0].seg_addr = idm_buffer->idb_buf;
14757978SPeter.Dunlap@Sun.COM 			result->db_sglist[0].seg_length =
14767978SPeter.Dunlap@Sun.COM 			    idm_buffer->idb_buflen;
14777978SPeter.Dunlap@Sun.COM 			return (result);
14787978SPeter.Dunlap@Sun.COM 		}
14797978SPeter.Dunlap@Sun.COM 
14807978SPeter.Dunlap@Sun.COM 		/* Couldn't get the stmf_data_buf_t so free the buffer */
14817978SPeter.Dunlap@Sun.COM 		idm_buf_free(idm_buffer);
14827978SPeter.Dunlap@Sun.COM 	}
14837978SPeter.Dunlap@Sun.COM 
14847978SPeter.Dunlap@Sun.COM 	return (NULL);
14857978SPeter.Dunlap@Sun.COM }
14867978SPeter.Dunlap@Sun.COM 
14877978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
14887978SPeter.Dunlap@Sun.COM static void
iscsit_dbuf_free(stmf_dbuf_store_t * ds,stmf_data_buf_t * dbuf)14897978SPeter.Dunlap@Sun.COM iscsit_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
14907978SPeter.Dunlap@Sun.COM {
14917978SPeter.Dunlap@Sun.COM 	iscsit_buf_t *ibuf = dbuf->db_port_private;
14927978SPeter.Dunlap@Sun.COM 
14937978SPeter.Dunlap@Sun.COM 	if (ibuf->ibuf_is_immed) {
14947978SPeter.Dunlap@Sun.COM 		/*
14957978SPeter.Dunlap@Sun.COM 		 * The iscsit_buf_t structure itself will be freed with its
14967978SPeter.Dunlap@Sun.COM 		 * associated task.  Here we just need to free the PDU that
14977978SPeter.Dunlap@Sun.COM 		 * held the immediate data.
14987978SPeter.Dunlap@Sun.COM 		 */
14997978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(ibuf->ibuf_immed_data_pdu, IDM_STATUS_SUCCESS);
15007978SPeter.Dunlap@Sun.COM 		ibuf->ibuf_immed_data_pdu = 0;
15017978SPeter.Dunlap@Sun.COM 	} else {
15027978SPeter.Dunlap@Sun.COM 		idm_buf_free(ibuf->ibuf_idm_buf);
15037978SPeter.Dunlap@Sun.COM 		stmf_free(dbuf);
15047978SPeter.Dunlap@Sun.COM 	}
15057978SPeter.Dunlap@Sun.COM }
15067978SPeter.Dunlap@Sun.COM 
15077978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
15087978SPeter.Dunlap@Sun.COM stmf_status_t
iscsit_xfer_scsi_data(scsi_task_t * task,stmf_data_buf_t * dbuf,uint32_t ioflags)15097978SPeter.Dunlap@Sun.COM iscsit_xfer_scsi_data(scsi_task_t *task, stmf_data_buf_t *dbuf,
15107978SPeter.Dunlap@Sun.COM     uint32_t ioflags)
15117978SPeter.Dunlap@Sun.COM {
15127978SPeter.Dunlap@Sun.COM 	iscsit_task_t *iscsit_task = task->task_port_private;
151311081SPriya.Krishnan@Sun.COM 	iscsit_sess_t *ict_sess = iscsit_task->it_ict->ict_sess;
15147978SPeter.Dunlap@Sun.COM 	iscsit_buf_t *ibuf = dbuf->db_port_private;
15157978SPeter.Dunlap@Sun.COM 	int idm_rc;
15167978SPeter.Dunlap@Sun.COM 
15177978SPeter.Dunlap@Sun.COM 	/*
15188062SPeter.Dunlap@Sun.COM 	 * If we are aborting then we can ignore this request
15198062SPeter.Dunlap@Sun.COM 	 */
15208062SPeter.Dunlap@Sun.COM 	if (iscsit_task->it_stmf_abort) {
15218062SPeter.Dunlap@Sun.COM 		return (STMF_SUCCESS);
15228062SPeter.Dunlap@Sun.COM 	}
15238062SPeter.Dunlap@Sun.COM 
15248062SPeter.Dunlap@Sun.COM 	/*
15257978SPeter.Dunlap@Sun.COM 	 * If it's not immediate data then start the transfer
15267978SPeter.Dunlap@Sun.COM 	 */
15277978SPeter.Dunlap@Sun.COM 	ASSERT(ibuf->ibuf_is_immed == B_FALSE);
15287978SPeter.Dunlap@Sun.COM 	if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) {
15297978SPeter.Dunlap@Sun.COM 		/*
153011081SPriya.Krishnan@Sun.COM 		 * The DB_SEND_STATUS_GOOD flag in the STMF data buffer allows
153111081SPriya.Krishnan@Sun.COM 		 * the port provider to phase-collapse, i.e. send the status
153211081SPriya.Krishnan@Sun.COM 		 * along with the final data PDU for the command. The port
153311081SPriya.Krishnan@Sun.COM 		 * provider passes this request to the transport layer by
153411081SPriya.Krishnan@Sun.COM 		 * setting a flag IDM_TASK_PHASECOLLAPSE_REQ in the task.
153511081SPriya.Krishnan@Sun.COM 		 */
153611081SPriya.Krishnan@Sun.COM 		if (dbuf->db_flags & DB_SEND_STATUS_GOOD)
153711081SPriya.Krishnan@Sun.COM 			iscsit_task->it_idm_task->idt_flags |=
153811081SPriya.Krishnan@Sun.COM 			    IDM_TASK_PHASECOLLAPSE_REQ;
153911081SPriya.Krishnan@Sun.COM 		/*
15407978SPeter.Dunlap@Sun.COM 		 * IDM will call iscsit_build_hdr so lock now to serialize
15417978SPeter.Dunlap@Sun.COM 		 * access to the SN values.  We need to lock here to enforce
15427978SPeter.Dunlap@Sun.COM 		 * lock ordering
15437978SPeter.Dunlap@Sun.COM 		 */
154412372SPriya.Krishnan@Sun.COM 		mutex_enter(&ict_sess->ist_sn_mutex);
15457978SPeter.Dunlap@Sun.COM 		idm_rc = idm_buf_tx_to_ini(iscsit_task->it_idm_task,
15467978SPeter.Dunlap@Sun.COM 		    ibuf->ibuf_idm_buf, dbuf->db_relative_offset,
15477978SPeter.Dunlap@Sun.COM 		    dbuf->db_data_size, &iscsit_buf_xfer_cb, dbuf);
154812372SPriya.Krishnan@Sun.COM 		mutex_exit(&ict_sess->ist_sn_mutex);
15497978SPeter.Dunlap@Sun.COM 
15507978SPeter.Dunlap@Sun.COM 		return (iscsit_idm_to_stmf(idm_rc));
15517978SPeter.Dunlap@Sun.COM 	} else if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) {
15527978SPeter.Dunlap@Sun.COM 		/* Grab the SN lock (see comment above) */
155312372SPriya.Krishnan@Sun.COM 		mutex_enter(&ict_sess->ist_sn_mutex);
15547978SPeter.Dunlap@Sun.COM 		idm_rc = idm_buf_rx_from_ini(iscsit_task->it_idm_task,
15557978SPeter.Dunlap@Sun.COM 		    ibuf->ibuf_idm_buf, dbuf->db_relative_offset,
15567978SPeter.Dunlap@Sun.COM 		    dbuf->db_data_size, &iscsit_buf_xfer_cb, dbuf);
155712372SPriya.Krishnan@Sun.COM 		mutex_exit(&ict_sess->ist_sn_mutex);
15587978SPeter.Dunlap@Sun.COM 
15597978SPeter.Dunlap@Sun.COM 		return (iscsit_idm_to_stmf(idm_rc));
15607978SPeter.Dunlap@Sun.COM 	}
15617978SPeter.Dunlap@Sun.COM 
15627978SPeter.Dunlap@Sun.COM 	/* What are we supposed to do if there is no direction? */
15637978SPeter.Dunlap@Sun.COM 	return (STMF_INVALID_ARG);
15647978SPeter.Dunlap@Sun.COM }
15657978SPeter.Dunlap@Sun.COM 
15667978SPeter.Dunlap@Sun.COM static void
iscsit_buf_xfer_cb(idm_buf_t * idb,idm_status_t status)15677978SPeter.Dunlap@Sun.COM iscsit_buf_xfer_cb(idm_buf_t *idb, idm_status_t status)
15687978SPeter.Dunlap@Sun.COM {
15697978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask = idb->idb_task_binding->idt_private;
15707978SPeter.Dunlap@Sun.COM 	stmf_data_buf_t *dbuf = idb->idb_cb_arg;
15717978SPeter.Dunlap@Sun.COM 
15727978SPeter.Dunlap@Sun.COM 	dbuf->db_xfer_status = iscsit_idm_to_stmf(status);
15737978SPeter.Dunlap@Sun.COM 
15747978SPeter.Dunlap@Sun.COM 	/*
15758062SPeter.Dunlap@Sun.COM 	 * If the task has been aborted then we don't need to call STMF
15768062SPeter.Dunlap@Sun.COM 	 */
15778062SPeter.Dunlap@Sun.COM 	if (itask->it_stmf_abort) {
15788062SPeter.Dunlap@Sun.COM 		return;
15798062SPeter.Dunlap@Sun.COM 	}
15808062SPeter.Dunlap@Sun.COM 
15818062SPeter.Dunlap@Sun.COM 	/*
158211081SPriya.Krishnan@Sun.COM 	 * For ISCSI over TCP (not iSER), the last SCSI Data PDU passed
158311081SPriya.Krishnan@Sun.COM 	 * for a successful command contains the status as requested by
158411081SPriya.Krishnan@Sun.COM 	 * by COMSTAR (via the DB_SEND_STATUS_GOOD flag). But the iSER
158511081SPriya.Krishnan@Sun.COM 	 * transport does not support phase-collapse. So pretend we are
158611081SPriya.Krishnan@Sun.COM 	 * COMSTAR and send the status in a separate PDU now.
15877978SPeter.Dunlap@Sun.COM 	 */
158811081SPriya.Krishnan@Sun.COM 	if (idb->idb_task_binding->idt_flags & IDM_TASK_PHASECOLLAPSE_SUCCESS) {
158911081SPriya.Krishnan@Sun.COM 		/*
159011611SPriya.Krishnan@Sun.COM 		 * Mark task complete and notify COMSTAR
159111611SPriya.Krishnan@Sun.COM 		 * that the status has been sent.
159211081SPriya.Krishnan@Sun.COM 		 */
159311081SPriya.Krishnan@Sun.COM 		itask->it_idm_task->idt_state = TASK_COMPLETE;
159411081SPriya.Krishnan@Sun.COM 		stmf_send_status_done(itask->it_stmf_task,
159511081SPriya.Krishnan@Sun.COM 		    iscsit_idm_to_stmf(status), STMF_IOF_LPORT_DONE);
159611081SPriya.Krishnan@Sun.COM 	} else if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) &&
15977978SPeter.Dunlap@Sun.COM 	    status == IDM_STATUS_SUCCESS) {
159811081SPriya.Krishnan@Sun.COM 
15997978SPeter.Dunlap@Sun.COM 		/*
160011392SPriya.Krishnan@Sun.COM 		 * The iscsi target port provider - for iSER, emulates the
160111392SPriya.Krishnan@Sun.COM 		 * DB_SEND_STATUS_GOOD optimization if requested by STMF;
160211392SPriya.Krishnan@Sun.COM 		 * it sends the status in a separate PDU after the data
160311392SPriya.Krishnan@Sun.COM 		 * transfer. In this case the port provider should first
160411392SPriya.Krishnan@Sun.COM 		 * call stmf_data_xfer_done() to mark the transfer complete
160511392SPriya.Krishnan@Sun.COM 		 * and then send the status. Although STMF will free the
160611392SPriya.Krishnan@Sun.COM 		 * buffer at the time the task is freed, even if the transfer
160711392SPriya.Krishnan@Sun.COM 		 * is not marked complete, this behavior makes statistics
160811392SPriya.Krishnan@Sun.COM 		 * gathering and task state tracking more difficult than it
160911392SPriya.Krishnan@Sun.COM 		 * needs to be.
16107978SPeter.Dunlap@Sun.COM 		 */
161111392SPriya.Krishnan@Sun.COM 		stmf_data_xfer_done(itask->it_stmf_task, dbuf, 0);
16127978SPeter.Dunlap@Sun.COM 		if (iscsit_send_scsi_status(itask->it_stmf_task, 0)
16138607SJames.Moore@Sun.COM 		    != STMF_SUCCESS) {
161411392SPriya.Krishnan@Sun.COM 			stmf_send_status_done(itask->it_stmf_task,
161511392SPriya.Krishnan@Sun.COM 			    STMF_FAILURE, STMF_IOF_LPORT_DONE);
16167978SPeter.Dunlap@Sun.COM 		}
16177978SPeter.Dunlap@Sun.COM 	} else {
16187978SPeter.Dunlap@Sun.COM 		stmf_data_xfer_done(itask->it_stmf_task, dbuf, 0);
161911392SPriya.Krishnan@Sun.COM 		/* don't touch dbuf after stmf_data_xfer_done */
16207978SPeter.Dunlap@Sun.COM 	}
16217978SPeter.Dunlap@Sun.COM }
16227978SPeter.Dunlap@Sun.COM 
16238062SPeter.Dunlap@Sun.COM 
16247978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
16257978SPeter.Dunlap@Sun.COM stmf_status_t
iscsit_send_scsi_status(scsi_task_t * task,uint32_t ioflags)16267978SPeter.Dunlap@Sun.COM iscsit_send_scsi_status(scsi_task_t *task, uint32_t ioflags)
16277978SPeter.Dunlap@Sun.COM {
16287978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask = task->task_port_private;
16297978SPeter.Dunlap@Sun.COM 	iscsi_scsi_rsp_hdr_t *rsp;
16307978SPeter.Dunlap@Sun.COM 	idm_pdu_t *pdu;
16317978SPeter.Dunlap@Sun.COM 	int resp_datalen;
16327978SPeter.Dunlap@Sun.COM 
16337978SPeter.Dunlap@Sun.COM 	/*
16347978SPeter.Dunlap@Sun.COM 	 * If this task is aborted then we don't need to respond.
16357978SPeter.Dunlap@Sun.COM 	 */
16367978SPeter.Dunlap@Sun.COM 	if (itask->it_stmf_abort) {
16377978SPeter.Dunlap@Sun.COM 		return (STMF_SUCCESS);
16387978SPeter.Dunlap@Sun.COM 	}
16397978SPeter.Dunlap@Sun.COM 
16407978SPeter.Dunlap@Sun.COM 	/*
16417978SPeter.Dunlap@Sun.COM 	 * If this is a task management status, handle it elsewhere.
16427978SPeter.Dunlap@Sun.COM 	 */
16437978SPeter.Dunlap@Sun.COM 	if (task->task_mgmt_function != TM_NONE) {
16447978SPeter.Dunlap@Sun.COM 		/*
16457978SPeter.Dunlap@Sun.COM 		 * Don't wait for the PDU completion to tell STMF
16467978SPeter.Dunlap@Sun.COM 		 * the task is done -- it doesn't really matter and
16477978SPeter.Dunlap@Sun.COM 		 * it makes life complicated if STMF later asks us to
16487978SPeter.Dunlap@Sun.COM 		 * abort the request and we don't know whether the
16497978SPeter.Dunlap@Sun.COM 		 * status has been sent or not.
16507978SPeter.Dunlap@Sun.COM 		 */
16517978SPeter.Dunlap@Sun.COM 		itask->it_tm_responded = B_TRUE;
16527978SPeter.Dunlap@Sun.COM 		iscsit_send_task_mgmt_resp(itask->it_tm_pdu,
16537978SPeter.Dunlap@Sun.COM 		    (task->task_completion_status == STMF_SUCCESS) ?
16547978SPeter.Dunlap@Sun.COM 		    SCSI_TCP_TM_RESP_COMPLETE : SCSI_TCP_TM_RESP_FUNC_NOT_SUPP);
16557978SPeter.Dunlap@Sun.COM 		stmf_send_status_done(task, STMF_SUCCESS,
16567978SPeter.Dunlap@Sun.COM 		    STMF_IOF_LPORT_DONE);
16577978SPeter.Dunlap@Sun.COM 		return (STMF_SUCCESS);
16587978SPeter.Dunlap@Sun.COM 	}
16597978SPeter.Dunlap@Sun.COM 
16609586SPeter.Dunlap@Sun.COM 	/*
16619586SPeter.Dunlap@Sun.COM 	 * Remove the task from the session task list
16629586SPeter.Dunlap@Sun.COM 	 */
16639586SPeter.Dunlap@Sun.COM 	iscsit_task_done(itask);
16649586SPeter.Dunlap@Sun.COM 
16659586SPeter.Dunlap@Sun.COM 	/*
16669586SPeter.Dunlap@Sun.COM 	 * Send status
16679586SPeter.Dunlap@Sun.COM 	 */
16687978SPeter.Dunlap@Sun.COM 	mutex_enter(&itask->it_idm_task->idt_mutex);
16697978SPeter.Dunlap@Sun.COM 	if ((itask->it_idm_task->idt_state == TASK_ACTIVE) &&
16707978SPeter.Dunlap@Sun.COM 	    (task->task_completion_status == STMF_SUCCESS) &&
16717978SPeter.Dunlap@Sun.COM 	    (task->task_sense_length == 0) &&
16727978SPeter.Dunlap@Sun.COM 	    (task->task_resid == 0)) {
16737978SPeter.Dunlap@Sun.COM 		itask->it_idm_task->idt_state = TASK_COMPLETE;
16747978SPeter.Dunlap@Sun.COM 		/* PDU callback releases task hold */
16757978SPeter.Dunlap@Sun.COM 		idm_task_hold(itask->it_idm_task);
16767978SPeter.Dunlap@Sun.COM 		mutex_exit(&itask->it_idm_task->idt_mutex);
16777978SPeter.Dunlap@Sun.COM 		/*
16787978SPeter.Dunlap@Sun.COM 		 * Fast path.  Cached status PDU's are already
16797978SPeter.Dunlap@Sun.COM 		 * initialized.  We just need to fill in
168011081SPriya.Krishnan@Sun.COM 		 * connection and task information. StatSN is
168111081SPriya.Krishnan@Sun.COM 		 * incremented by 1 for every status sent a
168211081SPriya.Krishnan@Sun.COM 		 * connection.
16837978SPeter.Dunlap@Sun.COM 		 */
16847978SPeter.Dunlap@Sun.COM 		pdu = kmem_cache_alloc(iscsit_status_pdu_cache, KM_SLEEP);
16857978SPeter.Dunlap@Sun.COM 		pdu->isp_ic = itask->it_ict->ict_ic;
16867978SPeter.Dunlap@Sun.COM 		pdu->isp_private = itask;
168711081SPriya.Krishnan@Sun.COM 		pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
16887978SPeter.Dunlap@Sun.COM 
16897978SPeter.Dunlap@Sun.COM 		rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
16907978SPeter.Dunlap@Sun.COM 		rsp->itt = itask->it_itt;
169111289SPriya.Krishnan@Sun.COM 		/*
169211289SPriya.Krishnan@Sun.COM 		 * ExpDataSN is the number of R2T and Data-In (read)
169311289SPriya.Krishnan@Sun.COM 		 * PDUs the target has sent for the SCSI command.
169411289SPriya.Krishnan@Sun.COM 		 *
169511289SPriya.Krishnan@Sun.COM 		 * Since there is no support for bidirectional transfer
169611289SPriya.Krishnan@Sun.COM 		 * yet, either idt_exp_datasn or idt_exp_rttsn, but not
169711289SPriya.Krishnan@Sun.COM 		 * both is valid at any time
169811289SPriya.Krishnan@Sun.COM 		 */
169911289SPriya.Krishnan@Sun.COM 		rsp->expdatasn = (itask->it_idm_task->idt_exp_datasn != 0) ?
170011289SPriya.Krishnan@Sun.COM 		    htonl(itask->it_idm_task->idt_exp_datasn):
170111289SPriya.Krishnan@Sun.COM 		    htonl(itask->it_idm_task->idt_exp_rttsn);
17027978SPeter.Dunlap@Sun.COM 		rsp->cmd_status = task->task_scsi_status;
17037978SPeter.Dunlap@Sun.COM 		iscsit_pdu_tx(pdu);
17047978SPeter.Dunlap@Sun.COM 		return (STMF_SUCCESS);
17057978SPeter.Dunlap@Sun.COM 	} else {
17067978SPeter.Dunlap@Sun.COM 		if (itask->it_idm_task->idt_state != TASK_ACTIVE) {
17077978SPeter.Dunlap@Sun.COM 			mutex_exit(&itask->it_idm_task->idt_mutex);
17088607SJames.Moore@Sun.COM 			return (STMF_FAILURE);
17097978SPeter.Dunlap@Sun.COM 		}
17107978SPeter.Dunlap@Sun.COM 		itask->it_idm_task->idt_state = TASK_COMPLETE;
17117978SPeter.Dunlap@Sun.COM 		/* PDU callback releases task hold */
17127978SPeter.Dunlap@Sun.COM 		idm_task_hold(itask->it_idm_task);
17137978SPeter.Dunlap@Sun.COM 		mutex_exit(&itask->it_idm_task->idt_mutex);
17147978SPeter.Dunlap@Sun.COM 
17157978SPeter.Dunlap@Sun.COM 		resp_datalen = (task->task_sense_length == 0) ? 0 :
17167978SPeter.Dunlap@Sun.COM 		    (task->task_sense_length + sizeof (uint16_t));
17177978SPeter.Dunlap@Sun.COM 
17187978SPeter.Dunlap@Sun.COM 		pdu = idm_pdu_alloc(sizeof (iscsi_hdr_t), resp_datalen);
17197978SPeter.Dunlap@Sun.COM 		idm_pdu_init(pdu, itask->it_ict->ict_ic, itask,
17207978SPeter.Dunlap@Sun.COM 		    iscsit_send_status_done);
172111081SPriya.Krishnan@Sun.COM 		pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
17227978SPeter.Dunlap@Sun.COM 
17237978SPeter.Dunlap@Sun.COM 		rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
17247978SPeter.Dunlap@Sun.COM 		bzero(rsp, sizeof (*rsp));
17257978SPeter.Dunlap@Sun.COM 		rsp->opcode = ISCSI_OP_SCSI_RSP;
17267978SPeter.Dunlap@Sun.COM 
17277978SPeter.Dunlap@Sun.COM 		rsp->flags = ISCSI_FLAG_FINAL;
17287978SPeter.Dunlap@Sun.COM 		if (task->task_status_ctrl & TASK_SCTRL_OVER) {
17297978SPeter.Dunlap@Sun.COM 			rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
17307978SPeter.Dunlap@Sun.COM 		} else if (task->task_status_ctrl & TASK_SCTRL_UNDER) {
17317978SPeter.Dunlap@Sun.COM 			rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
17327978SPeter.Dunlap@Sun.COM 		}
17337978SPeter.Dunlap@Sun.COM 
17347978SPeter.Dunlap@Sun.COM 		rsp->bi_residual_count = 0;
17357978SPeter.Dunlap@Sun.COM 		rsp->residual_count = htonl(task->task_resid);
17367978SPeter.Dunlap@Sun.COM 		rsp->itt = itask->it_itt;
17377978SPeter.Dunlap@Sun.COM 		rsp->response = ISCSI_STATUS_CMD_COMPLETED;
173811289SPriya.Krishnan@Sun.COM 		rsp->expdatasn = (itask->it_idm_task->idt_exp_datasn != 0) ?
173911289SPriya.Krishnan@Sun.COM 		    htonl(itask->it_idm_task->idt_exp_datasn):
174011289SPriya.Krishnan@Sun.COM 		    htonl(itask->it_idm_task->idt_exp_rttsn);
17417978SPeter.Dunlap@Sun.COM 		rsp->cmd_status = task->task_scsi_status;
17427978SPeter.Dunlap@Sun.COM 		if (task->task_sense_length != 0) {
17437978SPeter.Dunlap@Sun.COM 			/*
17447978SPeter.Dunlap@Sun.COM 			 * Add a byte to provide the sense length in
17457978SPeter.Dunlap@Sun.COM 			 * the response
17467978SPeter.Dunlap@Sun.COM 			 */
17477978SPeter.Dunlap@Sun.COM 			*(uint16_t *)((void *)pdu->isp_data) =
17487978SPeter.Dunlap@Sun.COM 			    htons(task->task_sense_length);
17497978SPeter.Dunlap@Sun.COM 			bcopy(task->task_sense_data,
17507978SPeter.Dunlap@Sun.COM 			    (uint8_t *)pdu->isp_data +
17517978SPeter.Dunlap@Sun.COM 			    sizeof (uint16_t),
17527978SPeter.Dunlap@Sun.COM 			    task->task_sense_length);
17537978SPeter.Dunlap@Sun.COM 			hton24(rsp->dlength, resp_datalen);
17547978SPeter.Dunlap@Sun.COM 		}
17557978SPeter.Dunlap@Sun.COM 
17569162SPeter.Dunlap@Sun.COM 		DTRACE_PROBE5(iscsi__scsi__response,
17579162SPeter.Dunlap@Sun.COM 		    iscsit_conn_t *, itask->it_ict,
17589162SPeter.Dunlap@Sun.COM 		    uint8_t, rsp->response,
17599162SPeter.Dunlap@Sun.COM 		    uint8_t, rsp->cmd_status,
17609162SPeter.Dunlap@Sun.COM 		    idm_pdu_t *, pdu,
17619162SPeter.Dunlap@Sun.COM 		    scsi_task_t *, task);
17629162SPeter.Dunlap@Sun.COM 
17637978SPeter.Dunlap@Sun.COM 		iscsit_pdu_tx(pdu);
17647978SPeter.Dunlap@Sun.COM 
17657978SPeter.Dunlap@Sun.COM 		return (STMF_SUCCESS);
17667978SPeter.Dunlap@Sun.COM 	}
17677978SPeter.Dunlap@Sun.COM }
17687978SPeter.Dunlap@Sun.COM 
17697978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
17707978SPeter.Dunlap@Sun.COM static void
iscsit_send_good_status_done(idm_pdu_t * pdu,idm_status_t status)17717978SPeter.Dunlap@Sun.COM iscsit_send_good_status_done(idm_pdu_t *pdu, idm_status_t status)
17727978SPeter.Dunlap@Sun.COM {
17738062SPeter.Dunlap@Sun.COM 	iscsit_task_t	*itask;
17748062SPeter.Dunlap@Sun.COM 	boolean_t	aborted;
17757978SPeter.Dunlap@Sun.COM 
17767978SPeter.Dunlap@Sun.COM 	itask = pdu->isp_private;
17778062SPeter.Dunlap@Sun.COM 	aborted = itask->it_stmf_abort;
17788062SPeter.Dunlap@Sun.COM 
17798062SPeter.Dunlap@Sun.COM 	/*
17808062SPeter.Dunlap@Sun.COM 	 * After releasing the hold the task may be freed at any time so
17818062SPeter.Dunlap@Sun.COM 	 * don't touch it.
17828062SPeter.Dunlap@Sun.COM 	 */
17837978SPeter.Dunlap@Sun.COM 	idm_task_rele(itask->it_idm_task);
17848062SPeter.Dunlap@Sun.COM 	if (!aborted) {
17858062SPeter.Dunlap@Sun.COM 		stmf_send_status_done(itask->it_stmf_task,
17868062SPeter.Dunlap@Sun.COM 		    iscsit_idm_to_stmf(pdu->isp_status), STMF_IOF_LPORT_DONE);
17878062SPeter.Dunlap@Sun.COM 	}
17887978SPeter.Dunlap@Sun.COM 	kmem_cache_free(iscsit_status_pdu_cache, pdu);
17897978SPeter.Dunlap@Sun.COM }
17907978SPeter.Dunlap@Sun.COM 
17917978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
17927978SPeter.Dunlap@Sun.COM static void
iscsit_send_status_done(idm_pdu_t * pdu,idm_status_t status)17937978SPeter.Dunlap@Sun.COM iscsit_send_status_done(idm_pdu_t *pdu, idm_status_t status)
17947978SPeter.Dunlap@Sun.COM {
17957978SPeter.Dunlap@Sun.COM 	iscsit_task_t	 *itask;
17968062SPeter.Dunlap@Sun.COM 	boolean_t	aborted;
17977978SPeter.Dunlap@Sun.COM 
17987978SPeter.Dunlap@Sun.COM 	itask = pdu->isp_private;
17998062SPeter.Dunlap@Sun.COM 	aborted = itask->it_stmf_abort;
18008062SPeter.Dunlap@Sun.COM 
18018062SPeter.Dunlap@Sun.COM 	/*
18028062SPeter.Dunlap@Sun.COM 	 * After releasing the hold the task may be freed at any time so
18038062SPeter.Dunlap@Sun.COM 	 * don't touch it.
18048062SPeter.Dunlap@Sun.COM 	 */
18057978SPeter.Dunlap@Sun.COM 	idm_task_rele(itask->it_idm_task);
18068062SPeter.Dunlap@Sun.COM 	if (!aborted) {
18078062SPeter.Dunlap@Sun.COM 		stmf_send_status_done(itask->it_stmf_task,
18088062SPeter.Dunlap@Sun.COM 		    iscsit_idm_to_stmf(pdu->isp_status), STMF_IOF_LPORT_DONE);
18098062SPeter.Dunlap@Sun.COM 	}
18107978SPeter.Dunlap@Sun.COM 	idm_pdu_free(pdu);
18117978SPeter.Dunlap@Sun.COM }
18127978SPeter.Dunlap@Sun.COM 
18137978SPeter.Dunlap@Sun.COM 
18147978SPeter.Dunlap@Sun.COM void
iscsit_lport_task_free(scsi_task_t * task)18157978SPeter.Dunlap@Sun.COM iscsit_lport_task_free(scsi_task_t *task)
18167978SPeter.Dunlap@Sun.COM {
18177978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask = task->task_port_private;
18187978SPeter.Dunlap@Sun.COM 
18197978SPeter.Dunlap@Sun.COM 	/* We only call idm_task_start for regular tasks, not task management */
18207978SPeter.Dunlap@Sun.COM 	if (task->task_mgmt_function == TM_NONE) {
18217978SPeter.Dunlap@Sun.COM 		idm_task_done(itask->it_idm_task);
18227978SPeter.Dunlap@Sun.COM 		iscsit_task_free(itask);
18237978SPeter.Dunlap@Sun.COM 		return;
18247978SPeter.Dunlap@Sun.COM 	} else {
18257978SPeter.Dunlap@Sun.COM 		iscsit_tm_task_free(itask);
18267978SPeter.Dunlap@Sun.COM 	}
18277978SPeter.Dunlap@Sun.COM }
18287978SPeter.Dunlap@Sun.COM 
18297978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
18307978SPeter.Dunlap@Sun.COM stmf_status_t
iscsit_abort(stmf_local_port_t * lport,int abort_cmd,void * arg,uint32_t flags)18317978SPeter.Dunlap@Sun.COM iscsit_abort(stmf_local_port_t *lport, int abort_cmd, void *arg, uint32_t flags)
18327978SPeter.Dunlap@Sun.COM {
18337978SPeter.Dunlap@Sun.COM 	scsi_task_t	*st = (scsi_task_t *)arg;
18347978SPeter.Dunlap@Sun.COM 	iscsit_task_t	*iscsit_task;
18357978SPeter.Dunlap@Sun.COM 	idm_task_t	*idt;
18367978SPeter.Dunlap@Sun.COM 
18377978SPeter.Dunlap@Sun.COM 	/*
18387978SPeter.Dunlap@Sun.COM 	 * If this is a task management request then there's really not much to
18397978SPeter.Dunlap@Sun.COM 	 * do.
18407978SPeter.Dunlap@Sun.COM 	 */
18417978SPeter.Dunlap@Sun.COM 	if (st->task_mgmt_function != TM_NONE) {
18427978SPeter.Dunlap@Sun.COM 		return (STMF_ABORT_SUCCESS);
18437978SPeter.Dunlap@Sun.COM 	}
18447978SPeter.Dunlap@Sun.COM 
18457978SPeter.Dunlap@Sun.COM 	/*
18467978SPeter.Dunlap@Sun.COM 	 * Regular task, start cleaning up
18477978SPeter.Dunlap@Sun.COM 	 */
18487978SPeter.Dunlap@Sun.COM 	iscsit_task = st->task_port_private;
18497978SPeter.Dunlap@Sun.COM 	idt = iscsit_task->it_idm_task;
18507978SPeter.Dunlap@Sun.COM 	mutex_enter(&iscsit_task->it_mutex);
18517978SPeter.Dunlap@Sun.COM 	iscsit_task->it_stmf_abort = B_TRUE;
18527978SPeter.Dunlap@Sun.COM 	if (iscsit_task->it_aborted) {
18537978SPeter.Dunlap@Sun.COM 		mutex_exit(&iscsit_task->it_mutex);
18547978SPeter.Dunlap@Sun.COM 		/*
18559586SPeter.Dunlap@Sun.COM 		 * Task is no longer active
18569586SPeter.Dunlap@Sun.COM 		 */
18579586SPeter.Dunlap@Sun.COM 		iscsit_task_done(iscsit_task);
18589586SPeter.Dunlap@Sun.COM 
18599586SPeter.Dunlap@Sun.COM 		/*
18607978SPeter.Dunlap@Sun.COM 		 * STMF specification is wrong... says to return
18617978SPeter.Dunlap@Sun.COM 		 * STMF_ABORTED, the code actually looks for
18627978SPeter.Dunlap@Sun.COM 		 * STMF_ABORT_SUCCESS.
18637978SPeter.Dunlap@Sun.COM 		 */
18647978SPeter.Dunlap@Sun.COM 		return (STMF_ABORT_SUCCESS);
18657978SPeter.Dunlap@Sun.COM 	} else {
18667978SPeter.Dunlap@Sun.COM 		mutex_exit(&iscsit_task->it_mutex);
18677978SPeter.Dunlap@Sun.COM 		/*
18687978SPeter.Dunlap@Sun.COM 		 * Call IDM to abort the task.  Due to a variety of
18697978SPeter.Dunlap@Sun.COM 		 * circumstances the task may already be in the process of
18707978SPeter.Dunlap@Sun.COM 		 * aborting.
18717978SPeter.Dunlap@Sun.COM 		 * We'll let IDM worry about rationalizing all that except
18727978SPeter.Dunlap@Sun.COM 		 * for one particular instance.  If the state of the task
18737978SPeter.Dunlap@Sun.COM 		 * is TASK_COMPLETE, we need to indicate to the framework
18747978SPeter.Dunlap@Sun.COM 		 * that we are in fact done.  This typically happens with
18757978SPeter.Dunlap@Sun.COM 		 * framework-initiated task management type requests
18767978SPeter.Dunlap@Sun.COM 		 * (e.g. abort task).
18777978SPeter.Dunlap@Sun.COM 		 */
18787978SPeter.Dunlap@Sun.COM 		if (idt->idt_state == TASK_COMPLETE) {
18797978SPeter.Dunlap@Sun.COM 			idm_refcnt_wait_ref(&idt->idt_refcnt);
18807978SPeter.Dunlap@Sun.COM 			return (STMF_ABORT_SUCCESS);
18817978SPeter.Dunlap@Sun.COM 		} else {
18827978SPeter.Dunlap@Sun.COM 			idm_task_abort(idt->idt_ic, idt, AT_TASK_MGMT_ABORT);
18837978SPeter.Dunlap@Sun.COM 			return (STMF_SUCCESS);
18847978SPeter.Dunlap@Sun.COM 		}
18857978SPeter.Dunlap@Sun.COM 	}
18867978SPeter.Dunlap@Sun.COM 
18877978SPeter.Dunlap@Sun.COM 	/*NOTREACHED*/
18887978SPeter.Dunlap@Sun.COM }
18897978SPeter.Dunlap@Sun.COM 
18907978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
18917978SPeter.Dunlap@Sun.COM void
iscsit_ctl(stmf_local_port_t * lport,int cmd,void * arg)18927978SPeter.Dunlap@Sun.COM iscsit_ctl(stmf_local_port_t *lport, int cmd, void *arg)
18937978SPeter.Dunlap@Sun.COM {
18947978SPeter.Dunlap@Sun.COM 	iscsit_tgt_t		*iscsit_tgt;
18957978SPeter.Dunlap@Sun.COM 
18967978SPeter.Dunlap@Sun.COM 	ASSERT((cmd == STMF_CMD_LPORT_ONLINE) ||
18977978SPeter.Dunlap@Sun.COM 	    (cmd == STMF_ACK_LPORT_ONLINE_COMPLETE) ||
18987978SPeter.Dunlap@Sun.COM 	    (cmd == STMF_CMD_LPORT_OFFLINE) ||
18997978SPeter.Dunlap@Sun.COM 	    (cmd == STMF_ACK_LPORT_OFFLINE_COMPLETE));
19007978SPeter.Dunlap@Sun.COM 
19017978SPeter.Dunlap@Sun.COM 	iscsit_tgt = (iscsit_tgt_t *)lport->lport_port_private;
19027978SPeter.Dunlap@Sun.COM 
19037978SPeter.Dunlap@Sun.COM 	switch (cmd) {
19047978SPeter.Dunlap@Sun.COM 	case STMF_CMD_LPORT_ONLINE:
19057978SPeter.Dunlap@Sun.COM 		iscsit_tgt_sm_event(iscsit_tgt, TE_STMF_ONLINE_REQ);
19067978SPeter.Dunlap@Sun.COM 		break;
19077978SPeter.Dunlap@Sun.COM 	case STMF_CMD_LPORT_OFFLINE:
19087978SPeter.Dunlap@Sun.COM 		iscsit_tgt_sm_event(iscsit_tgt, TE_STMF_OFFLINE_REQ);
19097978SPeter.Dunlap@Sun.COM 		break;
19107978SPeter.Dunlap@Sun.COM 	case STMF_ACK_LPORT_ONLINE_COMPLETE:
19117978SPeter.Dunlap@Sun.COM 		iscsit_tgt_sm_event(iscsit_tgt, TE_STMF_ONLINE_COMPLETE_ACK);
19127978SPeter.Dunlap@Sun.COM 		break;
19137978SPeter.Dunlap@Sun.COM 	case STMF_ACK_LPORT_OFFLINE_COMPLETE:
19147978SPeter.Dunlap@Sun.COM 		iscsit_tgt_sm_event(iscsit_tgt, TE_STMF_OFFLINE_COMPLETE_ACK);
19157978SPeter.Dunlap@Sun.COM 		break;
19167978SPeter.Dunlap@Sun.COM 
19177978SPeter.Dunlap@Sun.COM 	default:
19187978SPeter.Dunlap@Sun.COM 		break;
19197978SPeter.Dunlap@Sun.COM 	}
19207978SPeter.Dunlap@Sun.COM }
19217978SPeter.Dunlap@Sun.COM 
19227978SPeter.Dunlap@Sun.COM static stmf_status_t
iscsit_idm_to_stmf(idm_status_t idmrc)19237978SPeter.Dunlap@Sun.COM iscsit_idm_to_stmf(idm_status_t idmrc)
19247978SPeter.Dunlap@Sun.COM {
19257978SPeter.Dunlap@Sun.COM 	switch (idmrc) {
19267978SPeter.Dunlap@Sun.COM 	case IDM_STATUS_SUCCESS:
19277978SPeter.Dunlap@Sun.COM 		return (STMF_SUCCESS);
19287978SPeter.Dunlap@Sun.COM 	default:
19297978SPeter.Dunlap@Sun.COM 		return (STMF_FAILURE);
19307978SPeter.Dunlap@Sun.COM 	}
19317978SPeter.Dunlap@Sun.COM 	/*NOTREACHED*/
19327978SPeter.Dunlap@Sun.COM }
19337978SPeter.Dunlap@Sun.COM 
193412372SPriya.Krishnan@Sun.COM void
iscsit_op_scsi_cmd(idm_conn_t * ic,idm_pdu_t * rx_pdu)193512372SPriya.Krishnan@Sun.COM iscsit_op_scsi_cmd(idm_conn_t *ic, idm_pdu_t *rx_pdu)
193612372SPriya.Krishnan@Sun.COM {
193712372SPriya.Krishnan@Sun.COM 	iscsit_conn_t		*ict = ic->ic_handle;
193812372SPriya.Krishnan@Sun.COM 
193912372SPriya.Krishnan@Sun.COM 	if (iscsit_check_cmdsn_and_queue(rx_pdu)) {
194012372SPriya.Krishnan@Sun.COM 		iscsit_post_scsi_cmd(ic, rx_pdu);
194112372SPriya.Krishnan@Sun.COM 	}
194212372SPriya.Krishnan@Sun.COM 	iscsit_process_pdu_in_queue(ict->ict_sess);
194312372SPriya.Krishnan@Sun.COM }
19447978SPeter.Dunlap@Sun.COM 
19457978SPeter.Dunlap@Sun.COM /*
19467978SPeter.Dunlap@Sun.COM  * ISCSI protocol
19477978SPeter.Dunlap@Sun.COM  */
19487978SPeter.Dunlap@Sun.COM 
19497978SPeter.Dunlap@Sun.COM void
iscsit_post_scsi_cmd(idm_conn_t * ic,idm_pdu_t * rx_pdu)195012372SPriya.Krishnan@Sun.COM iscsit_post_scsi_cmd(idm_conn_t *ic, idm_pdu_t *rx_pdu)
19517978SPeter.Dunlap@Sun.COM {
19527978SPeter.Dunlap@Sun.COM 	iscsit_conn_t		*ict;
19537978SPeter.Dunlap@Sun.COM 	iscsit_task_t		*itask;
19547978SPeter.Dunlap@Sun.COM 	scsi_task_t		*task;
19557978SPeter.Dunlap@Sun.COM 	iscsit_buf_t		*ibuf;
19567978SPeter.Dunlap@Sun.COM 	iscsi_scsi_cmd_hdr_t	*iscsi_scsi =
19577978SPeter.Dunlap@Sun.COM 	    (iscsi_scsi_cmd_hdr_t *)rx_pdu->isp_hdr;
19587978SPeter.Dunlap@Sun.COM 	iscsi_addl_hdr_t	*ahs_hdr;
19597978SPeter.Dunlap@Sun.COM 	uint16_t		addl_cdb_len = 0;
19607978SPeter.Dunlap@Sun.COM 
19617978SPeter.Dunlap@Sun.COM 	ict = ic->ic_handle;
19627978SPeter.Dunlap@Sun.COM 
19637978SPeter.Dunlap@Sun.COM 	itask = iscsit_task_alloc(ict);
19647978SPeter.Dunlap@Sun.COM 	if (itask == NULL) {
19659586SPeter.Dunlap@Sun.COM 		/* Finish processing request */
19669586SPeter.Dunlap@Sun.COM 		iscsit_set_cmdsn(ict, rx_pdu);
19679586SPeter.Dunlap@Sun.COM 
19687978SPeter.Dunlap@Sun.COM 		iscsit_send_direct_scsi_resp(ict, rx_pdu,
19697978SPeter.Dunlap@Sun.COM 		    ISCSI_STATUS_CMD_COMPLETED, STATUS_BUSY);
19707978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
19717978SPeter.Dunlap@Sun.COM 		return;
19727978SPeter.Dunlap@Sun.COM 	}
19737978SPeter.Dunlap@Sun.COM 
19747978SPeter.Dunlap@Sun.COM 	/*
19757978SPeter.Dunlap@Sun.COM 	 * Note CmdSN and ITT in task.  IDM will have already validated this
19767978SPeter.Dunlap@Sun.COM 	 * request against the connection state so we don't need to check
19777978SPeter.Dunlap@Sun.COM 	 * that (the connection may have changed state in the meantime but
19787978SPeter.Dunlap@Sun.COM 	 * we will catch that when we try to send a response)
19797978SPeter.Dunlap@Sun.COM 	 */
19807978SPeter.Dunlap@Sun.COM 	itask->it_cmdsn = ntohl(iscsi_scsi->cmdsn);
19817978SPeter.Dunlap@Sun.COM 	itask->it_itt = iscsi_scsi->itt;
19827978SPeter.Dunlap@Sun.COM 
19837978SPeter.Dunlap@Sun.COM 	/*
19847978SPeter.Dunlap@Sun.COM 	 * Check for extended CDB AHS
19857978SPeter.Dunlap@Sun.COM 	 */
19867978SPeter.Dunlap@Sun.COM 	if (iscsi_scsi->hlength > 0) {
19877978SPeter.Dunlap@Sun.COM 		ahs_hdr = (iscsi_addl_hdr_t *)iscsi_scsi;
19887978SPeter.Dunlap@Sun.COM 		addl_cdb_len = ((ahs_hdr->ahs_hlen_hi << 8) |
19897978SPeter.Dunlap@Sun.COM 		    ahs_hdr->ahs_hlen_lo) - 1; /* Adjust for reserved byte */
19907978SPeter.Dunlap@Sun.COM 		if (((addl_cdb_len + 4) / sizeof (uint32_t)) >
19917978SPeter.Dunlap@Sun.COM 		    iscsi_scsi->hlength) {
19927978SPeter.Dunlap@Sun.COM 			/* Mangled header info, drop it */
19937978SPeter.Dunlap@Sun.COM 			idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
19947978SPeter.Dunlap@Sun.COM 			return;
19957978SPeter.Dunlap@Sun.COM 		}
19967978SPeter.Dunlap@Sun.COM 	}
19977978SPeter.Dunlap@Sun.COM 
19987978SPeter.Dunlap@Sun.COM 	ict = rx_pdu->isp_ic->ic_handle; /* IDM client private */
19997978SPeter.Dunlap@Sun.COM 
20009586SPeter.Dunlap@Sun.COM 	/*
20019586SPeter.Dunlap@Sun.COM 	 * Add task to session list.  This function will also check to
20029586SPeter.Dunlap@Sun.COM 	 * ensure that the task does not already exist.
20039586SPeter.Dunlap@Sun.COM 	 */
20049586SPeter.Dunlap@Sun.COM 	if (iscsit_task_start(itask) != IDM_STATUS_SUCCESS) {
20059586SPeter.Dunlap@Sun.COM 		/*
20069586SPeter.Dunlap@Sun.COM 		 * Task exists, free all resources and reject.  Don't
20079586SPeter.Dunlap@Sun.COM 		 * update expcmdsn in this case because RFC 3720 says
20089586SPeter.Dunlap@Sun.COM 		 * "The CmdSN of the rejected command PDU (if it is a
20099586SPeter.Dunlap@Sun.COM 		 * non-immediate command) MUST NOT be considered received
20109586SPeter.Dunlap@Sun.COM 		 * by the target (i.e., a command sequence gap must be
20119586SPeter.Dunlap@Sun.COM 		 * assumed for the CmdSN), even though the CmdSN of the
20129586SPeter.Dunlap@Sun.COM 		 * rejected command PDU may be reliably ascertained.  Upon
20139586SPeter.Dunlap@Sun.COM 		 * receiving the Reject, the initiator MUST plug the CmdSN
20149586SPeter.Dunlap@Sun.COM 		 * gap in order to continue to use the session.  The gap
20159586SPeter.Dunlap@Sun.COM 		 * may be plugged either by transmitting a command PDU
20169586SPeter.Dunlap@Sun.COM 		 * with the same CmdSN, or by aborting the task (see section
20179586SPeter.Dunlap@Sun.COM 		 * 6.9 on how an abort may plug a CmdSN gap)." (Section 6.3)
20189586SPeter.Dunlap@Sun.COM 		 */
20199586SPeter.Dunlap@Sun.COM 		iscsit_task_free(itask);
20209586SPeter.Dunlap@Sun.COM 		iscsit_send_reject(ict, rx_pdu, ISCSI_REJECT_TASK_IN_PROGRESS);
20219586SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
20229586SPeter.Dunlap@Sun.COM 		return;
20239586SPeter.Dunlap@Sun.COM 	}
20249586SPeter.Dunlap@Sun.COM 
20259586SPeter.Dunlap@Sun.COM 	/* Update sequence numbers */
20269586SPeter.Dunlap@Sun.COM 	iscsit_set_cmdsn(ict, rx_pdu);
20279586SPeter.Dunlap@Sun.COM 
20289586SPeter.Dunlap@Sun.COM 	/*
20299586SPeter.Dunlap@Sun.COM 	 * Allocate STMF task
20309586SPeter.Dunlap@Sun.COM 	 */
20317978SPeter.Dunlap@Sun.COM 	itask->it_stmf_task = stmf_task_alloc(
20327978SPeter.Dunlap@Sun.COM 	    itask->it_ict->ict_sess->ist_lport,
20337978SPeter.Dunlap@Sun.COM 	    itask->it_ict->ict_sess->ist_stmf_sess, iscsi_scsi->lun,
20347978SPeter.Dunlap@Sun.COM 	    16 + addl_cdb_len, 0);
20357978SPeter.Dunlap@Sun.COM 	if (itask->it_stmf_task == NULL) {
20367978SPeter.Dunlap@Sun.COM 		/*
20377978SPeter.Dunlap@Sun.COM 		 * Either stmf really couldn't get memory for a task or,
20387978SPeter.Dunlap@Sun.COM 		 * more likely, the LU is currently in reset.  Either way
20397978SPeter.Dunlap@Sun.COM 		 * we have no choice but to fail the request.
20407978SPeter.Dunlap@Sun.COM 		 */
20419586SPeter.Dunlap@Sun.COM 		iscsit_task_done(itask);
20427978SPeter.Dunlap@Sun.COM 		iscsit_task_free(itask);
20437978SPeter.Dunlap@Sun.COM 		iscsit_send_direct_scsi_resp(ict, rx_pdu,
20447978SPeter.Dunlap@Sun.COM 		    ISCSI_STATUS_CMD_COMPLETED, STATUS_BUSY);
20459586SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
20467978SPeter.Dunlap@Sun.COM 		return;
20477978SPeter.Dunlap@Sun.COM 	}
20487978SPeter.Dunlap@Sun.COM 
20497978SPeter.Dunlap@Sun.COM 	task = itask->it_stmf_task;
20507978SPeter.Dunlap@Sun.COM 	task->task_port_private = itask;
20517978SPeter.Dunlap@Sun.COM 
20527978SPeter.Dunlap@Sun.COM 	bcopy(iscsi_scsi->lun, task->task_lun_no, sizeof (task->task_lun_no));
20537978SPeter.Dunlap@Sun.COM 
20547978SPeter.Dunlap@Sun.COM 	/*
20557978SPeter.Dunlap@Sun.COM 	 * iSCSI and Comstar use the same values.  Should we rely on this
20567978SPeter.Dunlap@Sun.COM 	 * or translate them bit-wise?
20577978SPeter.Dunlap@Sun.COM 	 */
20587978SPeter.Dunlap@Sun.COM 
20597978SPeter.Dunlap@Sun.COM 	task->task_flags =
20607978SPeter.Dunlap@Sun.COM 	    (((iscsi_scsi->flags & ISCSI_FLAG_CMD_READ) ? TF_READ_DATA : 0) |
20617978SPeter.Dunlap@Sun.COM 	    ((iscsi_scsi->flags & ISCSI_FLAG_CMD_WRITE) ? TF_WRITE_DATA : 0) |
20627978SPeter.Dunlap@Sun.COM 	    ((rx_pdu->isp_datalen == 0) ? 0 : TF_INITIAL_BURST));
20637978SPeter.Dunlap@Sun.COM 
20647978SPeter.Dunlap@Sun.COM 	switch (iscsi_scsi->flags & ISCSI_FLAG_CMD_ATTR_MASK) {
20657978SPeter.Dunlap@Sun.COM 	case ISCSI_ATTR_UNTAGGED:
20667978SPeter.Dunlap@Sun.COM 		break;
20677978SPeter.Dunlap@Sun.COM 	case ISCSI_ATTR_SIMPLE:
20687978SPeter.Dunlap@Sun.COM 		task->task_additional_flags |= TF_ATTR_SIMPLE_QUEUE;
20697978SPeter.Dunlap@Sun.COM 		break;
20707978SPeter.Dunlap@Sun.COM 	case ISCSI_ATTR_ORDERED:
20717978SPeter.Dunlap@Sun.COM 		task->task_additional_flags |= TF_ATTR_ORDERED_QUEUE;
20727978SPeter.Dunlap@Sun.COM 		break;
20737978SPeter.Dunlap@Sun.COM 	case ISCSI_ATTR_HEAD_OF_QUEUE:
20747978SPeter.Dunlap@Sun.COM 		task->task_additional_flags |= TF_ATTR_HEAD_OF_QUEUE;
20757978SPeter.Dunlap@Sun.COM 		break;
20767978SPeter.Dunlap@Sun.COM 	case ISCSI_ATTR_ACA:
20777978SPeter.Dunlap@Sun.COM 		task->task_additional_flags |= TF_ATTR_ACA;
20787978SPeter.Dunlap@Sun.COM 		break;
20797978SPeter.Dunlap@Sun.COM 	default:
20807978SPeter.Dunlap@Sun.COM 		/* Protocol error but just take it, treat as untagged */
20817978SPeter.Dunlap@Sun.COM 		break;
20827978SPeter.Dunlap@Sun.COM 	}
20837978SPeter.Dunlap@Sun.COM 
20847978SPeter.Dunlap@Sun.COM 
20857978SPeter.Dunlap@Sun.COM 	task->task_additional_flags = 0;
20867978SPeter.Dunlap@Sun.COM 	task->task_priority = 0;
20877978SPeter.Dunlap@Sun.COM 	task->task_mgmt_function = TM_NONE;
20887978SPeter.Dunlap@Sun.COM 
20897978SPeter.Dunlap@Sun.COM 	/*
20907978SPeter.Dunlap@Sun.COM 	 * This "task_max_nbufs" doesn't map well to BIDI.  We probably need
20917978SPeter.Dunlap@Sun.COM 	 * parameter for each direction.  "MaxOutstandingR2T" may very well
20927978SPeter.Dunlap@Sun.COM 	 * be set to one which could prevent us from doing simultaneous
20937978SPeter.Dunlap@Sun.COM 	 * transfers in each direction.
20947978SPeter.Dunlap@Sun.COM 	 */
20957978SPeter.Dunlap@Sun.COM 	task->task_max_nbufs = (iscsi_scsi->flags & ISCSI_FLAG_CMD_WRITE) ?
20967978SPeter.Dunlap@Sun.COM 	    ict->ict_op.op_max_outstanding_r2t : STMF_BUFS_MAX;
20977978SPeter.Dunlap@Sun.COM 	task->task_cmd_seq_no = ntohl(iscsi_scsi->itt);
20987978SPeter.Dunlap@Sun.COM 	task->task_expected_xfer_length = ntohl(iscsi_scsi->data_length);
20997978SPeter.Dunlap@Sun.COM 
21007978SPeter.Dunlap@Sun.COM 	/* Copy CDB */
21017978SPeter.Dunlap@Sun.COM 	bcopy(iscsi_scsi->scb, task->task_cdb, 16);
21027978SPeter.Dunlap@Sun.COM 	if (addl_cdb_len > 0) {
21037978SPeter.Dunlap@Sun.COM 		bcopy(ahs_hdr->ahs_extscb, task->task_cdb + 16, addl_cdb_len);
21047978SPeter.Dunlap@Sun.COM 	}
21057978SPeter.Dunlap@Sun.COM 
21069721SPriya.Krishnan@Sun.COM 	DTRACE_ISCSI_3(scsi__command, idm_conn_t *, ic,
21079721SPriya.Krishnan@Sun.COM 	    iscsi_scsi_cmd_hdr_t *, (iscsi_scsi_cmd_hdr_t *)rx_pdu->isp_hdr,
21089721SPriya.Krishnan@Sun.COM 	    scsi_task_t *, task);
21099721SPriya.Krishnan@Sun.COM 
21107978SPeter.Dunlap@Sun.COM 	/*
21117978SPeter.Dunlap@Sun.COM 	 * Copy the transport header into the task handle from the PDU
21127978SPeter.Dunlap@Sun.COM 	 * handle. The transport header describes this task's remote tagged
21137978SPeter.Dunlap@Sun.COM 	 * buffer.
21147978SPeter.Dunlap@Sun.COM 	 */
21157978SPeter.Dunlap@Sun.COM 	if (rx_pdu->isp_transport_hdrlen != 0) {
21167978SPeter.Dunlap@Sun.COM 		bcopy(rx_pdu->isp_transport_hdr,
21177978SPeter.Dunlap@Sun.COM 		    itask->it_idm_task->idt_transport_hdr,
21187978SPeter.Dunlap@Sun.COM 		    rx_pdu->isp_transport_hdrlen);
21197978SPeter.Dunlap@Sun.COM 	}
21207978SPeter.Dunlap@Sun.COM 
21217978SPeter.Dunlap@Sun.COM 	/*
21227978SPeter.Dunlap@Sun.COM 	 * Tell IDM about our new active task
21237978SPeter.Dunlap@Sun.COM 	 */
21247978SPeter.Dunlap@Sun.COM 	idm_task_start(itask->it_idm_task, (uintptr_t)itask->it_itt);
21257978SPeter.Dunlap@Sun.COM 
21267978SPeter.Dunlap@Sun.COM 	/*
21277978SPeter.Dunlap@Sun.COM 	 * If we have any immediate data then setup the immediate buffer
21287978SPeter.Dunlap@Sun.COM 	 * context that comes with the task
21297978SPeter.Dunlap@Sun.COM 	 */
21307978SPeter.Dunlap@Sun.COM 	if (rx_pdu->isp_datalen) {
21317978SPeter.Dunlap@Sun.COM 		ibuf = itask->it_immed_data;
21327978SPeter.Dunlap@Sun.COM 		ibuf->ibuf_immed_data_pdu = rx_pdu;
21337978SPeter.Dunlap@Sun.COM 		ibuf->ibuf_stmf_buf->db_data_size = rx_pdu->isp_datalen;
21347978SPeter.Dunlap@Sun.COM 		ibuf->ibuf_stmf_buf->db_buf_size = rx_pdu->isp_datalen;
21357978SPeter.Dunlap@Sun.COM 		ibuf->ibuf_stmf_buf->db_relative_offset = 0;
21367978SPeter.Dunlap@Sun.COM 		ibuf->ibuf_stmf_buf->db_sglist[0].seg_length =
21377978SPeter.Dunlap@Sun.COM 		    rx_pdu->isp_datalen;
21387978SPeter.Dunlap@Sun.COM 		ibuf->ibuf_stmf_buf->db_sglist[0].seg_addr = rx_pdu->isp_data;
21397978SPeter.Dunlap@Sun.COM 
21409721SPriya.Krishnan@Sun.COM 		DTRACE_ISCSI_8(xfer__start, idm_conn_t *, ic,
21419721SPriya.Krishnan@Sun.COM 		    uintptr_t, ibuf->ibuf_stmf_buf->db_sglist[0].seg_addr,
21429721SPriya.Krishnan@Sun.COM 		    uint32_t, ibuf->ibuf_stmf_buf->db_relative_offset,
21439721SPriya.Krishnan@Sun.COM 		    uint64_t, 0, uint32_t, 0, uint32_t, 0, /* no raddr */
21449721SPriya.Krishnan@Sun.COM 		    uint32_t, rx_pdu->isp_datalen, int, XFER_BUF_TX_TO_INI);
21459721SPriya.Krishnan@Sun.COM 
21469934SPriya.Krishnan@Sun.COM 		/*
21479934SPriya.Krishnan@Sun.COM 		 * For immediate data transfer, there is no callback from
21489934SPriya.Krishnan@Sun.COM 		 * stmf to indicate that the initial burst of data is
21499934SPriya.Krishnan@Sun.COM 		 * transferred successfully. In some cases, the task can
21509934SPriya.Krishnan@Sun.COM 		 * get freed before execution returns from stmf_post_task.
21519934SPriya.Krishnan@Sun.COM 		 * Although this xfer-start/done probe accurately tracks
21529934SPriya.Krishnan@Sun.COM 		 * the size of the transfer, it does only provide a best
21539934SPriya.Krishnan@Sun.COM 		 * effort on the timing of the transfer.
21549934SPriya.Krishnan@Sun.COM 		 */
21559721SPriya.Krishnan@Sun.COM 		DTRACE_ISCSI_8(xfer__done, idm_conn_t *, ic,
21569721SPriya.Krishnan@Sun.COM 		    uintptr_t, ibuf->ibuf_stmf_buf->db_sglist[0].seg_addr,
21579721SPriya.Krishnan@Sun.COM 		    uint32_t, ibuf->ibuf_stmf_buf->db_relative_offset,
21589721SPriya.Krishnan@Sun.COM 		    uint64_t, 0, uint32_t, 0, uint32_t, 0, /* no raddr */
21599721SPriya.Krishnan@Sun.COM 		    uint32_t, rx_pdu->isp_datalen, int, XFER_BUF_TX_TO_INI);
21609934SPriya.Krishnan@Sun.COM 		stmf_post_task(task, ibuf->ibuf_stmf_buf);
21617978SPeter.Dunlap@Sun.COM 	} else {
21629934SPriya.Krishnan@Sun.COM 
21637978SPeter.Dunlap@Sun.COM 		stmf_post_task(task, NULL);
21647978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
21657978SPeter.Dunlap@Sun.COM 	}
21667978SPeter.Dunlap@Sun.COM }
21677978SPeter.Dunlap@Sun.COM 
21687978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
21697978SPeter.Dunlap@Sun.COM void
iscsit_deferred_dispatch(idm_pdu_t * rx_pdu)21707978SPeter.Dunlap@Sun.COM iscsit_deferred_dispatch(idm_pdu_t *rx_pdu)
21717978SPeter.Dunlap@Sun.COM {
21727978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict = rx_pdu->isp_ic->ic_handle;
21737978SPeter.Dunlap@Sun.COM 
21747978SPeter.Dunlap@Sun.COM 	/*
21757978SPeter.Dunlap@Sun.COM 	 * If the connection has been lost then ignore new PDU's
21767978SPeter.Dunlap@Sun.COM 	 */
21777978SPeter.Dunlap@Sun.COM 	mutex_enter(&ict->ict_mutex);
21787978SPeter.Dunlap@Sun.COM 	if (ict->ict_lost) {
21797978SPeter.Dunlap@Sun.COM 		mutex_exit(&ict->ict_mutex);
21807978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_FAIL);
21817978SPeter.Dunlap@Sun.COM 		return;
21827978SPeter.Dunlap@Sun.COM 	}
21837978SPeter.Dunlap@Sun.COM 
21847978SPeter.Dunlap@Sun.COM 	/*
21857978SPeter.Dunlap@Sun.COM 	 * Grab a hold on the connection to prevent it from going away
21867978SPeter.Dunlap@Sun.COM 	 * between now and when the taskq function is called.
21877978SPeter.Dunlap@Sun.COM 	 */
21887978SPeter.Dunlap@Sun.COM 	iscsit_conn_dispatch_hold(ict);
21897978SPeter.Dunlap@Sun.COM 	mutex_exit(&ict->ict_mutex);
21907978SPeter.Dunlap@Sun.COM 
21917978SPeter.Dunlap@Sun.COM 	if (taskq_dispatch(iscsit_global.global_dispatch_taskq,
21927978SPeter.Dunlap@Sun.COM 	    iscsit_deferred, rx_pdu, DDI_NOSLEEP) == NULL) {
21937978SPeter.Dunlap@Sun.COM 		/*
21947978SPeter.Dunlap@Sun.COM 		 * In the unlikely scenario that we couldn't get the resources
21957978SPeter.Dunlap@Sun.COM 		 * to dispatch the PDU then just drop it.
21967978SPeter.Dunlap@Sun.COM 		 */
21977978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_FAIL);
21987978SPeter.Dunlap@Sun.COM 		idm_conn_event(ict->ict_ic, CE_TRANSPORT_FAIL, NULL);
21997978SPeter.Dunlap@Sun.COM 		iscsit_conn_dispatch_rele(ict);
22007978SPeter.Dunlap@Sun.COM 	}
22017978SPeter.Dunlap@Sun.COM }
22027978SPeter.Dunlap@Sun.COM 
22037978SPeter.Dunlap@Sun.COM static void
iscsit_deferred(void * rx_pdu_void)22047978SPeter.Dunlap@Sun.COM iscsit_deferred(void *rx_pdu_void)
22057978SPeter.Dunlap@Sun.COM {
220612372SPriya.Krishnan@Sun.COM 	idm_pdu_t		*rx_pdu = rx_pdu_void;
220712372SPriya.Krishnan@Sun.COM 	idm_conn_t		*ic = rx_pdu->isp_ic;
220812372SPriya.Krishnan@Sun.COM 	iscsit_conn_t		*ict = ic->ic_handle;
220912372SPriya.Krishnan@Sun.COM 
221012372SPriya.Krishnan@Sun.COM 	/*
221112372SPriya.Krishnan@Sun.COM 	 * NOP and Task Management Commands can be marked for immediate
221212372SPriya.Krishnan@Sun.COM 	 * delivery. Commands marked as 'Immediate' are to be considered
221312372SPriya.Krishnan@Sun.COM 	 * for execution as soon as they arrive on the target. So these
221412372SPriya.Krishnan@Sun.COM 	 * should not be checked for sequence order and put in a queue.
221512372SPriya.Krishnan@Sun.COM 	 * The CmdSN is not advanced for Immediate Commands.
221612372SPriya.Krishnan@Sun.COM 	 */
22177978SPeter.Dunlap@Sun.COM 	switch (IDM_PDU_OPCODE(rx_pdu)) {
22187978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_NOOP_OUT:
221912372SPriya.Krishnan@Sun.COM 		if (iscsit_check_cmdsn_and_queue(rx_pdu)) {
222012372SPriya.Krishnan@Sun.COM 			iscsit_set_cmdsn(ict, rx_pdu);
222112372SPriya.Krishnan@Sun.COM 			iscsit_pdu_op_noop(ict, rx_pdu);
222212372SPriya.Krishnan@Sun.COM 		}
22237978SPeter.Dunlap@Sun.COM 		break;
22247978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGIN_CMD:
22257978SPeter.Dunlap@Sun.COM 		iscsit_pdu_op_login_cmd(ict, rx_pdu);
222612372SPriya.Krishnan@Sun.COM 		iscsit_conn_dispatch_rele(ict);
222712372SPriya.Krishnan@Sun.COM 		return;
22287978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_TEXT_CMD:
222912372SPriya.Krishnan@Sun.COM 		if (iscsit_check_cmdsn_and_queue(rx_pdu)) {
223012372SPriya.Krishnan@Sun.COM 			iscsit_set_cmdsn(ict, rx_pdu);
223112372SPriya.Krishnan@Sun.COM 			iscsit_pdu_op_text_cmd(ict, rx_pdu);
223212372SPriya.Krishnan@Sun.COM 		}
22337978SPeter.Dunlap@Sun.COM 		break;
22347978SPeter.Dunlap@Sun.COM 	case ISCSI_OP_LOGOUT_CMD:
223512372SPriya.Krishnan@Sun.COM 		if (iscsit_check_cmdsn_and_queue(rx_pdu)) {
223612372SPriya.Krishnan@Sun.COM 			iscsit_set_cmdsn(ict, rx_pdu);
223712372SPriya.Krishnan@Sun.COM 			iscsit_pdu_op_logout_cmd(ict, rx_pdu);
223812372SPriya.Krishnan@Sun.COM 		}
22397978SPeter.Dunlap@Sun.COM 		break;
22407978SPeter.Dunlap@Sun.COM 	default:
22417978SPeter.Dunlap@Sun.COM 		/* Protocol error.  IDM should have caught this */
22427978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_FAIL);
22437978SPeter.Dunlap@Sun.COM 		ASSERT(0);
22447978SPeter.Dunlap@Sun.COM 		break;
22457978SPeter.Dunlap@Sun.COM 	}
224612372SPriya.Krishnan@Sun.COM 	/*
224712372SPriya.Krishnan@Sun.COM 	 * Check if there are other PDUs in the session staging queue
224812372SPriya.Krishnan@Sun.COM 	 * waiting to be posted to SCSI layer.
224912372SPriya.Krishnan@Sun.COM 	 */
225012372SPriya.Krishnan@Sun.COM 	iscsit_process_pdu_in_queue(ict->ict_sess);
22517978SPeter.Dunlap@Sun.COM 
22527978SPeter.Dunlap@Sun.COM 	iscsit_conn_dispatch_rele(ict);
22537978SPeter.Dunlap@Sun.COM }
22547978SPeter.Dunlap@Sun.COM 
22557978SPeter.Dunlap@Sun.COM static void
iscsit_send_direct_scsi_resp(iscsit_conn_t * ict,idm_pdu_t * rx_pdu,uint8_t response,uint8_t cmd_status)22567978SPeter.Dunlap@Sun.COM iscsit_send_direct_scsi_resp(iscsit_conn_t *ict, idm_pdu_t *rx_pdu,
22577978SPeter.Dunlap@Sun.COM     uint8_t response, uint8_t cmd_status)
22587978SPeter.Dunlap@Sun.COM {
22597978SPeter.Dunlap@Sun.COM 	idm_pdu_t			*rsp_pdu;
22607978SPeter.Dunlap@Sun.COM 	idm_conn_t			*ic;
22617978SPeter.Dunlap@Sun.COM 	iscsi_scsi_rsp_hdr_t		*resp;
22629162SPeter.Dunlap@Sun.COM 	iscsi_scsi_cmd_hdr_t		*req =
22639162SPeter.Dunlap@Sun.COM 	    (iscsi_scsi_cmd_hdr_t *)rx_pdu->isp_hdr;
22647978SPeter.Dunlap@Sun.COM 
22657978SPeter.Dunlap@Sun.COM 	ic = ict->ict_ic;
22667978SPeter.Dunlap@Sun.COM 
22677978SPeter.Dunlap@Sun.COM 	rsp_pdu = idm_pdu_alloc(sizeof (iscsi_scsi_rsp_hdr_t), 0);
22687978SPeter.Dunlap@Sun.COM 	idm_pdu_init(rsp_pdu, ic, NULL, NULL);
226911081SPriya.Krishnan@Sun.COM 	/*
227011081SPriya.Krishnan@Sun.COM 	 * StatSN is incremented by 1 for every response sent on
227111081SPriya.Krishnan@Sun.COM 	 * a connection except for responses sent as a result of
227211081SPriya.Krishnan@Sun.COM 	 * a retry or SNACK
227311081SPriya.Krishnan@Sun.COM 	 */
227411081SPriya.Krishnan@Sun.COM 	rsp_pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
227511081SPriya.Krishnan@Sun.COM 
22767978SPeter.Dunlap@Sun.COM 	resp = (iscsi_scsi_rsp_hdr_t *)rsp_pdu->isp_hdr;
22777978SPeter.Dunlap@Sun.COM 
22787978SPeter.Dunlap@Sun.COM 	resp->opcode = ISCSI_OP_SCSI_RSP;
22797978SPeter.Dunlap@Sun.COM 	resp->flags = ISCSI_FLAG_FINAL;
22807978SPeter.Dunlap@Sun.COM 	resp->response = response;
22817978SPeter.Dunlap@Sun.COM 	resp->cmd_status = cmd_status;
22829162SPeter.Dunlap@Sun.COM 	resp->itt = req->itt;
22839162SPeter.Dunlap@Sun.COM 	if ((response == ISCSI_STATUS_CMD_COMPLETED) &&
22849162SPeter.Dunlap@Sun.COM 	    (req->data_length != 0) &&
22859162SPeter.Dunlap@Sun.COM 	    ((req->flags & ISCSI_FLAG_CMD_READ) ||
22869162SPeter.Dunlap@Sun.COM 	    (req->flags & ISCSI_FLAG_CMD_WRITE))) {
22879162SPeter.Dunlap@Sun.COM 		resp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
22889162SPeter.Dunlap@Sun.COM 		resp->residual_count = req->data_length;
22899162SPeter.Dunlap@Sun.COM 	}
22909162SPeter.Dunlap@Sun.COM 
22919162SPeter.Dunlap@Sun.COM 	DTRACE_PROBE4(iscsi__scsi__direct__response,
22929162SPeter.Dunlap@Sun.COM 	    iscsit_conn_t *, ict,
22939162SPeter.Dunlap@Sun.COM 	    uint8_t, resp->response,
22949162SPeter.Dunlap@Sun.COM 	    uint8_t, resp->cmd_status,
22959162SPeter.Dunlap@Sun.COM 	    idm_pdu_t *, rsp_pdu);
22967978SPeter.Dunlap@Sun.COM 
22977978SPeter.Dunlap@Sun.COM 	iscsit_pdu_tx(rsp_pdu);
22987978SPeter.Dunlap@Sun.COM }
22997978SPeter.Dunlap@Sun.COM 
23007978SPeter.Dunlap@Sun.COM void
iscsit_send_task_mgmt_resp(idm_pdu_t * tm_resp_pdu,uint8_t tm_status)23017978SPeter.Dunlap@Sun.COM iscsit_send_task_mgmt_resp(idm_pdu_t *tm_resp_pdu, uint8_t tm_status)
23027978SPeter.Dunlap@Sun.COM {
23037978SPeter.Dunlap@Sun.COM 	iscsi_scsi_task_mgt_rsp_hdr_t	*tm_resp;
23047978SPeter.Dunlap@Sun.COM 
230511081SPriya.Krishnan@Sun.COM 	/*
230611081SPriya.Krishnan@Sun.COM 	 * The target must take note of the last-sent StatSN.
230711081SPriya.Krishnan@Sun.COM 	 * The StatSN is to be incremented after sending a
230811081SPriya.Krishnan@Sun.COM 	 * task management response. Digest recovery can only
230911081SPriya.Krishnan@Sun.COM 	 * work if StatSN is incremented.
231011081SPriya.Krishnan@Sun.COM 	 */
231111081SPriya.Krishnan@Sun.COM 	tm_resp_pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
23127978SPeter.Dunlap@Sun.COM 	tm_resp = (iscsi_scsi_task_mgt_rsp_hdr_t *)tm_resp_pdu->isp_hdr;
23137978SPeter.Dunlap@Sun.COM 	tm_resp->response = tm_status;
23149162SPeter.Dunlap@Sun.COM 
23159162SPeter.Dunlap@Sun.COM 	DTRACE_PROBE3(iscsi__scsi__tm__response,
23169162SPeter.Dunlap@Sun.COM 	    iscsit_conn_t *, tm_resp_pdu->isp_ic->ic_handle,
23179162SPeter.Dunlap@Sun.COM 	    uint8_t, tm_resp->response,
23189162SPeter.Dunlap@Sun.COM 	    idm_pdu_t *, tm_resp_pdu);
23197978SPeter.Dunlap@Sun.COM 	iscsit_pdu_tx(tm_resp_pdu);
23207978SPeter.Dunlap@Sun.COM }
23217978SPeter.Dunlap@Sun.COM 
23227978SPeter.Dunlap@Sun.COM void
iscsit_op_scsi_task_mgmt(iscsit_conn_t * ict,idm_pdu_t * rx_pdu)23237978SPeter.Dunlap@Sun.COM iscsit_op_scsi_task_mgmt(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
23247978SPeter.Dunlap@Sun.COM {
23257978SPeter.Dunlap@Sun.COM 	idm_pdu_t			*tm_resp_pdu;
23267978SPeter.Dunlap@Sun.COM 	iscsit_task_t			*itask;
23277978SPeter.Dunlap@Sun.COM 	iscsit_task_t			*tm_itask;
23287978SPeter.Dunlap@Sun.COM 	scsi_task_t			*task;
23297978SPeter.Dunlap@Sun.COM 	iscsi_scsi_task_mgt_hdr_t 	*iscsi_tm =
23307978SPeter.Dunlap@Sun.COM 	    (iscsi_scsi_task_mgt_hdr_t *)rx_pdu->isp_hdr;
23317978SPeter.Dunlap@Sun.COM 	iscsi_scsi_task_mgt_rsp_hdr_t 	*iscsi_tm_rsp =
23327978SPeter.Dunlap@Sun.COM 	    (iscsi_scsi_task_mgt_rsp_hdr_t *)rx_pdu->isp_hdr;
23337978SPeter.Dunlap@Sun.COM 	uint32_t			rtt, cmdsn, refcmdsn;
23347978SPeter.Dunlap@Sun.COM 	uint8_t				tm_func;
23357978SPeter.Dunlap@Sun.COM 
23367978SPeter.Dunlap@Sun.COM 	/*
23377978SPeter.Dunlap@Sun.COM 	 * Setup response PDU (response field will get filled in later)
23387978SPeter.Dunlap@Sun.COM 	 */
23397978SPeter.Dunlap@Sun.COM 	tm_resp_pdu = idm_pdu_alloc(sizeof (iscsi_scsi_task_mgt_rsp_hdr_t), 0);
23407978SPeter.Dunlap@Sun.COM 	if (tm_resp_pdu == NULL) {
23417978SPeter.Dunlap@Sun.COM 		/* Can't respond, just drop it */
23427978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
23437978SPeter.Dunlap@Sun.COM 		return;
23447978SPeter.Dunlap@Sun.COM 	}
23457978SPeter.Dunlap@Sun.COM 	idm_pdu_init(tm_resp_pdu, ict->ict_ic, NULL, NULL);
23467978SPeter.Dunlap@Sun.COM 	iscsi_tm_rsp = (iscsi_scsi_task_mgt_rsp_hdr_t *)tm_resp_pdu->isp_hdr;
23477978SPeter.Dunlap@Sun.COM 	bzero(iscsi_tm_rsp, sizeof (iscsi_scsi_task_mgt_rsp_hdr_t));
23487978SPeter.Dunlap@Sun.COM 	iscsi_tm_rsp->opcode = ISCSI_OP_SCSI_TASK_MGT_RSP;
23497978SPeter.Dunlap@Sun.COM 	iscsi_tm_rsp->flags = ISCSI_FLAG_FINAL;
23507978SPeter.Dunlap@Sun.COM 	iscsi_tm_rsp->itt = rx_pdu->isp_hdr->itt;
23517978SPeter.Dunlap@Sun.COM 
23527978SPeter.Dunlap@Sun.COM 	/*
23537978SPeter.Dunlap@Sun.COM 	 * Figure out what we're being asked to do.
23547978SPeter.Dunlap@Sun.COM 	 */
23559162SPeter.Dunlap@Sun.COM 	DTRACE_PROBE4(iscsi__scsi__tm__request,
23569162SPeter.Dunlap@Sun.COM 	    iscsit_conn_t *, ict,
23579162SPeter.Dunlap@Sun.COM 	    uint8_t, (iscsi_tm->function & ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK),
23589162SPeter.Dunlap@Sun.COM 	    uint32_t, iscsi_tm->rtt,
23599162SPeter.Dunlap@Sun.COM 	    idm_pdu_t *, rx_pdu);
23607978SPeter.Dunlap@Sun.COM 	switch (iscsi_tm->function & ISCSI_FLAG_TASK_MGMT_FUNCTION_MASK) {
23617978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_ABORT_TASK:
23627978SPeter.Dunlap@Sun.COM 		/*
23637978SPeter.Dunlap@Sun.COM 		 * STMF doesn't currently support the "abort task" task
23647978SPeter.Dunlap@Sun.COM 		 * management command although it does support aborting
23657978SPeter.Dunlap@Sun.COM 		 * an individual task.  We'll get STMF to abort the task
23667978SPeter.Dunlap@Sun.COM 		 * for us but handle the details of the task management
23677978SPeter.Dunlap@Sun.COM 		 * command ourselves.
23687978SPeter.Dunlap@Sun.COM 		 *
23697978SPeter.Dunlap@Sun.COM 		 * Find the task associated with the referenced task tag.
23707978SPeter.Dunlap@Sun.COM 		 */
23717978SPeter.Dunlap@Sun.COM 		rtt = iscsi_tm->rtt;
23727978SPeter.Dunlap@Sun.COM 		itask = (iscsit_task_t *)idm_task_find_by_handle(ict->ict_ic,
23737978SPeter.Dunlap@Sun.COM 		    (uintptr_t)rtt);
23747978SPeter.Dunlap@Sun.COM 
23757978SPeter.Dunlap@Sun.COM 		if (itask == NULL) {
23767978SPeter.Dunlap@Sun.COM 			cmdsn = ntohl(iscsi_tm->cmdsn);
23777978SPeter.Dunlap@Sun.COM 			refcmdsn = ntohl(iscsi_tm->refcmdsn);
23787978SPeter.Dunlap@Sun.COM 
23797978SPeter.Dunlap@Sun.COM 			/*
238012372SPriya.Krishnan@Sun.COM 			 * Task was not found. But the SCSI command could be
238112372SPriya.Krishnan@Sun.COM 			 * on the rxpdu wait queue. If RefCmdSN is within
238212372SPriya.Krishnan@Sun.COM 			 * the CmdSN window and less than CmdSN of the TM
238312372SPriya.Krishnan@Sun.COM 			 * function, return "Function Complete". Otherwise,
238412372SPriya.Krishnan@Sun.COM 			 * return "Task Does Not Exist".
23857978SPeter.Dunlap@Sun.COM 			 */
23867978SPeter.Dunlap@Sun.COM 
23877978SPeter.Dunlap@Sun.COM 			if (iscsit_cmdsn_in_window(ict, refcmdsn) &&
238812372SPriya.Krishnan@Sun.COM 			    iscsit_sna_lt(refcmdsn, cmdsn)) {
238912372SPriya.Krishnan@Sun.COM 				mutex_enter(&ict->ict_sess->ist_sn_mutex);
239012372SPriya.Krishnan@Sun.COM 				(void) iscsit_remove_pdu_from_queue(
239112372SPriya.Krishnan@Sun.COM 				    ict->ict_sess, refcmdsn);
239212372SPriya.Krishnan@Sun.COM 				iscsit_conn_dispatch_rele(ict);
239312372SPriya.Krishnan@Sun.COM 				mutex_exit(&ict->ict_sess->ist_sn_mutex);
23947978SPeter.Dunlap@Sun.COM 				iscsit_send_task_mgmt_resp(tm_resp_pdu,
23957978SPeter.Dunlap@Sun.COM 				    SCSI_TCP_TM_RESP_COMPLETE);
23967978SPeter.Dunlap@Sun.COM 			} else {
23977978SPeter.Dunlap@Sun.COM 				iscsit_send_task_mgmt_resp(tm_resp_pdu,
23987978SPeter.Dunlap@Sun.COM 				    SCSI_TCP_TM_RESP_NO_TASK);
23997978SPeter.Dunlap@Sun.COM 			}
24007978SPeter.Dunlap@Sun.COM 		} else {
24017978SPeter.Dunlap@Sun.COM 
24027978SPeter.Dunlap@Sun.COM 			/*
24037978SPeter.Dunlap@Sun.COM 			 * Tell STMF to abort the task.  This will do no harm
24047978SPeter.Dunlap@Sun.COM 			 * if the task is already complete.
24057978SPeter.Dunlap@Sun.COM 			 */
24067978SPeter.Dunlap@Sun.COM 			stmf_abort(STMF_QUEUE_TASK_ABORT, itask->it_stmf_task,
24077978SPeter.Dunlap@Sun.COM 			    STMF_ABORTED, NULL);
24087978SPeter.Dunlap@Sun.COM 
24097978SPeter.Dunlap@Sun.COM 			/*
24107978SPeter.Dunlap@Sun.COM 			 * Make sure the task hasn't already completed
24117978SPeter.Dunlap@Sun.COM 			 */
24127978SPeter.Dunlap@Sun.COM 			mutex_enter(&itask->it_idm_task->idt_mutex);
24137978SPeter.Dunlap@Sun.COM 			if ((itask->it_idm_task->idt_state == TASK_COMPLETE) ||
24147978SPeter.Dunlap@Sun.COM 			    (itask->it_idm_task->idt_state == TASK_IDLE)) {
24157978SPeter.Dunlap@Sun.COM 				/*
24167978SPeter.Dunlap@Sun.COM 				 * Task is complete, return "Task Does Not
24177978SPeter.Dunlap@Sun.COM 				 * Exist"
24187978SPeter.Dunlap@Sun.COM 				 */
24197978SPeter.Dunlap@Sun.COM 				mutex_exit(&itask->it_idm_task->idt_mutex);
24207978SPeter.Dunlap@Sun.COM 				iscsit_send_task_mgmt_resp(tm_resp_pdu,
24217978SPeter.Dunlap@Sun.COM 				    SCSI_TCP_TM_RESP_NO_TASK);
24227978SPeter.Dunlap@Sun.COM 			} else {
24237978SPeter.Dunlap@Sun.COM 				/*
24247978SPeter.Dunlap@Sun.COM 				 * STMF is now aborting the task, return
24257978SPeter.Dunlap@Sun.COM 				 * "Function Complete"
24267978SPeter.Dunlap@Sun.COM 				 */
24277978SPeter.Dunlap@Sun.COM 				mutex_exit(&itask->it_idm_task->idt_mutex);
24287978SPeter.Dunlap@Sun.COM 				iscsit_send_task_mgmt_resp(tm_resp_pdu,
24297978SPeter.Dunlap@Sun.COM 				    SCSI_TCP_TM_RESP_COMPLETE);
24307978SPeter.Dunlap@Sun.COM 			}
24317978SPeter.Dunlap@Sun.COM 			idm_task_rele(itask->it_idm_task);
24327978SPeter.Dunlap@Sun.COM 		}
24337978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
24347978SPeter.Dunlap@Sun.COM 		return;
24357978SPeter.Dunlap@Sun.COM 
24367978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_ABORT_TASK_SET:
24377978SPeter.Dunlap@Sun.COM 		tm_func = TM_ABORT_TASK_SET;
24387978SPeter.Dunlap@Sun.COM 		break;
24397978SPeter.Dunlap@Sun.COM 
24407978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_CLEAR_ACA:
24417978SPeter.Dunlap@Sun.COM 		tm_func = TM_CLEAR_ACA;
24427978SPeter.Dunlap@Sun.COM 		break;
24437978SPeter.Dunlap@Sun.COM 
24447978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_CLEAR_TASK_SET:
24457978SPeter.Dunlap@Sun.COM 		tm_func = TM_CLEAR_TASK_SET;
24467978SPeter.Dunlap@Sun.COM 		break;
24477978SPeter.Dunlap@Sun.COM 
24487978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
24497978SPeter.Dunlap@Sun.COM 		tm_func = TM_LUN_RESET;
24507978SPeter.Dunlap@Sun.COM 		break;
24517978SPeter.Dunlap@Sun.COM 
24527978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_TARGET_WARM_RESET:
24537978SPeter.Dunlap@Sun.COM 		tm_func = TM_TARGET_WARM_RESET;
24547978SPeter.Dunlap@Sun.COM 		break;
24557978SPeter.Dunlap@Sun.COM 
24567978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_TARGET_COLD_RESET:
24577978SPeter.Dunlap@Sun.COM 		tm_func = TM_TARGET_COLD_RESET;
24587978SPeter.Dunlap@Sun.COM 		break;
24597978SPeter.Dunlap@Sun.COM 
24607978SPeter.Dunlap@Sun.COM 	case ISCSI_TM_FUNC_TASK_REASSIGN:
24617978SPeter.Dunlap@Sun.COM 		/*
24627978SPeter.Dunlap@Sun.COM 		 * We do not currently support allegiance reassignment.  When
24637978SPeter.Dunlap@Sun.COM 		 * we start supporting ERL1+, we will need to.
24647978SPeter.Dunlap@Sun.COM 		 */
24657978SPeter.Dunlap@Sun.COM 		iscsit_send_task_mgmt_resp(tm_resp_pdu,
24667978SPeter.Dunlap@Sun.COM 		    SCSI_TCP_TM_RESP_NO_ALLG_REASSN);
24677978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
24687978SPeter.Dunlap@Sun.COM 		return;
24697978SPeter.Dunlap@Sun.COM 
24707978SPeter.Dunlap@Sun.COM 	default:
24717978SPeter.Dunlap@Sun.COM 		iscsit_send_task_mgmt_resp(tm_resp_pdu,
24727978SPeter.Dunlap@Sun.COM 		    SCSI_TCP_TM_RESP_REJECTED);
24737978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
24747978SPeter.Dunlap@Sun.COM 		return;
24757978SPeter.Dunlap@Sun.COM 	}
24767978SPeter.Dunlap@Sun.COM 
24777978SPeter.Dunlap@Sun.COM 	tm_itask = iscsit_tm_task_alloc(ict);
24787978SPeter.Dunlap@Sun.COM 	if (tm_itask == NULL) {
24797978SPeter.Dunlap@Sun.COM 		iscsit_send_task_mgmt_resp(tm_resp_pdu,
24807978SPeter.Dunlap@Sun.COM 		    SCSI_TCP_TM_RESP_REJECTED);
24817978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
24827978SPeter.Dunlap@Sun.COM 		return;
24837978SPeter.Dunlap@Sun.COM 	}
24847978SPeter.Dunlap@Sun.COM 
24857978SPeter.Dunlap@Sun.COM 
24867978SPeter.Dunlap@Sun.COM 	task = stmf_task_alloc(ict->ict_sess->ist_lport,
24877978SPeter.Dunlap@Sun.COM 	    ict->ict_sess->ist_stmf_sess, iscsi_tm->lun,
24887978SPeter.Dunlap@Sun.COM 	    0, STMF_TASK_EXT_NONE);
24897978SPeter.Dunlap@Sun.COM 	if (task == NULL) {
24907978SPeter.Dunlap@Sun.COM 		/*
24917978SPeter.Dunlap@Sun.COM 		 * If this happens, either the LU is in reset, couldn't
24927978SPeter.Dunlap@Sun.COM 		 * get memory, or some other condition in which we simply
24937978SPeter.Dunlap@Sun.COM 		 * can't complete this request.  It would be nice to return
24947978SPeter.Dunlap@Sun.COM 		 * an error code like "busy" but the closest we have is
24957978SPeter.Dunlap@Sun.COM 		 * "rejected".
24967978SPeter.Dunlap@Sun.COM 		 */
24977978SPeter.Dunlap@Sun.COM 		iscsit_send_task_mgmt_resp(tm_resp_pdu,
24987978SPeter.Dunlap@Sun.COM 		    SCSI_TCP_TM_RESP_REJECTED);
24997978SPeter.Dunlap@Sun.COM 		iscsit_tm_task_free(tm_itask);
25007978SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
25017978SPeter.Dunlap@Sun.COM 		return;
25027978SPeter.Dunlap@Sun.COM 	}
25037978SPeter.Dunlap@Sun.COM 
25047978SPeter.Dunlap@Sun.COM 	tm_itask->it_tm_pdu = tm_resp_pdu;
25057978SPeter.Dunlap@Sun.COM 	tm_itask->it_stmf_task = task;
25067978SPeter.Dunlap@Sun.COM 	task->task_port_private = tm_itask;
25077978SPeter.Dunlap@Sun.COM 	task->task_mgmt_function = tm_func;
25087978SPeter.Dunlap@Sun.COM 	task->task_additional_flags = TASK_AF_NO_EXPECTED_XFER_LENGTH;
25097978SPeter.Dunlap@Sun.COM 	task->task_priority = 0;
25107978SPeter.Dunlap@Sun.COM 	task->task_max_nbufs = STMF_BUFS_MAX;
25117978SPeter.Dunlap@Sun.COM 	task->task_cmd_seq_no = iscsi_tm->itt;
25127978SPeter.Dunlap@Sun.COM 	task->task_expected_xfer_length = 0;
25137978SPeter.Dunlap@Sun.COM 
25147978SPeter.Dunlap@Sun.COM 	stmf_post_task(task, NULL);
25157978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
25167978SPeter.Dunlap@Sun.COM }
25177978SPeter.Dunlap@Sun.COM 
25187978SPeter.Dunlap@Sun.COM static void
iscsit_pdu_op_noop(iscsit_conn_t * ict,idm_pdu_t * rx_pdu)25197978SPeter.Dunlap@Sun.COM iscsit_pdu_op_noop(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
25207978SPeter.Dunlap@Sun.COM {
25217978SPeter.Dunlap@Sun.COM 	iscsi_nop_out_hdr_t *out = (iscsi_nop_out_hdr_t *)rx_pdu->isp_hdr;
25227978SPeter.Dunlap@Sun.COM 	iscsi_nop_in_hdr_t *in;
25237978SPeter.Dunlap@Sun.COM 	int resp_datalen;
25247978SPeter.Dunlap@Sun.COM 	idm_pdu_t *resp;
25257978SPeter.Dunlap@Sun.COM 
25267978SPeter.Dunlap@Sun.COM 	/* Ignore the response from initiator */
25279586SPeter.Dunlap@Sun.COM 	if ((out->itt == ISCSI_RSVD_TASK_TAG) ||
25289586SPeter.Dunlap@Sun.COM 	    (out->ttt != ISCSI_RSVD_TASK_TAG)) {
25299586SPeter.Dunlap@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
25307978SPeter.Dunlap@Sun.COM 		return;
25319586SPeter.Dunlap@Sun.COM 	}
25327978SPeter.Dunlap@Sun.COM 
25337978SPeter.Dunlap@Sun.COM 	/* Allocate a PDU to respond */
25347978SPeter.Dunlap@Sun.COM 	resp_datalen = ntoh24(out->dlength);
25357978SPeter.Dunlap@Sun.COM 	resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), resp_datalen);
25367978SPeter.Dunlap@Sun.COM 	idm_pdu_init(resp, ict->ict_ic, NULL, NULL);
25377978SPeter.Dunlap@Sun.COM 	if (resp_datalen > 0) {
25387978SPeter.Dunlap@Sun.COM 		bcopy(rx_pdu->isp_data, resp->isp_data, resp_datalen);
25397978SPeter.Dunlap@Sun.COM 	}
25407978SPeter.Dunlap@Sun.COM 
254111081SPriya.Krishnan@Sun.COM 	/*
254211081SPriya.Krishnan@Sun.COM 	 * When sending a NOP-In as a response to a NOP-Out from the initiator,
254311081SPriya.Krishnan@Sun.COM 	 * the target must respond with the same initiator task tag that was
254411081SPriya.Krishnan@Sun.COM 	 * provided in the NOP-Out request, the target transfer tag must be
254511081SPriya.Krishnan@Sun.COM 	 * ISCSI_RSVD_TASK_TAG (0xffffffff) and StatSN will contain the next
254611081SPriya.Krishnan@Sun.COM 	 * status sequence number. The StatSN for the connection is advanced
254711081SPriya.Krishnan@Sun.COM 	 * after this PDU is sent.
254811081SPriya.Krishnan@Sun.COM 	 */
25497978SPeter.Dunlap@Sun.COM 	in = (iscsi_nop_in_hdr_t *)resp->isp_hdr;
25507978SPeter.Dunlap@Sun.COM 	bzero(in, sizeof (*in));
25517978SPeter.Dunlap@Sun.COM 	in->opcode = ISCSI_OP_NOOP_IN;
25527978SPeter.Dunlap@Sun.COM 	in->flags = ISCSI_FLAG_FINAL;
25537978SPeter.Dunlap@Sun.COM 	bcopy(out->lun, in->lun, 8);
25547978SPeter.Dunlap@Sun.COM 	in->itt		= out->itt;
25557978SPeter.Dunlap@Sun.COM 	in->ttt		= ISCSI_RSVD_TASK_TAG;
25567978SPeter.Dunlap@Sun.COM 	hton24(in->dlength, resp_datalen);
255711081SPriya.Krishnan@Sun.COM 	resp->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
25587978SPeter.Dunlap@Sun.COM 	/* Any other field in resp to be set? */
25597978SPeter.Dunlap@Sun.COM 	iscsit_pdu_tx(resp);
25607978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
25617978SPeter.Dunlap@Sun.COM }
25627978SPeter.Dunlap@Sun.COM 
25637978SPeter.Dunlap@Sun.COM static void
iscsit_pdu_op_login_cmd(iscsit_conn_t * ict,idm_pdu_t * rx_pdu)25647978SPeter.Dunlap@Sun.COM iscsit_pdu_op_login_cmd(iscsit_conn_t	*ict, idm_pdu_t *rx_pdu)
25657978SPeter.Dunlap@Sun.COM {
25667978SPeter.Dunlap@Sun.COM 
25677978SPeter.Dunlap@Sun.COM 	/*
25687978SPeter.Dunlap@Sun.COM 	 * Submit PDU to login state machine.  State machine will free the
25697978SPeter.Dunlap@Sun.COM 	 * PDU.
25707978SPeter.Dunlap@Sun.COM 	 */
25717978SPeter.Dunlap@Sun.COM 	iscsit_login_sm_event(ict, ILE_LOGIN_RCV, rx_pdu);
25727978SPeter.Dunlap@Sun.COM }
25737978SPeter.Dunlap@Sun.COM 
25747978SPeter.Dunlap@Sun.COM void
iscsit_pdu_op_logout_cmd(iscsit_conn_t * ict,idm_pdu_t * rx_pdu)25757978SPeter.Dunlap@Sun.COM iscsit_pdu_op_logout_cmd(iscsit_conn_t	*ict, idm_pdu_t *rx_pdu)
25767978SPeter.Dunlap@Sun.COM {
25777978SPeter.Dunlap@Sun.COM 	iscsi_logout_hdr_t 	*logout_req =
25787978SPeter.Dunlap@Sun.COM 	    (iscsi_logout_hdr_t *)rx_pdu->isp_hdr;
25797978SPeter.Dunlap@Sun.COM 	iscsi_logout_rsp_hdr_t	*logout_rsp;
25807978SPeter.Dunlap@Sun.COM 	idm_pdu_t *resp;
25817978SPeter.Dunlap@Sun.COM 
25827978SPeter.Dunlap@Sun.COM 	/* Allocate a PDU to respond */
25837978SPeter.Dunlap@Sun.COM 	resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), 0);
25847978SPeter.Dunlap@Sun.COM 	idm_pdu_init(resp, ict->ict_ic, NULL, NULL);
258511081SPriya.Krishnan@Sun.COM 	/*
258611081SPriya.Krishnan@Sun.COM 	 * The StatSN is to be sent to the initiator,
258711081SPriya.Krishnan@Sun.COM 	 * it is not required to increment the number
258811081SPriya.Krishnan@Sun.COM 	 * as the connection is terminating.
258911081SPriya.Krishnan@Sun.COM 	 */
259011081SPriya.Krishnan@Sun.COM 	resp->isp_flags |= IDM_PDU_SET_STATSN;
25917978SPeter.Dunlap@Sun.COM 	/*
25927978SPeter.Dunlap@Sun.COM 	 * Logout results in the immediate termination of all tasks except
25937978SPeter.Dunlap@Sun.COM 	 * if the logout reason is ISCSI_LOGOUT_REASON_RECOVERY.  The
25947978SPeter.Dunlap@Sun.COM 	 * connection state machine will drive this task cleanup automatically
25957978SPeter.Dunlap@Sun.COM 	 * so we don't need to handle that here.
25967978SPeter.Dunlap@Sun.COM 	 */
25977978SPeter.Dunlap@Sun.COM 	logout_rsp = (iscsi_logout_rsp_hdr_t *)resp->isp_hdr;
25987978SPeter.Dunlap@Sun.COM 	bzero(logout_rsp, sizeof (*logout_rsp));
25997978SPeter.Dunlap@Sun.COM 	logout_rsp->opcode = ISCSI_OP_LOGOUT_RSP;
26007978SPeter.Dunlap@Sun.COM 	logout_rsp->flags = ISCSI_FLAG_FINAL;
26017978SPeter.Dunlap@Sun.COM 	logout_rsp->itt = logout_req->itt;
26027978SPeter.Dunlap@Sun.COM 	if ((logout_req->flags & ISCSI_FLAG_LOGOUT_REASON_MASK) >
26037978SPeter.Dunlap@Sun.COM 	    ISCSI_LOGOUT_REASON_RECOVERY) {
26047978SPeter.Dunlap@Sun.COM 		logout_rsp->response = ISCSI_LOGOUT_RECOVERY_UNSUPPORTED;
26057978SPeter.Dunlap@Sun.COM 	} else {
26067978SPeter.Dunlap@Sun.COM 		logout_rsp->response = ISCSI_LOGOUT_SUCCESS;
26077978SPeter.Dunlap@Sun.COM 	}
26087978SPeter.Dunlap@Sun.COM 
26097978SPeter.Dunlap@Sun.COM 	iscsit_pdu_tx(resp);
26107978SPeter.Dunlap@Sun.COM 	idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
26117978SPeter.Dunlap@Sun.COM }
26127978SPeter.Dunlap@Sun.COM 
26137978SPeter.Dunlap@Sun.COM /*
26147978SPeter.Dunlap@Sun.COM  * Calculate the number of outstanding commands we can process
26157978SPeter.Dunlap@Sun.COM  */
26167978SPeter.Dunlap@Sun.COM int
iscsit_cmd_window()26177978SPeter.Dunlap@Sun.COM iscsit_cmd_window()
26187978SPeter.Dunlap@Sun.COM {
261912372SPriya.Krishnan@Sun.COM 	/*
262012372SPriya.Krishnan@Sun.COM 	 * Instead of using a pre-defined constant for the command window,
262112372SPriya.Krishnan@Sun.COM 	 * it should be made confiurable and dynamic. With MC/S, sequence
262212372SPriya.Krishnan@Sun.COM 	 * numbers will be used up at a much faster rate than with SC/S.
262312372SPriya.Krishnan@Sun.COM 	 */
262412372SPriya.Krishnan@Sun.COM 	return	(ISCSIT_MAX_WINDOW);
26257978SPeter.Dunlap@Sun.COM }
26267978SPeter.Dunlap@Sun.COM 
26277978SPeter.Dunlap@Sun.COM /*
26287978SPeter.Dunlap@Sun.COM  * Set local registers based on incoming PDU
26297978SPeter.Dunlap@Sun.COM  */
26307978SPeter.Dunlap@Sun.COM void
iscsit_set_cmdsn(iscsit_conn_t * ict,idm_pdu_t * rx_pdu)26317978SPeter.Dunlap@Sun.COM iscsit_set_cmdsn(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
26327978SPeter.Dunlap@Sun.COM {
26337978SPeter.Dunlap@Sun.COM 	iscsit_sess_t *ist;
26347978SPeter.Dunlap@Sun.COM 	iscsi_scsi_cmd_hdr_t *req;
26357978SPeter.Dunlap@Sun.COM 
26367978SPeter.Dunlap@Sun.COM 	ist = ict->ict_sess;
26377978SPeter.Dunlap@Sun.COM 
26387978SPeter.Dunlap@Sun.COM 	req = (iscsi_scsi_cmd_hdr_t *)rx_pdu->isp_hdr;
263912372SPriya.Krishnan@Sun.COM 	if (req->opcode & ISCSI_OP_IMMEDIATE) {
264012372SPriya.Krishnan@Sun.COM 		/* no cmdsn increment for immediate PDUs */
264112372SPriya.Krishnan@Sun.COM 		return;
264212372SPriya.Krishnan@Sun.COM 	}
264312372SPriya.Krishnan@Sun.COM 
264412372SPriya.Krishnan@Sun.COM 	/* Ensure that the ExpCmdSN advances in an orderly manner */
264512372SPriya.Krishnan@Sun.COM 	mutex_enter(&ist->ist_sn_mutex);
26467978SPeter.Dunlap@Sun.COM 	ist->ist_expcmdsn = ntohl(req->cmdsn) + 1;
26477978SPeter.Dunlap@Sun.COM 	ist->ist_maxcmdsn = ntohl(req->cmdsn) + iscsit_cmd_window();
264812372SPriya.Krishnan@Sun.COM 	mutex_exit(&ist->ist_sn_mutex);
26497978SPeter.Dunlap@Sun.COM }
26507978SPeter.Dunlap@Sun.COM 
26517978SPeter.Dunlap@Sun.COM /*
26527978SPeter.Dunlap@Sun.COM  * Wrapper funtion, calls iscsi_calc_rspsn and idm_pdu_tx
26537978SPeter.Dunlap@Sun.COM  */
26547978SPeter.Dunlap@Sun.COM void
iscsit_pdu_tx(idm_pdu_t * pdu)26557978SPeter.Dunlap@Sun.COM iscsit_pdu_tx(idm_pdu_t *pdu)
26567978SPeter.Dunlap@Sun.COM {
26577978SPeter.Dunlap@Sun.COM 	iscsit_conn_t *ict = pdu->isp_ic->ic_handle;
265811081SPriya.Krishnan@Sun.COM 	iscsi_scsi_rsp_hdr_t *rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
265911081SPriya.Krishnan@Sun.COM 	iscsit_sess_t *ist = ict->ict_sess;
26607978SPeter.Dunlap@Sun.COM 
26617978SPeter.Dunlap@Sun.COM 	/*
266211081SPriya.Krishnan@Sun.COM 	 * The command sequence numbers are session-wide and must stay
266311081SPriya.Krishnan@Sun.COM 	 * consistent across the transfer, so protect the cmdsn with a
266412372SPriya.Krishnan@Sun.COM 	 * mutex lock on the session. The status sequence number will
266511081SPriya.Krishnan@Sun.COM 	 * be updated just before the transport layer transmits the PDU.
26667978SPeter.Dunlap@Sun.COM 	 */
266711081SPriya.Krishnan@Sun.COM 
266812372SPriya.Krishnan@Sun.COM 	mutex_enter(&ict->ict_sess->ist_sn_mutex);
266911081SPriya.Krishnan@Sun.COM 	/* Set ExpCmdSN and MaxCmdSN */
267011081SPriya.Krishnan@Sun.COM 	rsp->maxcmdsn = htonl(ist->ist_maxcmdsn);
267111081SPriya.Krishnan@Sun.COM 	rsp->expcmdsn = htonl(ist->ist_expcmdsn);
26727978SPeter.Dunlap@Sun.COM 	idm_pdu_tx(pdu);
267312372SPriya.Krishnan@Sun.COM 	mutex_exit(&ict->ict_sess->ist_sn_mutex);
26747978SPeter.Dunlap@Sun.COM }
26757978SPeter.Dunlap@Sun.COM 
26767978SPeter.Dunlap@Sun.COM /*
26777978SPeter.Dunlap@Sun.COM  * Internal functions
26787978SPeter.Dunlap@Sun.COM  */
26797978SPeter.Dunlap@Sun.COM 
26807978SPeter.Dunlap@Sun.COM void
iscsit_send_async_event(iscsit_conn_t * ict,uint8_t event)26817978SPeter.Dunlap@Sun.COM iscsit_send_async_event(iscsit_conn_t *ict, uint8_t event)
26827978SPeter.Dunlap@Sun.COM {
26837978SPeter.Dunlap@Sun.COM 	idm_pdu_t		*abt;
26847978SPeter.Dunlap@Sun.COM 	iscsi_async_evt_hdr_t	*async_abt;
26857978SPeter.Dunlap@Sun.COM 
26867978SPeter.Dunlap@Sun.COM 	/*
26877978SPeter.Dunlap@Sun.COM 	 * Get a PDU to build the abort request.
26887978SPeter.Dunlap@Sun.COM 	 */
26897978SPeter.Dunlap@Sun.COM 	abt = idm_pdu_alloc(sizeof (iscsi_hdr_t), 0);
26907978SPeter.Dunlap@Sun.COM 	if (abt == NULL) {
26917978SPeter.Dunlap@Sun.COM 		idm_conn_event(ict->ict_ic, CE_TRANSPORT_FAIL, NULL);
26927978SPeter.Dunlap@Sun.COM 		return;
26937978SPeter.Dunlap@Sun.COM 	}
26949586SPeter.Dunlap@Sun.COM 
269511081SPriya.Krishnan@Sun.COM 	/*
269611081SPriya.Krishnan@Sun.COM 	 * A asynchronous message is sent by the target to request a logout.
269711081SPriya.Krishnan@Sun.COM 	 * The StatSN for the connection is advanced after the PDU is sent
269811081SPriya.Krishnan@Sun.COM 	 * to allow for initiator and target state synchronization.
269911081SPriya.Krishnan@Sun.COM 	 */
27007978SPeter.Dunlap@Sun.COM 	idm_pdu_init(abt, ict->ict_ic, NULL, NULL);
27017978SPeter.Dunlap@Sun.COM 	abt->isp_datalen = 0;
270211081SPriya.Krishnan@Sun.COM 	abt->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
27037978SPeter.Dunlap@Sun.COM 
27047978SPeter.Dunlap@Sun.COM 	async_abt = (iscsi_async_evt_hdr_t *)abt->isp_hdr;
27057978SPeter.Dunlap@Sun.COM 	bzero(async_abt, sizeof (*async_abt));
27067978SPeter.Dunlap@Sun.COM 	async_abt->opcode = ISCSI_OP_ASYNC_EVENT;
27077978SPeter.Dunlap@Sun.COM 	async_abt->async_event = event;
27087978SPeter.Dunlap@Sun.COM 	async_abt->flags = ISCSI_FLAG_FINAL;
27097978SPeter.Dunlap@Sun.COM 	async_abt->rsvd4[0] = 0xff;
27107978SPeter.Dunlap@Sun.COM 	async_abt->rsvd4[1] = 0xff;
27117978SPeter.Dunlap@Sun.COM 	async_abt->rsvd4[2] = 0xff;
27127978SPeter.Dunlap@Sun.COM 	async_abt->rsvd4[3] = 0xff;
27137978SPeter.Dunlap@Sun.COM 
27147978SPeter.Dunlap@Sun.COM 	switch (event) {
27157978SPeter.Dunlap@Sun.COM 	case ISCSI_ASYNC_EVENT_REQUEST_LOGOUT:
27167978SPeter.Dunlap@Sun.COM 		async_abt->param3 = htons(IDM_LOGOUT_SECONDS);
27177978SPeter.Dunlap@Sun.COM 		break;
27187978SPeter.Dunlap@Sun.COM 	case ISCSI_ASYNC_EVENT_SCSI_EVENT:
27197978SPeter.Dunlap@Sun.COM 	case ISCSI_ASYNC_EVENT_DROPPING_CONNECTION:
27207978SPeter.Dunlap@Sun.COM 	case ISCSI_ASYNC_EVENT_DROPPING_ALL_CONNECTIONS:
27217978SPeter.Dunlap@Sun.COM 	case ISCSI_ASYNC_EVENT_PARAM_NEGOTIATION:
27227978SPeter.Dunlap@Sun.COM 	default:
27237978SPeter.Dunlap@Sun.COM 		ASSERT(0);
27247978SPeter.Dunlap@Sun.COM 	}
27257978SPeter.Dunlap@Sun.COM 
27267978SPeter.Dunlap@Sun.COM 	iscsit_pdu_tx(abt);
27277978SPeter.Dunlap@Sun.COM }
27287978SPeter.Dunlap@Sun.COM 
27299586SPeter.Dunlap@Sun.COM void
iscsit_send_reject(iscsit_conn_t * ict,idm_pdu_t * rejected_pdu,uint8_t reason)27309586SPeter.Dunlap@Sun.COM iscsit_send_reject(iscsit_conn_t *ict, idm_pdu_t *rejected_pdu, uint8_t reason)
27319586SPeter.Dunlap@Sun.COM {
27329586SPeter.Dunlap@Sun.COM 	idm_pdu_t		*reject_pdu;
27339586SPeter.Dunlap@Sun.COM 	iscsi_reject_rsp_hdr_t	*reject;
27349586SPeter.Dunlap@Sun.COM 
27359586SPeter.Dunlap@Sun.COM 	/*
27369586SPeter.Dunlap@Sun.COM 	 * Get a PDU to build the abort request.
27379586SPeter.Dunlap@Sun.COM 	 */
27389586SPeter.Dunlap@Sun.COM 	reject_pdu = idm_pdu_alloc(sizeof (iscsi_hdr_t),
27399586SPeter.Dunlap@Sun.COM 	    rejected_pdu->isp_hdrlen);
27409586SPeter.Dunlap@Sun.COM 	if (reject_pdu == NULL) {
27419586SPeter.Dunlap@Sun.COM 		idm_conn_event(ict->ict_ic, CE_TRANSPORT_FAIL, NULL);
27429586SPeter.Dunlap@Sun.COM 		return;
27439586SPeter.Dunlap@Sun.COM 	}
27449586SPeter.Dunlap@Sun.COM 	idm_pdu_init(reject_pdu, ict->ict_ic, NULL, NULL);
274511081SPriya.Krishnan@Sun.COM 	/* StatSN is advanced after a Reject PDU */
274611081SPriya.Krishnan@Sun.COM 	reject_pdu->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
27479586SPeter.Dunlap@Sun.COM 	reject_pdu->isp_datalen = rejected_pdu->isp_hdrlen;
27489586SPeter.Dunlap@Sun.COM 	bcopy(rejected_pdu->isp_hdr, reject_pdu->isp_data,
27499586SPeter.Dunlap@Sun.COM 	    rejected_pdu->isp_hdrlen);
27509586SPeter.Dunlap@Sun.COM 
27519586SPeter.Dunlap@Sun.COM 	reject = (iscsi_reject_rsp_hdr_t *)reject_pdu->isp_hdr;
27529586SPeter.Dunlap@Sun.COM 	bzero(reject, sizeof (*reject));
27539586SPeter.Dunlap@Sun.COM 	reject->opcode = ISCSI_OP_REJECT_MSG;
27549586SPeter.Dunlap@Sun.COM 	reject->reason = reason;
27559586SPeter.Dunlap@Sun.COM 	reject->flags = ISCSI_FLAG_FINAL;
27569586SPeter.Dunlap@Sun.COM 	hton24(reject->dlength, rejected_pdu->isp_hdrlen);
27579586SPeter.Dunlap@Sun.COM 	reject->must_be_ff[0] = 0xff;
27589586SPeter.Dunlap@Sun.COM 	reject->must_be_ff[1] = 0xff;
27599586SPeter.Dunlap@Sun.COM 	reject->must_be_ff[2] = 0xff;
27609586SPeter.Dunlap@Sun.COM 	reject->must_be_ff[3] = 0xff;
27619586SPeter.Dunlap@Sun.COM 
27629586SPeter.Dunlap@Sun.COM 	iscsit_pdu_tx(reject_pdu);
27639586SPeter.Dunlap@Sun.COM }
27649586SPeter.Dunlap@Sun.COM 
27657978SPeter.Dunlap@Sun.COM 
27667978SPeter.Dunlap@Sun.COM static iscsit_task_t *
iscsit_task_alloc(iscsit_conn_t * ict)27677978SPeter.Dunlap@Sun.COM iscsit_task_alloc(iscsit_conn_t *ict)
27687978SPeter.Dunlap@Sun.COM {
27697978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask;
27707978SPeter.Dunlap@Sun.COM 	iscsit_buf_t *immed_ibuf;
27717978SPeter.Dunlap@Sun.COM 
27727978SPeter.Dunlap@Sun.COM 	/*
27737978SPeter.Dunlap@Sun.COM 	 * Possible items to pre-alloc if we cache iscsit_task_t's:
27747978SPeter.Dunlap@Sun.COM 	 *
27757978SPeter.Dunlap@Sun.COM 	 * Status PDU w/ sense buffer
27767978SPeter.Dunlap@Sun.COM 	 * stmf_data_buf_t for immediate data
27777978SPeter.Dunlap@Sun.COM 	 */
27787978SPeter.Dunlap@Sun.COM 	itask = kmem_alloc(sizeof (iscsit_task_t) + sizeof (iscsit_buf_t) +
27797978SPeter.Dunlap@Sun.COM 	    sizeof (stmf_data_buf_t), KM_NOSLEEP);
27807978SPeter.Dunlap@Sun.COM 	if (itask != NULL) {
27817978SPeter.Dunlap@Sun.COM 		mutex_init(&itask->it_mutex, NULL, MUTEX_DRIVER, NULL);
27827978SPeter.Dunlap@Sun.COM 		itask->it_aborted = itask->it_stmf_abort =
27837978SPeter.Dunlap@Sun.COM 		    itask->it_tm_task = 0;
27847978SPeter.Dunlap@Sun.COM 
27857978SPeter.Dunlap@Sun.COM 		immed_ibuf = (iscsit_buf_t *)(itask + 1);
27867978SPeter.Dunlap@Sun.COM 		bzero(immed_ibuf, sizeof (*immed_ibuf));
27877978SPeter.Dunlap@Sun.COM 		immed_ibuf->ibuf_is_immed = B_TRUE;
27887978SPeter.Dunlap@Sun.COM 		immed_ibuf->ibuf_stmf_buf = (stmf_data_buf_t *)(immed_ibuf + 1);
27897978SPeter.Dunlap@Sun.COM 
27907978SPeter.Dunlap@Sun.COM 		bzero(immed_ibuf->ibuf_stmf_buf, sizeof (stmf_data_buf_t));
27917978SPeter.Dunlap@Sun.COM 		immed_ibuf->ibuf_stmf_buf->db_port_private = immed_ibuf;
27927978SPeter.Dunlap@Sun.COM 		immed_ibuf->ibuf_stmf_buf->db_sglist_length = 1;
27937978SPeter.Dunlap@Sun.COM 		immed_ibuf->ibuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT |
27947978SPeter.Dunlap@Sun.COM 		    DB_DONT_CACHE;
27957978SPeter.Dunlap@Sun.COM 		itask->it_immed_data = immed_ibuf;
27967978SPeter.Dunlap@Sun.COM 		itask->it_idm_task = idm_task_alloc(ict->ict_ic);
27977978SPeter.Dunlap@Sun.COM 		if (itask->it_idm_task != NULL) {
27987978SPeter.Dunlap@Sun.COM 			itask->it_idm_task->idt_private = itask;
27997978SPeter.Dunlap@Sun.COM 			itask->it_ict = ict;
28007978SPeter.Dunlap@Sun.COM 			itask->it_ttt = itask->it_idm_task->idt_tt;
28017978SPeter.Dunlap@Sun.COM 			return (itask);
28027978SPeter.Dunlap@Sun.COM 		} else {
28037978SPeter.Dunlap@Sun.COM 			kmem_free(itask, sizeof (iscsit_task_t) +
28047978SPeter.Dunlap@Sun.COM 			    sizeof (iscsit_buf_t) + sizeof (stmf_data_buf_t));
28057978SPeter.Dunlap@Sun.COM 		}
28067978SPeter.Dunlap@Sun.COM 	}
28077978SPeter.Dunlap@Sun.COM 
28087978SPeter.Dunlap@Sun.COM 	return (NULL);
28097978SPeter.Dunlap@Sun.COM }
28107978SPeter.Dunlap@Sun.COM 
28117978SPeter.Dunlap@Sun.COM static void
iscsit_task_free(iscsit_task_t * itask)28127978SPeter.Dunlap@Sun.COM iscsit_task_free(iscsit_task_t *itask)
28137978SPeter.Dunlap@Sun.COM {
28147978SPeter.Dunlap@Sun.COM 	idm_task_free(itask->it_idm_task);
28157978SPeter.Dunlap@Sun.COM 	mutex_destroy(&itask->it_mutex);
28167978SPeter.Dunlap@Sun.COM 	kmem_free(itask, sizeof (iscsit_task_t) +
28177978SPeter.Dunlap@Sun.COM 	    sizeof (iscsit_buf_t) + sizeof (stmf_data_buf_t));
28187978SPeter.Dunlap@Sun.COM }
28197978SPeter.Dunlap@Sun.COM 
28207978SPeter.Dunlap@Sun.COM static iscsit_task_t *
iscsit_tm_task_alloc(iscsit_conn_t * ict)28217978SPeter.Dunlap@Sun.COM iscsit_tm_task_alloc(iscsit_conn_t *ict)
28227978SPeter.Dunlap@Sun.COM {
28237978SPeter.Dunlap@Sun.COM 	iscsit_task_t *itask;
28247978SPeter.Dunlap@Sun.COM 
28257978SPeter.Dunlap@Sun.COM 	itask = kmem_zalloc(sizeof (iscsit_task_t), KM_NOSLEEP);
28267978SPeter.Dunlap@Sun.COM 	if (itask != NULL) {
28277978SPeter.Dunlap@Sun.COM 		idm_conn_hold(ict->ict_ic);
28287978SPeter.Dunlap@Sun.COM 		mutex_init(&itask->it_mutex, NULL, MUTEX_DRIVER, NULL);
28297978SPeter.Dunlap@Sun.COM 		itask->it_aborted = itask->it_stmf_abort =
28307978SPeter.Dunlap@Sun.COM 		    itask->it_tm_responded = 0;
28317978SPeter.Dunlap@Sun.COM 		itask->it_tm_pdu = NULL;
28327978SPeter.Dunlap@Sun.COM 		itask->it_tm_task = 1;
28337978SPeter.Dunlap@Sun.COM 		itask->it_ict = ict;
28347978SPeter.Dunlap@Sun.COM 	}
28357978SPeter.Dunlap@Sun.COM 
28367978SPeter.Dunlap@Sun.COM 	return (itask);
28377978SPeter.Dunlap@Sun.COM }
28387978SPeter.Dunlap@Sun.COM 
28397978SPeter.Dunlap@Sun.COM static void
iscsit_tm_task_free(iscsit_task_t * itask)28407978SPeter.Dunlap@Sun.COM iscsit_tm_task_free(iscsit_task_t *itask)
28417978SPeter.Dunlap@Sun.COM {
28427978SPeter.Dunlap@Sun.COM 	/*
28437978SPeter.Dunlap@Sun.COM 	 * If we responded then the call to idm_pdu_complete will free the
28447978SPeter.Dunlap@Sun.COM 	 * PDU.  Otherwise we got aborted before the TM function could
28457978SPeter.Dunlap@Sun.COM 	 * complete and we need to free the PDU explicitly.
28467978SPeter.Dunlap@Sun.COM 	 */
28477978SPeter.Dunlap@Sun.COM 	if (itask->it_tm_pdu != NULL && !itask->it_tm_responded)
28487978SPeter.Dunlap@Sun.COM 		idm_pdu_free(itask->it_tm_pdu);
28497978SPeter.Dunlap@Sun.COM 	idm_conn_rele(itask->it_ict->ict_ic);
28507978SPeter.Dunlap@Sun.COM 	mutex_destroy(&itask->it_mutex);
28517978SPeter.Dunlap@Sun.COM 	kmem_free(itask, sizeof (iscsit_task_t));
28527978SPeter.Dunlap@Sun.COM }
28537978SPeter.Dunlap@Sun.COM 
28549586SPeter.Dunlap@Sun.COM static idm_status_t
iscsit_task_start(iscsit_task_t * itask)28559586SPeter.Dunlap@Sun.COM iscsit_task_start(iscsit_task_t *itask)
28569586SPeter.Dunlap@Sun.COM {
28579586SPeter.Dunlap@Sun.COM 	iscsit_sess_t *ist = itask->it_ict->ict_sess;
28589586SPeter.Dunlap@Sun.COM 	avl_index_t		where;
28599586SPeter.Dunlap@Sun.COM 
28609586SPeter.Dunlap@Sun.COM 	/*
28619586SPeter.Dunlap@Sun.COM 	 * Sanity check the ITT and ensure that this task does not already
28629586SPeter.Dunlap@Sun.COM 	 * exist.  If not then add the task to the session task list.
28639586SPeter.Dunlap@Sun.COM 	 */
28649586SPeter.Dunlap@Sun.COM 	mutex_enter(&ist->ist_mutex);
28659586SPeter.Dunlap@Sun.COM 	mutex_enter(&itask->it_mutex);
28669586SPeter.Dunlap@Sun.COM 	itask->it_active = 1;
28679586SPeter.Dunlap@Sun.COM 	if (avl_find(&ist->ist_task_list, itask, &where) == NULL) {
28689586SPeter.Dunlap@Sun.COM 		/* New task, add to AVL */
28699586SPeter.Dunlap@Sun.COM 		avl_insert(&ist->ist_task_list, itask, where);
28709586SPeter.Dunlap@Sun.COM 		mutex_exit(&itask->it_mutex);
28719586SPeter.Dunlap@Sun.COM 		mutex_exit(&ist->ist_mutex);
28729586SPeter.Dunlap@Sun.COM 		return (IDM_STATUS_SUCCESS);
28739586SPeter.Dunlap@Sun.COM 	}
28749586SPeter.Dunlap@Sun.COM 	mutex_exit(&itask->it_mutex);
28759586SPeter.Dunlap@Sun.COM 	mutex_exit(&ist->ist_mutex);
28769586SPeter.Dunlap@Sun.COM 
28779586SPeter.Dunlap@Sun.COM 	return (IDM_STATUS_REJECT);
28789586SPeter.Dunlap@Sun.COM }
28799586SPeter.Dunlap@Sun.COM 
28809586SPeter.Dunlap@Sun.COM static void
iscsit_task_done(iscsit_task_t * itask)28819586SPeter.Dunlap@Sun.COM iscsit_task_done(iscsit_task_t *itask)
28829586SPeter.Dunlap@Sun.COM {
28839586SPeter.Dunlap@Sun.COM 	iscsit_sess_t *ist = itask->it_ict->ict_sess;
28849586SPeter.Dunlap@Sun.COM 
28859586SPeter.Dunlap@Sun.COM 	mutex_enter(&ist->ist_mutex);
28869586SPeter.Dunlap@Sun.COM 	mutex_enter(&itask->it_mutex);
28879586SPeter.Dunlap@Sun.COM 	if (itask->it_active) {
28889586SPeter.Dunlap@Sun.COM 		avl_remove(&ist->ist_task_list, itask);
28899586SPeter.Dunlap@Sun.COM 		itask->it_active = 0;
28909586SPeter.Dunlap@Sun.COM 	}
28919586SPeter.Dunlap@Sun.COM 	mutex_exit(&itask->it_mutex);
28929586SPeter.Dunlap@Sun.COM 	mutex_exit(&ist->ist_mutex);
28939586SPeter.Dunlap@Sun.COM }
28949586SPeter.Dunlap@Sun.COM 
28957978SPeter.Dunlap@Sun.COM /*
28967978SPeter.Dunlap@Sun.COM  * iscsit status PDU cache
28977978SPeter.Dunlap@Sun.COM  */
28987978SPeter.Dunlap@Sun.COM 
28997978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
29007978SPeter.Dunlap@Sun.COM static int
iscsit_status_pdu_constructor(void * pdu_void,void * arg,int flags)29017978SPeter.Dunlap@Sun.COM iscsit_status_pdu_constructor(void *pdu_void, void *arg, int flags)
29027978SPeter.Dunlap@Sun.COM {
29037978SPeter.Dunlap@Sun.COM 	idm_pdu_t *pdu = pdu_void;
29047978SPeter.Dunlap@Sun.COM 	iscsi_scsi_rsp_hdr_t *rsp;
29057978SPeter.Dunlap@Sun.COM 
29067978SPeter.Dunlap@Sun.COM 	bzero(pdu, sizeof (idm_pdu_t));
29077978SPeter.Dunlap@Sun.COM 	pdu->isp_callback = iscsit_send_good_status_done;
29087978SPeter.Dunlap@Sun.COM 	pdu->isp_magic = IDM_PDU_MAGIC;
29097978SPeter.Dunlap@Sun.COM 	pdu->isp_hdr = (iscsi_hdr_t *)(pdu + 1); /* Ptr arithmetic */
29107978SPeter.Dunlap@Sun.COM 	pdu->isp_hdrlen = sizeof (iscsi_hdr_t);
29117978SPeter.Dunlap@Sun.COM 
29127978SPeter.Dunlap@Sun.COM 	/* Setup status response */
29137978SPeter.Dunlap@Sun.COM 	rsp = (iscsi_scsi_rsp_hdr_t *)pdu->isp_hdr;
29147978SPeter.Dunlap@Sun.COM 	bzero(rsp, sizeof (*rsp));
29157978SPeter.Dunlap@Sun.COM 	rsp->opcode = ISCSI_OP_SCSI_RSP;
29167978SPeter.Dunlap@Sun.COM 	rsp->flags = ISCSI_FLAG_FINAL;
29177978SPeter.Dunlap@Sun.COM 	rsp->response = ISCSI_STATUS_CMD_COMPLETED;
29187978SPeter.Dunlap@Sun.COM 
29197978SPeter.Dunlap@Sun.COM 	return (0);
29207978SPeter.Dunlap@Sun.COM }
29217978SPeter.Dunlap@Sun.COM 
29227978SPeter.Dunlap@Sun.COM /*
29237978SPeter.Dunlap@Sun.COM  * iscsit private data handler
29247978SPeter.Dunlap@Sun.COM  */
29257978SPeter.Dunlap@Sun.COM 
29267978SPeter.Dunlap@Sun.COM /*ARGSUSED*/
29277978SPeter.Dunlap@Sun.COM static void
iscsit_pp_cb(struct stmf_port_provider * pp,int cmd,void * arg,uint32_t flags)29287978SPeter.Dunlap@Sun.COM iscsit_pp_cb(struct stmf_port_provider *pp, int cmd, void *arg, uint32_t flags)
29297978SPeter.Dunlap@Sun.COM {
29307978SPeter.Dunlap@Sun.COM 	it_config_t		*cfg;
29317978SPeter.Dunlap@Sun.COM 	nvlist_t		*nvl;
293211431SPeter.Cudhea@Sun.COM 	iscsit_service_enabled_t	old_state;
29337978SPeter.Dunlap@Sun.COM 
29347978SPeter.Dunlap@Sun.COM 	if ((cmd != STMF_PROVIDER_DATA_UPDATED) || (arg == NULL)) {
29357978SPeter.Dunlap@Sun.COM 		return;
29367978SPeter.Dunlap@Sun.COM 	}
29377978SPeter.Dunlap@Sun.COM 
29387978SPeter.Dunlap@Sun.COM 	nvl = (nvlist_t *)arg;
29397978SPeter.Dunlap@Sun.COM 
29407978SPeter.Dunlap@Sun.COM 	/* Translate nvlist */
29417978SPeter.Dunlap@Sun.COM 	if (it_nv_to_config(nvl, &cfg) != 0) {
29427978SPeter.Dunlap@Sun.COM 		cmn_err(CE_WARN, "Configuration is invalid");
29437978SPeter.Dunlap@Sun.COM 		return;
29447978SPeter.Dunlap@Sun.COM 	}
29457978SPeter.Dunlap@Sun.COM 
294611431SPeter.Cudhea@Sun.COM 	/* Check that no iSCSI ioctl is currently running */
294711431SPeter.Cudhea@Sun.COM 	mutex_enter(&iscsit_global.global_state_mutex);
294811431SPeter.Cudhea@Sun.COM 	old_state = iscsit_global.global_svc_state;
294911431SPeter.Cudhea@Sun.COM 	switch (iscsit_global.global_svc_state) {
295011431SPeter.Cudhea@Sun.COM 	case ISE_ENABLED:
295111431SPeter.Cudhea@Sun.COM 	case ISE_DISABLED:
295211431SPeter.Cudhea@Sun.COM 		iscsit_global.global_svc_state = ISE_BUSY;
295311431SPeter.Cudhea@Sun.COM 		break;
295411431SPeter.Cudhea@Sun.COM 	case ISE_ENABLING:
295511431SPeter.Cudhea@Sun.COM 		/*
295611431SPeter.Cudhea@Sun.COM 		 * It is OK for the iscsit_pp_cb to be called from inside of
295711431SPeter.Cudhea@Sun.COM 		 * an iSCSI ioctl only if we are currently executing inside
295811431SPeter.Cudhea@Sun.COM 		 * of stmf_register_port_provider.
295911431SPeter.Cudhea@Sun.COM 		 */
296011431SPeter.Cudhea@Sun.COM 		ASSERT((flags & STMF_PCB_PREG_COMPLETE) != 0);
296111431SPeter.Cudhea@Sun.COM 		break;
296211431SPeter.Cudhea@Sun.COM 	default:
296311431SPeter.Cudhea@Sun.COM 		cmn_err(CE_WARN, "iscsit_pp_cb called when global_svc_state"
296411431SPeter.Cudhea@Sun.COM 		    " is not ENABLED(0x%x) -- ignoring",
296511431SPeter.Cudhea@Sun.COM 		    iscsit_global.global_svc_state);
296611431SPeter.Cudhea@Sun.COM 		mutex_exit(&iscsit_global.global_state_mutex);
296711431SPeter.Cudhea@Sun.COM 		it_config_free_cmn(cfg);
296811431SPeter.Cudhea@Sun.COM 		return;
296911431SPeter.Cudhea@Sun.COM 	}
297011431SPeter.Cudhea@Sun.COM 	mutex_exit(&iscsit_global.global_state_mutex);
297111431SPeter.Cudhea@Sun.COM 
29727978SPeter.Dunlap@Sun.COM 	/* Update config */
29737978SPeter.Dunlap@Sun.COM 	(void) iscsit_config_merge(cfg);
29747978SPeter.Dunlap@Sun.COM 
29757978SPeter.Dunlap@Sun.COM 	it_config_free_cmn(cfg);
297611431SPeter.Cudhea@Sun.COM 
297711431SPeter.Cudhea@Sun.COM 	/* Restore old iSCSI driver global state */
297811431SPeter.Cudhea@Sun.COM 	mutex_enter(&iscsit_global.global_state_mutex);
297911431SPeter.Cudhea@Sun.COM 	ASSERT(iscsit_global.global_svc_state == ISE_BUSY ||
298011431SPeter.Cudhea@Sun.COM 	    iscsit_global.global_svc_state == ISE_ENABLING);
298111431SPeter.Cudhea@Sun.COM 	iscsit_global.global_svc_state = old_state;
298211431SPeter.Cudhea@Sun.COM 	mutex_exit(&iscsit_global.global_state_mutex);
29837978SPeter.Dunlap@Sun.COM }
29847978SPeter.Dunlap@Sun.COM 
29857978SPeter.Dunlap@Sun.COM 
29867978SPeter.Dunlap@Sun.COM static it_cfg_status_t
iscsit_config_merge(it_config_t * in_cfg)29877978SPeter.Dunlap@Sun.COM iscsit_config_merge(it_config_t *in_cfg)
29887978SPeter.Dunlap@Sun.COM {
29897978SPeter.Dunlap@Sun.COM 	it_cfg_status_t	status;
29907978SPeter.Dunlap@Sun.COM 	it_config_t	*cfg;
29917978SPeter.Dunlap@Sun.COM 	it_config_t	tmp_cfg;
29927978SPeter.Dunlap@Sun.COM 	list_t		tpg_del_list;
29937978SPeter.Dunlap@Sun.COM 
29947978SPeter.Dunlap@Sun.COM 	if (in_cfg) {
29957978SPeter.Dunlap@Sun.COM 		cfg = in_cfg;
29967978SPeter.Dunlap@Sun.COM 	} else {
29977978SPeter.Dunlap@Sun.COM 		/* Make empty config */
29987978SPeter.Dunlap@Sun.COM 		bzero(&tmp_cfg, sizeof (tmp_cfg));
29997978SPeter.Dunlap@Sun.COM 		cfg = &tmp_cfg;
30007978SPeter.Dunlap@Sun.COM 	}
30017978SPeter.Dunlap@Sun.COM 
30027978SPeter.Dunlap@Sun.COM 	list_create(&tpg_del_list,  sizeof (iscsit_tpg_t),
30037978SPeter.Dunlap@Sun.COM 	    offsetof(iscsit_tpg_t, tpg_delete_ln));
30047978SPeter.Dunlap@Sun.COM 
30057978SPeter.Dunlap@Sun.COM 	/*
30067978SPeter.Dunlap@Sun.COM 	 * Update targets, initiator contexts, target portal groups,
30077978SPeter.Dunlap@Sun.COM 	 * and iSNS client
30087978SPeter.Dunlap@Sun.COM 	 */
30097978SPeter.Dunlap@Sun.COM 	ISCSIT_GLOBAL_LOCK(RW_WRITER);
30107978SPeter.Dunlap@Sun.COM 	if (((status = iscsit_config_merge_tpg(cfg, &tpg_del_list))
30117978SPeter.Dunlap@Sun.COM 	    != 0) ||
30127978SPeter.Dunlap@Sun.COM 	    ((status = iscsit_config_merge_tgt(cfg)) != 0) ||
30137978SPeter.Dunlap@Sun.COM 	    ((status = iscsit_config_merge_ini(cfg)) != 0) ||
30147978SPeter.Dunlap@Sun.COM 	    ((status = isnst_config_merge(cfg)) != 0)) {
30157978SPeter.Dunlap@Sun.COM 		ISCSIT_GLOBAL_UNLOCK();
30167978SPeter.Dunlap@Sun.COM 		return (status);
30177978SPeter.Dunlap@Sun.COM 	}
30187978SPeter.Dunlap@Sun.COM 
30197978SPeter.Dunlap@Sun.COM 	/* Update other global config parameters */
30207978SPeter.Dunlap@Sun.COM 	if (iscsit_global.global_props) {
30217978SPeter.Dunlap@Sun.COM 		nvlist_free(iscsit_global.global_props);
30227978SPeter.Dunlap@Sun.COM 		iscsit_global.global_props = NULL;
30237978SPeter.Dunlap@Sun.COM 	}
30247978SPeter.Dunlap@Sun.COM 	if (in_cfg) {
30257978SPeter.Dunlap@Sun.COM 		(void) nvlist_dup(cfg->config_global_properties,
30267978SPeter.Dunlap@Sun.COM 		    &iscsit_global.global_props, KM_SLEEP);
30277978SPeter.Dunlap@Sun.COM 	}
30287978SPeter.Dunlap@Sun.COM 	ISCSIT_GLOBAL_UNLOCK();
30297978SPeter.Dunlap@Sun.COM 
30307978SPeter.Dunlap@Sun.COM 	iscsit_config_destroy_tpgs(&tpg_del_list);
30317978SPeter.Dunlap@Sun.COM 
30327978SPeter.Dunlap@Sun.COM 	list_destroy(&tpg_del_list);
30337978SPeter.Dunlap@Sun.COM 
30347978SPeter.Dunlap@Sun.COM 	return (ITCFG_SUCCESS);
30357978SPeter.Dunlap@Sun.COM }
30367978SPeter.Dunlap@Sun.COM 
30377978SPeter.Dunlap@Sun.COM /*
30387978SPeter.Dunlap@Sun.COM  * iscsit_sna_lt[e]
30397978SPeter.Dunlap@Sun.COM  *
30407978SPeter.Dunlap@Sun.COM  * Compare serial numbers using serial number arithmetic as defined in
30417978SPeter.Dunlap@Sun.COM  * RFC 1982.
30427978SPeter.Dunlap@Sun.COM  *
304311876SJames.Dunham@Sun.COM  * NOTE: This code is duplicated in the isns server. It ought to be common.
30447978SPeter.Dunlap@Sun.COM  */
30457978SPeter.Dunlap@Sun.COM 
30467978SPeter.Dunlap@Sun.COM static int
iscsit_sna_lt(uint32_t sn1,uint32_t sn2)30477978SPeter.Dunlap@Sun.COM iscsit_sna_lt(uint32_t sn1, uint32_t sn2)
30487978SPeter.Dunlap@Sun.COM {
30497978SPeter.Dunlap@Sun.COM 	return ((sn1 != sn2) &&
30507978SPeter.Dunlap@Sun.COM 	    (((sn1 < sn2) && ((sn2 - sn1) < ISCSIT_SNA32_CHECK)) ||
30517978SPeter.Dunlap@Sun.COM 	    ((sn1 > sn2) && ((sn1 - sn2) > ISCSIT_SNA32_CHECK))));
30527978SPeter.Dunlap@Sun.COM }
30537978SPeter.Dunlap@Sun.COM 
30547978SPeter.Dunlap@Sun.COM static int
iscsit_sna_lte(uint32_t sn1,uint32_t sn2)30557978SPeter.Dunlap@Sun.COM iscsit_sna_lte(uint32_t sn1, uint32_t sn2)
30567978SPeter.Dunlap@Sun.COM {
30577978SPeter.Dunlap@Sun.COM 	return ((sn1 == sn2) ||
30587978SPeter.Dunlap@Sun.COM 	    (((sn1 < sn2) && ((sn2 - sn1) < ISCSIT_SNA32_CHECK)) ||
30597978SPeter.Dunlap@Sun.COM 	    ((sn1 > sn2) && ((sn1 - sn2) > ISCSIT_SNA32_CHECK))));
30607978SPeter.Dunlap@Sun.COM }
30617978SPeter.Dunlap@Sun.COM 
30627978SPeter.Dunlap@Sun.COM 
30637978SPeter.Dunlap@Sun.COM static boolean_t
iscsit_cmdsn_in_window(iscsit_conn_t * ict,uint32_t cmdsn)30647978SPeter.Dunlap@Sun.COM iscsit_cmdsn_in_window(iscsit_conn_t *ict, uint32_t cmdsn)
30657978SPeter.Dunlap@Sun.COM {
30667978SPeter.Dunlap@Sun.COM 	iscsit_sess_t	*ist = ict->ict_sess;
30677978SPeter.Dunlap@Sun.COM 	int		rval = B_TRUE;
30687978SPeter.Dunlap@Sun.COM 
30697978SPeter.Dunlap@Sun.COM 	ist = ict->ict_sess;
30707978SPeter.Dunlap@Sun.COM 
307112372SPriya.Krishnan@Sun.COM 	mutex_enter(&ist->ist_sn_mutex);
30727978SPeter.Dunlap@Sun.COM 
30737978SPeter.Dunlap@Sun.COM 	/*
30747978SPeter.Dunlap@Sun.COM 	 * If cmdsn is less than ist_expcmdsn - iscsit_cmd_window() or
30757978SPeter.Dunlap@Sun.COM 	 * greater than ist_expcmdsn, it's not in the window.
30767978SPeter.Dunlap@Sun.COM 	 */
30777978SPeter.Dunlap@Sun.COM 
30787978SPeter.Dunlap@Sun.COM 	if (iscsit_sna_lt(cmdsn, (ist->ist_expcmdsn - iscsit_cmd_window())) ||
30797978SPeter.Dunlap@Sun.COM 	    !iscsit_sna_lte(cmdsn, ist->ist_expcmdsn)) {
30807978SPeter.Dunlap@Sun.COM 		rval = B_FALSE;
30817978SPeter.Dunlap@Sun.COM 	}
30827978SPeter.Dunlap@Sun.COM 
308312372SPriya.Krishnan@Sun.COM 	mutex_exit(&ist->ist_sn_mutex);
30847978SPeter.Dunlap@Sun.COM 
30857978SPeter.Dunlap@Sun.COM 	return (rval);
30867978SPeter.Dunlap@Sun.COM }
308712372SPriya.Krishnan@Sun.COM 
308812372SPriya.Krishnan@Sun.COM /*
308912372SPriya.Krishnan@Sun.COM  * iscsit_check_cmdsn_and_queue
309012372SPriya.Krishnan@Sun.COM  *
309112372SPriya.Krishnan@Sun.COM  * Independent of the order in which the iSCSI target receives non-immediate
309212372SPriya.Krishnan@Sun.COM  * command PDU across the entire session and any multiple connections within
309312372SPriya.Krishnan@Sun.COM  * the session, the target must deliver the commands to the SCSI layer in
309412372SPriya.Krishnan@Sun.COM  * CmdSN order. So out-of-order non-immediate commands are queued up on a
309512372SPriya.Krishnan@Sun.COM  * session-wide wait queue. Duplicate commands are ignored.
309612372SPriya.Krishnan@Sun.COM  *
309712372SPriya.Krishnan@Sun.COM  */
309812372SPriya.Krishnan@Sun.COM static int
iscsit_check_cmdsn_and_queue(idm_pdu_t * rx_pdu)309912372SPriya.Krishnan@Sun.COM iscsit_check_cmdsn_and_queue(idm_pdu_t *rx_pdu)
310012372SPriya.Krishnan@Sun.COM {
310112372SPriya.Krishnan@Sun.COM 	idm_conn_t		*ic = rx_pdu->isp_ic;
310212372SPriya.Krishnan@Sun.COM 	iscsit_conn_t		*ict = ic->ic_handle;
310312372SPriya.Krishnan@Sun.COM 	iscsit_sess_t		*ist = ict->ict_sess;
310412372SPriya.Krishnan@Sun.COM 	iscsi_scsi_cmd_hdr_t	*hdr = (iscsi_scsi_cmd_hdr_t *)rx_pdu->isp_hdr;
310512372SPriya.Krishnan@Sun.COM 
310612372SPriya.Krishnan@Sun.COM 	mutex_enter(&ist->ist_sn_mutex);
310712372SPriya.Krishnan@Sun.COM 	if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
310812372SPriya.Krishnan@Sun.COM 		/* do not queue, handle it immediately */
310912372SPriya.Krishnan@Sun.COM 		DTRACE_PROBE2(immediate__cmd, iscsit_sess_t *, ist,
311012372SPriya.Krishnan@Sun.COM 		    idm_pdu_t *, rx_pdu);
311112372SPriya.Krishnan@Sun.COM 		mutex_exit(&ist->ist_sn_mutex);
311212372SPriya.Krishnan@Sun.COM 		return (ISCSIT_CMDSN_EQ_EXPCMDSN);
311312372SPriya.Krishnan@Sun.COM 	}
311412372SPriya.Krishnan@Sun.COM 	if (iscsit_sna_lt(ist->ist_expcmdsn, ntohl(hdr->cmdsn))) {
311512372SPriya.Krishnan@Sun.COM 		/*
311612372SPriya.Krishnan@Sun.COM 		 * Out-of-order commands (cmdSN higher than ExpCmdSN)
311712372SPriya.Krishnan@Sun.COM 		 * are staged on a fixed-size circular buffer until
311812372SPriya.Krishnan@Sun.COM 		 * the missing command is delivered to the SCSI layer.
311912372SPriya.Krishnan@Sun.COM 		 * Irrespective of the order of insertion into the
312012372SPriya.Krishnan@Sun.COM 		 * staging queue, the commands are processed out of the
312112372SPriya.Krishnan@Sun.COM 		 * queue in cmdSN order only.
312212372SPriya.Krishnan@Sun.COM 		 */
312312372SPriya.Krishnan@Sun.COM 		rx_pdu->isp_queue_time = ddi_get_time();
312412372SPriya.Krishnan@Sun.COM 		iscsit_add_pdu_to_queue(ist, rx_pdu);
312512372SPriya.Krishnan@Sun.COM 		mutex_exit(&ist->ist_sn_mutex);
312612372SPriya.Krishnan@Sun.COM 		return (ISCSIT_CMDSN_GT_EXPCMDSN);
312712372SPriya.Krishnan@Sun.COM 	} else if (iscsit_sna_lt(ntohl(hdr->cmdsn), ist->ist_expcmdsn)) {
312812372SPriya.Krishnan@Sun.COM 		DTRACE_PROBE3(cmdsn__lt__expcmdsn, iscsit_sess_t *, ist,
312912372SPriya.Krishnan@Sun.COM 		    iscsit_conn_t *, ict, idm_pdu_t *, rx_pdu);
313012372SPriya.Krishnan@Sun.COM 		mutex_exit(&ist->ist_sn_mutex);
313112372SPriya.Krishnan@Sun.COM 		return (ISCSIT_CMDSN_LT_EXPCMDSN);
313212372SPriya.Krishnan@Sun.COM 	} else {
313312372SPriya.Krishnan@Sun.COM 		mutex_exit(&ist->ist_sn_mutex);
313412372SPriya.Krishnan@Sun.COM 		return (ISCSIT_CMDSN_EQ_EXPCMDSN);
313512372SPriya.Krishnan@Sun.COM 	}
313612372SPriya.Krishnan@Sun.COM }
313712372SPriya.Krishnan@Sun.COM 
313812372SPriya.Krishnan@Sun.COM /*
313912372SPriya.Krishnan@Sun.COM  * iscsit_add_pdu_to_queue() adds PDUs into the array indexed by
314012372SPriya.Krishnan@Sun.COM  * their cmdsn value. The length of the array is kept above the
314112372SPriya.Krishnan@Sun.COM  * maximum window size. The window keeps the cmdsn within a range
314212372SPriya.Krishnan@Sun.COM  * such that there are no collisons. e.g. the assumption is that
314312372SPriya.Krishnan@Sun.COM  * the windowing checks make it impossible to receive PDUs that
314412372SPriya.Krishnan@Sun.COM  * index into the same location in the array.
314512372SPriya.Krishnan@Sun.COM  */
314612372SPriya.Krishnan@Sun.COM static void
iscsit_add_pdu_to_queue(iscsit_sess_t * ist,idm_pdu_t * rx_pdu)314712372SPriya.Krishnan@Sun.COM iscsit_add_pdu_to_queue(iscsit_sess_t *ist, idm_pdu_t *rx_pdu)
314812372SPriya.Krishnan@Sun.COM {
314912372SPriya.Krishnan@Sun.COM 	iscsit_cbuf_t	*cbuf	= ist->ist_rxpdu_queue;
315012372SPriya.Krishnan@Sun.COM 	iscsit_conn_t	*ict 	= rx_pdu->isp_ic->ic_handle;
315112372SPriya.Krishnan@Sun.COM 	uint32_t	cmdsn	=
315212372SPriya.Krishnan@Sun.COM 	    ((iscsi_scsi_cmd_hdr_t *)rx_pdu->isp_hdr)->cmdsn;
315312372SPriya.Krishnan@Sun.COM 	uint32_t	index;
315412372SPriya.Krishnan@Sun.COM 
315512372SPriya.Krishnan@Sun.COM 	ASSERT(MUTEX_HELD(&ist->ist_sn_mutex));
315612372SPriya.Krishnan@Sun.COM 	/*
315712372SPriya.Krishnan@Sun.COM 	 * If the connection is being torn down, then
315812372SPriya.Krishnan@Sun.COM 	 * don't add the PDU to the staging queue
315912372SPriya.Krishnan@Sun.COM 	 */
316012372SPriya.Krishnan@Sun.COM 	mutex_enter(&ict->ict_mutex);
316112372SPriya.Krishnan@Sun.COM 	if (ict->ict_lost) {
316212372SPriya.Krishnan@Sun.COM 		mutex_exit(&ict->ict_mutex);
316312372SPriya.Krishnan@Sun.COM 		idm_pdu_complete(rx_pdu, IDM_STATUS_FAIL);
316412372SPriya.Krishnan@Sun.COM 		return;
316512372SPriya.Krishnan@Sun.COM 	}
316612372SPriya.Krishnan@Sun.COM 	iscsit_conn_dispatch_hold(ict);
316712372SPriya.Krishnan@Sun.COM 	mutex_exit(&ict->ict_mutex);
316812372SPriya.Krishnan@Sun.COM 
316912372SPriya.Krishnan@Sun.COM 	index = ntohl(cmdsn) % ISCSIT_RXPDU_QUEUE_LEN;
317012372SPriya.Krishnan@Sun.COM 	ASSERT(cbuf->cb_buffer[index] == NULL);
317112372SPriya.Krishnan@Sun.COM 	cbuf->cb_buffer[index] = rx_pdu;
317212372SPriya.Krishnan@Sun.COM 	cbuf->cb_num_elems++;
317312372SPriya.Krishnan@Sun.COM }
317412372SPriya.Krishnan@Sun.COM 
317512372SPriya.Krishnan@Sun.COM static idm_pdu_t *
iscsit_remove_pdu_from_queue(iscsit_sess_t * ist,uint32_t cmdsn)317612372SPriya.Krishnan@Sun.COM iscsit_remove_pdu_from_queue(iscsit_sess_t *ist, uint32_t cmdsn)
317712372SPriya.Krishnan@Sun.COM {
317812372SPriya.Krishnan@Sun.COM 	iscsit_cbuf_t	*cbuf	= ist->ist_rxpdu_queue;
317912372SPriya.Krishnan@Sun.COM 	idm_pdu_t	*pdu	= NULL;
318012372SPriya.Krishnan@Sun.COM 	uint32_t	index;
318112372SPriya.Krishnan@Sun.COM 
318212372SPriya.Krishnan@Sun.COM 	ASSERT(MUTEX_HELD(&ist->ist_sn_mutex));
318312372SPriya.Krishnan@Sun.COM 	index = cmdsn % ISCSIT_RXPDU_QUEUE_LEN;
318412372SPriya.Krishnan@Sun.COM 	if ((pdu = cbuf->cb_buffer[index]) != NULL) {
318512372SPriya.Krishnan@Sun.COM 		ASSERT(cmdsn ==
318612372SPriya.Krishnan@Sun.COM 		    ntohl(((iscsi_scsi_cmd_hdr_t *)pdu->isp_hdr)->cmdsn));
318712372SPriya.Krishnan@Sun.COM 		cbuf->cb_buffer[index] = NULL;
318812372SPriya.Krishnan@Sun.COM 		cbuf->cb_num_elems--;
318912372SPriya.Krishnan@Sun.COM 		return (pdu);
319012372SPriya.Krishnan@Sun.COM 	}
319112372SPriya.Krishnan@Sun.COM 	return (NULL);
319212372SPriya.Krishnan@Sun.COM }
319312372SPriya.Krishnan@Sun.COM 
319412372SPriya.Krishnan@Sun.COM /*
319512372SPriya.Krishnan@Sun.COM  * iscsit_process_pdu_in_queue() finds the next pdu in sequence
319612372SPriya.Krishnan@Sun.COM  * and posts it to the SCSI layer
319712372SPriya.Krishnan@Sun.COM  */
319812372SPriya.Krishnan@Sun.COM static void
iscsit_process_pdu_in_queue(iscsit_sess_t * ist)319912372SPriya.Krishnan@Sun.COM iscsit_process_pdu_in_queue(iscsit_sess_t *ist)
320012372SPriya.Krishnan@Sun.COM {
320112372SPriya.Krishnan@Sun.COM 	iscsit_cbuf_t	*cbuf	= ist->ist_rxpdu_queue;
320212372SPriya.Krishnan@Sun.COM 	idm_pdu_t	*pdu = NULL;
320312372SPriya.Krishnan@Sun.COM 	uint32_t	expcmdsn;
320412372SPriya.Krishnan@Sun.COM 
320512372SPriya.Krishnan@Sun.COM 	for (;;) {
320612372SPriya.Krishnan@Sun.COM 		mutex_enter(&ist->ist_sn_mutex);
320712372SPriya.Krishnan@Sun.COM 		if (cbuf->cb_num_elems == 0) {
320812372SPriya.Krishnan@Sun.COM 			mutex_exit(&ist->ist_sn_mutex);
320912372SPriya.Krishnan@Sun.COM 			break;
321012372SPriya.Krishnan@Sun.COM 		}
321112372SPriya.Krishnan@Sun.COM 		expcmdsn = ist->ist_expcmdsn;
321212372SPriya.Krishnan@Sun.COM 		if ((pdu = iscsit_remove_pdu_from_queue(ist, expcmdsn))
321312372SPriya.Krishnan@Sun.COM 		    == NULL) {
321412372SPriya.Krishnan@Sun.COM 			mutex_exit(&ist->ist_sn_mutex);
321512372SPriya.Krishnan@Sun.COM 			break;
321612372SPriya.Krishnan@Sun.COM 		}
321712372SPriya.Krishnan@Sun.COM 		mutex_exit(&ist->ist_sn_mutex);
321812372SPriya.Krishnan@Sun.COM 		iscsit_post_staged_pdu(pdu);
321912372SPriya.Krishnan@Sun.COM 	}
322012372SPriya.Krishnan@Sun.COM }
322112372SPriya.Krishnan@Sun.COM 
322212372SPriya.Krishnan@Sun.COM static void
iscsit_post_staged_pdu(idm_pdu_t * rx_pdu)322312372SPriya.Krishnan@Sun.COM iscsit_post_staged_pdu(idm_pdu_t *rx_pdu)
322412372SPriya.Krishnan@Sun.COM {
322512372SPriya.Krishnan@Sun.COM 	iscsit_conn_t	*ict	= rx_pdu->isp_ic->ic_handle;
322612372SPriya.Krishnan@Sun.COM 
322712372SPriya.Krishnan@Sun.COM 	/* Post the PDU to the SCSI layer */
322812372SPriya.Krishnan@Sun.COM 	switch (IDM_PDU_OPCODE(rx_pdu)) {
322912372SPriya.Krishnan@Sun.COM 	case ISCSI_OP_NOOP_OUT:
323012372SPriya.Krishnan@Sun.COM 		iscsit_set_cmdsn(ict, rx_pdu);
323112372SPriya.Krishnan@Sun.COM 		iscsit_pdu_op_noop(ict, rx_pdu);
323212372SPriya.Krishnan@Sun.COM 		break;
323312372SPriya.Krishnan@Sun.COM 	case ISCSI_OP_TEXT_CMD:
323412372SPriya.Krishnan@Sun.COM 		iscsit_set_cmdsn(ict, rx_pdu);
323512372SPriya.Krishnan@Sun.COM 		iscsit_pdu_op_text_cmd(ict, rx_pdu);
323612372SPriya.Krishnan@Sun.COM 		break;
323712372SPriya.Krishnan@Sun.COM 	case ISCSI_OP_SCSI_TASK_MGT_MSG:
323812372SPriya.Krishnan@Sun.COM 		iscsit_set_cmdsn(ict, rx_pdu);
323912372SPriya.Krishnan@Sun.COM 		iscsit_op_scsi_task_mgmt(ict, rx_pdu);
324012372SPriya.Krishnan@Sun.COM 		break;
324112372SPriya.Krishnan@Sun.COM 	case ISCSI_OP_SCSI_CMD:
324212372SPriya.Krishnan@Sun.COM 		/* cmdSN will be incremented after creating itask */
324312372SPriya.Krishnan@Sun.COM 		iscsit_post_scsi_cmd(rx_pdu->isp_ic, rx_pdu);
324412372SPriya.Krishnan@Sun.COM 		break;
324512372SPriya.Krishnan@Sun.COM 	case ISCSI_OP_LOGOUT_CMD:
324612372SPriya.Krishnan@Sun.COM 		iscsit_set_cmdsn(ict, rx_pdu);
324712372SPriya.Krishnan@Sun.COM 		iscsit_pdu_op_logout_cmd(ict, rx_pdu);
324812372SPriya.Krishnan@Sun.COM 		break;
324912372SPriya.Krishnan@Sun.COM 	default:
325012372SPriya.Krishnan@Sun.COM 		/* No other PDUs should be placed on the queue */
325112372SPriya.Krishnan@Sun.COM 		ASSERT(0);
325212372SPriya.Krishnan@Sun.COM 	}
325312372SPriya.Krishnan@Sun.COM 	iscsit_conn_dispatch_rele(ict); /* release hold on the conn */
325412372SPriya.Krishnan@Sun.COM }
325512372SPriya.Krishnan@Sun.COM 
325612372SPriya.Krishnan@Sun.COM /* ARGSUSED */
325712372SPriya.Krishnan@Sun.COM void
iscsit_rxpdu_queue_monitor_start(void)325812372SPriya.Krishnan@Sun.COM iscsit_rxpdu_queue_monitor_start(void)
325912372SPriya.Krishnan@Sun.COM {
326012372SPriya.Krishnan@Sun.COM 	mutex_enter(&iscsit_rxpdu_queue_monitor_mutex);
326112372SPriya.Krishnan@Sun.COM 	if (iscsit_rxpdu_queue_monitor_thr_running) {
326212372SPriya.Krishnan@Sun.COM 		mutex_exit(&iscsit_rxpdu_queue_monitor_mutex);
326312372SPriya.Krishnan@Sun.COM 		return;
326412372SPriya.Krishnan@Sun.COM 	}
326512372SPriya.Krishnan@Sun.COM 	iscsit_rxpdu_queue_monitor_thr_id =
326612372SPriya.Krishnan@Sun.COM 	    thread_create(NULL, 0, iscsit_rxpdu_queue_monitor, NULL,
326712372SPriya.Krishnan@Sun.COM 	    0, &p0, TS_RUN, minclsyspri);
326812372SPriya.Krishnan@Sun.COM 	while (!iscsit_rxpdu_queue_monitor_thr_running) {
326912372SPriya.Krishnan@Sun.COM 		cv_wait(&iscsit_rxpdu_queue_monitor_cv,
327012372SPriya.Krishnan@Sun.COM 		    &iscsit_rxpdu_queue_monitor_mutex);
327112372SPriya.Krishnan@Sun.COM 	}
327212372SPriya.Krishnan@Sun.COM 	mutex_exit(&iscsit_rxpdu_queue_monitor_mutex);
327312372SPriya.Krishnan@Sun.COM 
327412372SPriya.Krishnan@Sun.COM }
327512372SPriya.Krishnan@Sun.COM 
327612372SPriya.Krishnan@Sun.COM /* ARGSUSED */
327712372SPriya.Krishnan@Sun.COM void
iscsit_rxpdu_queue_monitor_stop(void)327812372SPriya.Krishnan@Sun.COM iscsit_rxpdu_queue_monitor_stop(void)
327912372SPriya.Krishnan@Sun.COM {
328012372SPriya.Krishnan@Sun.COM 	mutex_enter(&iscsit_rxpdu_queue_monitor_mutex);
328112372SPriya.Krishnan@Sun.COM 	if (iscsit_rxpdu_queue_monitor_thr_running) {
328212372SPriya.Krishnan@Sun.COM 		iscsit_rxpdu_queue_monitor_thr_running = B_FALSE;
328312372SPriya.Krishnan@Sun.COM 		cv_signal(&iscsit_rxpdu_queue_monitor_cv);
328412372SPriya.Krishnan@Sun.COM 		mutex_exit(&iscsit_rxpdu_queue_monitor_mutex);
328512372SPriya.Krishnan@Sun.COM 
328612372SPriya.Krishnan@Sun.COM 		thread_join(iscsit_rxpdu_queue_monitor_thr_did);
328712372SPriya.Krishnan@Sun.COM 		return;
328812372SPriya.Krishnan@Sun.COM 	}
328912372SPriya.Krishnan@Sun.COM 	mutex_exit(&iscsit_rxpdu_queue_monitor_mutex);
329012372SPriya.Krishnan@Sun.COM }
329112372SPriya.Krishnan@Sun.COM 
329212372SPriya.Krishnan@Sun.COM /*
329312372SPriya.Krishnan@Sun.COM  * A separate thread is used to scan the staging queue on all the
329412372SPriya.Krishnan@Sun.COM  * sessions, If a delayed PDU does not arrive within a timeout, the
329512372SPriya.Krishnan@Sun.COM  * target will advance to the staged PDU that is next in sequence
329612372SPriya.Krishnan@Sun.COM  * and exceeded the threshold wait time. It is up to the initiator
329712372SPriya.Krishnan@Sun.COM  * to note that the target has not acknowledged a particular cmdsn
329812372SPriya.Krishnan@Sun.COM  * and take appropriate action.
329912372SPriya.Krishnan@Sun.COM  */
330012372SPriya.Krishnan@Sun.COM /* ARGSUSED */
330112372SPriya.Krishnan@Sun.COM static void
iscsit_rxpdu_queue_monitor(void * arg)330212372SPriya.Krishnan@Sun.COM iscsit_rxpdu_queue_monitor(void *arg)
330312372SPriya.Krishnan@Sun.COM {
330412372SPriya.Krishnan@Sun.COM 	iscsit_tgt_t	*tgt;
330512372SPriya.Krishnan@Sun.COM 	iscsit_sess_t	*ist;
330612372SPriya.Krishnan@Sun.COM 
330712372SPriya.Krishnan@Sun.COM 	mutex_enter(&iscsit_rxpdu_queue_monitor_mutex);
330812372SPriya.Krishnan@Sun.COM 	iscsit_rxpdu_queue_monitor_thr_did = curthread->t_did;
330912372SPriya.Krishnan@Sun.COM 	iscsit_rxpdu_queue_monitor_thr_running = B_TRUE;
331012372SPriya.Krishnan@Sun.COM 	cv_signal(&iscsit_rxpdu_queue_monitor_cv);
331112372SPriya.Krishnan@Sun.COM 
331212372SPriya.Krishnan@Sun.COM 	while (iscsit_rxpdu_queue_monitor_thr_running) {
331312372SPriya.Krishnan@Sun.COM 		ISCSIT_GLOBAL_LOCK(RW_READER);
331412372SPriya.Krishnan@Sun.COM 		for (tgt = avl_first(&iscsit_global.global_target_list);
331512372SPriya.Krishnan@Sun.COM 		    tgt != NULL;
331612372SPriya.Krishnan@Sun.COM 		    tgt = AVL_NEXT(&iscsit_global.global_target_list, tgt)) {
331712372SPriya.Krishnan@Sun.COM 			mutex_enter(&tgt->target_mutex);
331812372SPriya.Krishnan@Sun.COM 			for (ist = avl_first(&tgt->target_sess_list);
331912372SPriya.Krishnan@Sun.COM 			    ist != NULL;
332012372SPriya.Krishnan@Sun.COM 			    ist = AVL_NEXT(&tgt->target_sess_list, ist)) {
332112372SPriya.Krishnan@Sun.COM 
332212372SPriya.Krishnan@Sun.COM 				iscsit_rxpdu_queue_monitor_session(ist);
332312372SPriya.Krishnan@Sun.COM 			}
332412372SPriya.Krishnan@Sun.COM 			mutex_exit(&tgt->target_mutex);
332512372SPriya.Krishnan@Sun.COM 		}
332612372SPriya.Krishnan@Sun.COM 		ISCSIT_GLOBAL_UNLOCK();
332712372SPriya.Krishnan@Sun.COM 		if (iscsit_rxpdu_queue_monitor_thr_running == B_FALSE) {
332812372SPriya.Krishnan@Sun.COM 			break;
332912372SPriya.Krishnan@Sun.COM 		}
333012372SPriya.Krishnan@Sun.COM 		(void) cv_reltimedwait(&iscsit_rxpdu_queue_monitor_cv,
333112372SPriya.Krishnan@Sun.COM 		    &iscsit_rxpdu_queue_monitor_mutex,
333212372SPriya.Krishnan@Sun.COM 		    ISCSIT_RXPDU_QUEUE_MONITOR_INTERVAL * drv_usectohz(1000000),
333312372SPriya.Krishnan@Sun.COM 		    TR_CLOCK_TICK);
333412372SPriya.Krishnan@Sun.COM 	}
333512372SPriya.Krishnan@Sun.COM 	mutex_exit(&iscsit_rxpdu_queue_monitor_mutex);
333612372SPriya.Krishnan@Sun.COM 	thread_exit();
333712372SPriya.Krishnan@Sun.COM }
333812372SPriya.Krishnan@Sun.COM 
333912372SPriya.Krishnan@Sun.COM static void
iscsit_rxpdu_queue_monitor_session(iscsit_sess_t * ist)334012372SPriya.Krishnan@Sun.COM iscsit_rxpdu_queue_monitor_session(iscsit_sess_t *ist)
334112372SPriya.Krishnan@Sun.COM {
334212372SPriya.Krishnan@Sun.COM 	iscsit_cbuf_t	*cbuf	= ist->ist_rxpdu_queue;
334312372SPriya.Krishnan@Sun.COM 	idm_pdu_t	*next_pdu = NULL;
334412372SPriya.Krishnan@Sun.COM 	uint32_t	index, next_cmdsn, i;
334512372SPriya.Krishnan@Sun.COM 
334612372SPriya.Krishnan@Sun.COM 	/*
334712372SPriya.Krishnan@Sun.COM 	 * Assume that all PDUs in the staging queue have a cmdsn >= expcmdsn.
334812372SPriya.Krishnan@Sun.COM 	 * Starting with the expcmdsn, iterate over the staged PDUs to find
334912372SPriya.Krishnan@Sun.COM 	 * the next PDU with a wait time greater than the threshold. If found
335012372SPriya.Krishnan@Sun.COM 	 * advance the staged PDU to the SCSI layer, skipping over the missing
335112372SPriya.Krishnan@Sun.COM 	 * PDU(s) to get past the hole in the command sequence. It is up to
335212372SPriya.Krishnan@Sun.COM 	 * the initiator to note that the target has not acknowledged a cmdsn
335312372SPriya.Krishnan@Sun.COM 	 * and take appropriate action.
335412372SPriya.Krishnan@Sun.COM 	 *
335512372SPriya.Krishnan@Sun.COM 	 * Since the PDU(s) arrive in any random order, it is possible that
335612372SPriya.Krishnan@Sun.COM 	 * that the actual wait time for a particular PDU is much longer than
335712372SPriya.Krishnan@Sun.COM 	 * the defined threshold. e.g. Consider a case where commands are sent
335812372SPriya.Krishnan@Sun.COM 	 * over 4 different connections, and cmdsn = 1004 arrives first, then
335912372SPriya.Krishnan@Sun.COM 	 * 1003, and 1002 and 1001 are lost due to a connection failure.
336012372SPriya.Krishnan@Sun.COM 	 * So now 1003 is waiting for 1002 to be delivered, and although the
336112372SPriya.Krishnan@Sun.COM 	 * wait time of 1004 > wait time of 1003, only 1003 will be considered
336212372SPriya.Krishnan@Sun.COM 	 * by the monitor thread. 1004 will be automatically processed by
336312372SPriya.Krishnan@Sun.COM 	 * iscsit_process_pdu_in_queue() once the scan is complete and the
336412372SPriya.Krishnan@Sun.COM 	 * expcmdsn becomes current.
336512372SPriya.Krishnan@Sun.COM 	 */
336612372SPriya.Krishnan@Sun.COM 	mutex_enter(&ist->ist_sn_mutex);
336712372SPriya.Krishnan@Sun.COM 	cbuf = ist->ist_rxpdu_queue;
336812372SPriya.Krishnan@Sun.COM 	if (cbuf->cb_num_elems == 0) {
336912372SPriya.Krishnan@Sun.COM 		mutex_exit(&ist->ist_sn_mutex);
337012372SPriya.Krishnan@Sun.COM 		return;
337112372SPriya.Krishnan@Sun.COM 	}
337212372SPriya.Krishnan@Sun.COM 	for (next_pdu = NULL, i = 0; ; i++) {
337312372SPriya.Krishnan@Sun.COM 		next_cmdsn = ist->ist_expcmdsn + i; /* start at expcmdsn */
337412372SPriya.Krishnan@Sun.COM 		index = next_cmdsn % ISCSIT_RXPDU_QUEUE_LEN;
337512372SPriya.Krishnan@Sun.COM 		if ((next_pdu = cbuf->cb_buffer[index]) != NULL) {
337612372SPriya.Krishnan@Sun.COM 			/*
337712372SPriya.Krishnan@Sun.COM 			 * If the PDU wait time has not exceeded threshold
337812372SPriya.Krishnan@Sun.COM 			 * stop scanning the staging queue until the timer
337912372SPriya.Krishnan@Sun.COM 			 * fires again
338012372SPriya.Krishnan@Sun.COM 			 */
338112372SPriya.Krishnan@Sun.COM 			if ((ddi_get_time() - next_pdu->isp_queue_time)
338212372SPriya.Krishnan@Sun.COM 			    < rxpdu_queue_threshold) {
338312372SPriya.Krishnan@Sun.COM 				mutex_exit(&ist->ist_sn_mutex);
338412372SPriya.Krishnan@Sun.COM 				return;
338512372SPriya.Krishnan@Sun.COM 			}
338612372SPriya.Krishnan@Sun.COM 			/*
338712372SPriya.Krishnan@Sun.COM 			 * Remove the next PDU from the queue and post it
338812372SPriya.Krishnan@Sun.COM 			 * to the SCSI layer, skipping over the missing
338912372SPriya.Krishnan@Sun.COM 			 * PDU. Stop scanning the staging queue until
339012372SPriya.Krishnan@Sun.COM 			 * the monitor timer fires again
339112372SPriya.Krishnan@Sun.COM 			 */
339212372SPriya.Krishnan@Sun.COM 			(void) iscsit_remove_pdu_from_queue(ist, next_cmdsn);
339312372SPriya.Krishnan@Sun.COM 			mutex_exit(&ist->ist_sn_mutex);
339412372SPriya.Krishnan@Sun.COM 			DTRACE_PROBE3(advanced__to__blocked__cmdsn,
339512372SPriya.Krishnan@Sun.COM 			    iscsit_sess_t *, ist, idm_pdu_t *, next_pdu,
339612372SPriya.Krishnan@Sun.COM 			    uint32_t, next_cmdsn);
339712372SPriya.Krishnan@Sun.COM 			iscsit_post_staged_pdu(next_pdu);
339812372SPriya.Krishnan@Sun.COM 			/* Deliver any subsequent PDUs immediately */
339912372SPriya.Krishnan@Sun.COM 			iscsit_process_pdu_in_queue(ist);
340012372SPriya.Krishnan@Sun.COM 			return;
340112372SPriya.Krishnan@Sun.COM 		}
340212372SPriya.Krishnan@Sun.COM 		/*
340312372SPriya.Krishnan@Sun.COM 		 * Skipping over i PDUs, e.g. a case where commands 1001 and
340412372SPriya.Krishnan@Sun.COM 		 * 1002 are lost in the network, skip over both and post 1003
340512372SPriya.Krishnan@Sun.COM 		 * expcmdsn then becomes 1004 at the end of the scan.
340612372SPriya.Krishnan@Sun.COM 		 */
340712372SPriya.Krishnan@Sun.COM 		DTRACE_PROBE2(skipping__over__cmdsn, iscsit_sess_t *, ist,
340812372SPriya.Krishnan@Sun.COM 		    uint32_t, next_cmdsn);
340912372SPriya.Krishnan@Sun.COM 	}
341012372SPriya.Krishnan@Sun.COM 	/*
341112372SPriya.Krishnan@Sun.COM 	 * following the assumption, staged cmdsn >= expcmdsn, this statement
341212372SPriya.Krishnan@Sun.COM 	 * is never reached.
341312372SPriya.Krishnan@Sun.COM 	 */
341412372SPriya.Krishnan@Sun.COM }
3415