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