xref: /titanic_51/usr/src/uts/sun/io/scsi/adapters/sf.c (revision 5c5f137104b2d56181283389fa902220f2023809)
13d19cdaeSstevel /*
23d19cdaeSstevel  * CDDL HEADER START
33d19cdaeSstevel  *
43d19cdaeSstevel  * The contents of this file are subject to the terms of the
53d19cdaeSstevel  * Common Development and Distribution License (the "License").
63d19cdaeSstevel  * You may not use this file except in compliance with the License.
73d19cdaeSstevel  *
83d19cdaeSstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93d19cdaeSstevel  * or http://www.opensolaris.org/os/licensing.
103d19cdaeSstevel  * See the License for the specific language governing permissions
113d19cdaeSstevel  * and limitations under the License.
123d19cdaeSstevel  *
133d19cdaeSstevel  * When distributing Covered Code, include this CDDL HEADER in each
143d19cdaeSstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153d19cdaeSstevel  * If applicable, add the following below this CDDL HEADER, with the
163d19cdaeSstevel  * fields enclosed by brackets "[]" replaced with your own identifying
173d19cdaeSstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
183d19cdaeSstevel  *
193d19cdaeSstevel  * CDDL HEADER END
203d19cdaeSstevel  */
213d19cdaeSstevel /*
229c57abc8Ssrivijitha dugganapalli  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
233d19cdaeSstevel  * Use is subject to license terms.
2489b43686SBayard Bell  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
253d19cdaeSstevel  */
263d19cdaeSstevel 
273d19cdaeSstevel /*
283d19cdaeSstevel  * sf - Solaris Fibre Channel driver
293d19cdaeSstevel  *
303d19cdaeSstevel  * This module implements some of the Fibre Channel FC-4 layer, converting
313d19cdaeSstevel  * from FC frames to SCSI and back.  (Note: no sequence management is done
323d19cdaeSstevel  * here, though.)
333d19cdaeSstevel  */
343d19cdaeSstevel 
353d19cdaeSstevel #if defined(lint) && !defined(DEBUG)
363d19cdaeSstevel #define	DEBUG	1
373d19cdaeSstevel #endif
383d19cdaeSstevel 
393d19cdaeSstevel /*
403d19cdaeSstevel  * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
413d19cdaeSstevel  * Need to use the ugly RAID LUN mappings in FCP Annex D
423d19cdaeSstevel  * to prevent SCSA from barfing.  This *REALLY* needs to
433d19cdaeSstevel  * be addressed by the standards committee.
443d19cdaeSstevel  */
453d19cdaeSstevel #define	RAID_LUNS	1
463d19cdaeSstevel 
473d19cdaeSstevel #ifdef DEBUG
483d19cdaeSstevel static int sfdebug = 0;
493d19cdaeSstevel #include <sys/debug.h>
503d19cdaeSstevel 
513d19cdaeSstevel #define	SF_DEBUG(level, args) \
523d19cdaeSstevel 	if (sfdebug >= (level)) sf_log args
533d19cdaeSstevel #else
543d19cdaeSstevel #define	SF_DEBUG(level, args)
553d19cdaeSstevel #endif
563d19cdaeSstevel 
573d19cdaeSstevel static int sf_bus_config_debug = 0;
583d19cdaeSstevel 
593d19cdaeSstevel #include <sys/scsi/scsi.h>
603d19cdaeSstevel #include <sys/fc4/fcal.h>
613d19cdaeSstevel #include <sys/fc4/fcp.h>
623d19cdaeSstevel #include <sys/fc4/fcal_linkapp.h>
633d19cdaeSstevel #include <sys/socal_cq_defs.h>
643d19cdaeSstevel #include <sys/fc4/fcal_transport.h>
653d19cdaeSstevel #include <sys/fc4/fcio.h>
663d19cdaeSstevel #include <sys/scsi/adapters/sfvar.h>
673d19cdaeSstevel #include <sys/scsi/impl/scsi_reset_notify.h>
683d19cdaeSstevel #include <sys/stat.h>
693d19cdaeSstevel #include <sys/varargs.h>
703d19cdaeSstevel #include <sys/var.h>
713d19cdaeSstevel #include <sys/thread.h>
723d19cdaeSstevel #include <sys/proc.h>
733d19cdaeSstevel #include <sys/kstat.h>
743d19cdaeSstevel #include <sys/devctl.h>
753d19cdaeSstevel #include <sys/scsi/targets/ses.h>
763d19cdaeSstevel #include <sys/callb.h>
77*5c5f1371SRichard Lowe #include <sys/sysmacros.h>
783d19cdaeSstevel 
793d19cdaeSstevel static int sf_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
803d19cdaeSstevel static int sf_attach(dev_info_t *, ddi_attach_cmd_t);
813d19cdaeSstevel static int sf_detach(dev_info_t *, ddi_detach_cmd_t);
823d19cdaeSstevel static void sf_softstate_unlink(struct sf *);
833d19cdaeSstevel static int sf_scsi_bus_config(dev_info_t *parent, uint_t flag,
843d19cdaeSstevel     ddi_bus_config_op_t op, void *arg, dev_info_t **childp);
853d19cdaeSstevel static int sf_scsi_bus_unconfig(dev_info_t *parent, uint_t flag,
863d19cdaeSstevel     ddi_bus_config_op_t op, void *arg);
873d19cdaeSstevel static int sf_scsi_tgt_init(dev_info_t *, dev_info_t *,
883d19cdaeSstevel     scsi_hba_tran_t *, struct scsi_device *);
893d19cdaeSstevel static void sf_scsi_tgt_free(dev_info_t *, dev_info_t *,
903d19cdaeSstevel     scsi_hba_tran_t *, struct scsi_device *);
913d19cdaeSstevel static int sf_pkt_alloc_extern(struct sf *, struct sf_pkt *,
923d19cdaeSstevel     int, int, int);
933d19cdaeSstevel static void sf_pkt_destroy_extern(struct sf *, struct sf_pkt *);
943d19cdaeSstevel static struct scsi_pkt *sf_scsi_init_pkt(struct scsi_address *,
953d19cdaeSstevel     struct scsi_pkt *, struct buf *, int, int, int, int, int (*)(), caddr_t);
963d19cdaeSstevel static void sf_scsi_destroy_pkt(struct scsi_address *, struct scsi_pkt *);
973d19cdaeSstevel static void sf_scsi_dmafree(struct scsi_address *, struct scsi_pkt *);
983d19cdaeSstevel static void sf_scsi_sync_pkt(struct scsi_address *, struct scsi_pkt *);
993d19cdaeSstevel static int sf_scsi_reset_notify(struct scsi_address *, int,
1003d19cdaeSstevel     void (*)(caddr_t), caddr_t);
1013d19cdaeSstevel static int sf_scsi_get_name(struct scsi_device *, char *, int);
1023d19cdaeSstevel static int sf_scsi_get_bus_addr(struct scsi_device *, char *, int);
1033d19cdaeSstevel static int sf_add_cr_pool(struct sf *);
1043d19cdaeSstevel static int sf_cr_alloc(struct sf *, struct sf_pkt *, int (*)());
1053d19cdaeSstevel static void sf_cr_free(struct sf_cr_pool *, struct sf_pkt *);
1063d19cdaeSstevel static void sf_crpool_free(struct sf *);
1073d19cdaeSstevel static int sf_kmem_cache_constructor(void *, void *, int);
1083d19cdaeSstevel static void sf_kmem_cache_destructor(void *, void *);
1093d19cdaeSstevel static void sf_statec_callback(void *, int);
1103d19cdaeSstevel static int sf_login(struct sf *, uchar_t, uchar_t, uint_t, int);
1113d19cdaeSstevel static int sf_els_transport(struct sf *, struct sf_els_hdr *);
1123d19cdaeSstevel static void sf_els_callback(struct fcal_packet *);
1133d19cdaeSstevel static int sf_do_prli(struct sf *, struct sf_els_hdr *, struct la_els_logi *);
1143d19cdaeSstevel static int sf_do_adisc(struct sf *, struct sf_els_hdr *);
1153d19cdaeSstevel static int sf_do_reportlun(struct sf *, struct sf_els_hdr *,
1163d19cdaeSstevel     struct sf_target *);
1173d19cdaeSstevel static void sf_reportlun_callback(struct fcal_packet *);
1183d19cdaeSstevel static int sf_do_inquiry(struct sf *, struct sf_els_hdr *,
1193d19cdaeSstevel     struct sf_target *);
1203d19cdaeSstevel static void sf_inq_callback(struct fcal_packet *);
1213d19cdaeSstevel static struct fcal_packet *sf_els_alloc(struct sf *, uchar_t, int, int,
1223d19cdaeSstevel     int, caddr_t *, caddr_t *);
1233d19cdaeSstevel static void sf_els_free(struct fcal_packet *);
1243d19cdaeSstevel static struct sf_target *sf_create_target(struct sf *,
1253d19cdaeSstevel     struct sf_els_hdr *, int, int64_t);
1263d19cdaeSstevel #ifdef RAID_LUNS
1273d19cdaeSstevel static struct sf_target *sf_lookup_target(struct sf *, uchar_t *, int);
1283d19cdaeSstevel #else
1293d19cdaeSstevel static struct sf_target *sf_lookup_target(struct sf *, uchar_t *, int64_t);
1303d19cdaeSstevel #endif
1313d19cdaeSstevel static void sf_finish_init(struct sf *, int);
1323d19cdaeSstevel static void sf_offline_target(struct sf *, struct sf_target *);
1333d19cdaeSstevel static void sf_create_devinfo(struct sf *, struct sf_target *, int);
1343d19cdaeSstevel static int sf_create_props(dev_info_t *, struct sf_target *, int);
1353d19cdaeSstevel static int sf_commoncap(struct scsi_address *, char *, int, int, int);
1363d19cdaeSstevel static int sf_getcap(struct scsi_address *, char *, int);
1373d19cdaeSstevel static int sf_setcap(struct scsi_address *, char *, int, int);
1383d19cdaeSstevel static int sf_abort(struct scsi_address *, struct scsi_pkt *);
1393d19cdaeSstevel static int sf_reset(struct scsi_address *, int);
1403d19cdaeSstevel static void sf_abort_all(struct sf *, struct sf_target *, int, int, int);
1413d19cdaeSstevel static int sf_start(struct scsi_address *, struct scsi_pkt *);
1423d19cdaeSstevel static int sf_start_internal(struct sf *, struct sf_pkt *);
1433d19cdaeSstevel static void sf_fill_ids(struct sf *, struct sf_pkt *, struct sf_target *);
1443d19cdaeSstevel static int sf_prepare_pkt(struct sf *, struct sf_pkt *, struct sf_target *);
1453d19cdaeSstevel static int sf_dopoll(struct sf *, struct sf_pkt *);
1463d19cdaeSstevel static void sf_cmd_callback(struct fcal_packet *);
1473d19cdaeSstevel static void sf_throttle(struct sf *);
1483d19cdaeSstevel static void sf_watch(void *);
1493d19cdaeSstevel static void sf_throttle_start(struct sf *);
1503d19cdaeSstevel static void sf_check_targets(struct sf *);
1513d19cdaeSstevel static void sf_check_reset_delay(void *);
1523d19cdaeSstevel static int sf_target_timeout(struct sf *, struct sf_pkt *);
1533d19cdaeSstevel static void sf_force_lip(struct sf *);
1543d19cdaeSstevel static void sf_unsol_els_callback(void *, soc_response_t *, caddr_t);
1553d19cdaeSstevel static struct sf_els_hdr *sf_els_timeout(struct sf *, struct sf_els_hdr *);
1563d19cdaeSstevel /*PRINTFLIKE3*/
1573d19cdaeSstevel static void sf_log(struct sf *, int, const char *, ...);
1583d19cdaeSstevel static int sf_kstat_update(kstat_t *, int);
1593d19cdaeSstevel static int sf_open(dev_t *, int, int, cred_t *);
1603d19cdaeSstevel static int sf_close(dev_t, int, int, cred_t *);
1613d19cdaeSstevel static int sf_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1623d19cdaeSstevel static struct sf_target *sf_get_target_from_dip(struct sf *, dev_info_t *);
1633d19cdaeSstevel static int sf_bus_get_eventcookie(dev_info_t *, dev_info_t *, char *,
1643d19cdaeSstevel     ddi_eventcookie_t *);
1653d19cdaeSstevel static int sf_bus_add_eventcall(dev_info_t *, dev_info_t *,
1663d19cdaeSstevel     ddi_eventcookie_t, void (*)(), void *, ddi_callback_id_t *cb_id);
1673d19cdaeSstevel static int sf_bus_remove_eventcall(dev_info_t *devi, ddi_callback_id_t cb_id);
1683d19cdaeSstevel static int sf_bus_post_event(dev_info_t *, dev_info_t *,
1693d19cdaeSstevel     ddi_eventcookie_t, void *);
1703d19cdaeSstevel 
1713d19cdaeSstevel static void sf_hp_daemon(void *);
1723d19cdaeSstevel 
1733d19cdaeSstevel /*
1743d19cdaeSstevel  * this is required to be able to supply a control node
1753d19cdaeSstevel  * where ioctls can be executed
1763d19cdaeSstevel  */
1773d19cdaeSstevel struct cb_ops sf_cb_ops = {
1783d19cdaeSstevel 	sf_open,			/* open */
1793d19cdaeSstevel 	sf_close,			/* close */
1803d19cdaeSstevel 	nodev,				/* strategy */
1813d19cdaeSstevel 	nodev,				/* print */
1823d19cdaeSstevel 	nodev,				/* dump */
1833d19cdaeSstevel 	nodev,				/* read */
1843d19cdaeSstevel 	nodev,				/* write */
1853d19cdaeSstevel 	sf_ioctl,			/* ioctl */
1863d19cdaeSstevel 	nodev,				/* devmap */
1873d19cdaeSstevel 	nodev,				/* mmap */
1883d19cdaeSstevel 	nodev,				/* segmap */
1893d19cdaeSstevel 	nochpoll,			/* poll */
1903d19cdaeSstevel 	ddi_prop_op,			/* cb_prop_op */
1913d19cdaeSstevel 	0,				/* streamtab  */
1923d19cdaeSstevel 	D_MP | D_NEW | D_HOTPLUG	/* driver flags */
1933d19cdaeSstevel 
1943d19cdaeSstevel };
1953d19cdaeSstevel 
1963d19cdaeSstevel /*
1973d19cdaeSstevel  * autoconfiguration routines.
1983d19cdaeSstevel  */
1993d19cdaeSstevel static struct dev_ops sf_ops = {
2003d19cdaeSstevel 	DEVO_REV,		/* devo_rev, */
2013d19cdaeSstevel 	0,			/* refcnt  */
2023d19cdaeSstevel 	sf_info,		/* info */
2033d19cdaeSstevel 	nulldev,		/* identify */
2043d19cdaeSstevel 	nulldev,		/* probe */
2053d19cdaeSstevel 	sf_attach,		/* attach */
2063d19cdaeSstevel 	sf_detach,		/* detach */
2073d19cdaeSstevel 	nodev,			/* reset */
2083d19cdaeSstevel 	&sf_cb_ops,		/* driver operations */
2093d19cdaeSstevel 	NULL,			/* bus operations */
21019397407SSherry Moore 	NULL,			/* power management */
21119397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
2123d19cdaeSstevel };
2133d19cdaeSstevel 
2143d19cdaeSstevel #define	SF_NAME	"FC-AL FCP Nexus Driver"	/* Name of the module. */
21519397407SSherry Moore static	char	sf_version[] = "1.72 08/19/2008"; /* version of the module */
2163d19cdaeSstevel 
2173d19cdaeSstevel static struct modldrv modldrv = {
2183d19cdaeSstevel 	&mod_driverops, /* Type of module. This one is a driver */
21919397407SSherry Moore 	SF_NAME,
2203d19cdaeSstevel 	&sf_ops,	/* driver ops */
2213d19cdaeSstevel };
2223d19cdaeSstevel 
2233d19cdaeSstevel static struct modlinkage modlinkage = {
2243d19cdaeSstevel 	MODREV_1, (void *)&modldrv, NULL
2253d19cdaeSstevel };
2263d19cdaeSstevel 
2273d19cdaeSstevel /* XXXXXX The following is here to handle broken targets -- remove it later */
2283d19cdaeSstevel static int sf_reportlun_forever = 0;
2293d19cdaeSstevel /* XXXXXX */
2303d19cdaeSstevel static int sf_lip_on_plogo = 0;
2313d19cdaeSstevel static int sf_els_retries = SF_ELS_RETRIES;
2323d19cdaeSstevel static struct sf *sf_head = NULL;
2333d19cdaeSstevel static int sf_target_scan_cnt = 4;
2343d19cdaeSstevel static int sf_pkt_scan_cnt = 5;
2353d19cdaeSstevel static int sf_pool_scan_cnt = 1800;
2363d19cdaeSstevel static void *sf_state = NULL;
2373d19cdaeSstevel static int sf_watchdog_init = 0;
2383d19cdaeSstevel static int sf_watchdog_time = 0;
2393d19cdaeSstevel static int sf_watchdog_timeout = 1;
2403d19cdaeSstevel static int sf_watchdog_tick;
2413d19cdaeSstevel static int sf_watch_running = 0;
2423d19cdaeSstevel static timeout_id_t sf_watchdog_id;
2433d19cdaeSstevel static timeout_id_t sf_reset_timeout_id;
2443d19cdaeSstevel static int sf_max_targets = SF_MAX_TARGETS;
2453d19cdaeSstevel static kmutex_t sf_global_mutex;
2463d19cdaeSstevel static int sf_core = 0;
2473d19cdaeSstevel int *sf_token = NULL; /* Must not be static or lint complains. */
2483d19cdaeSstevel static kcondvar_t sf_watch_cv;
2493d19cdaeSstevel extern pri_t minclsyspri;
2503d19cdaeSstevel static ddi_eventcookie_t	sf_insert_eid;
2513d19cdaeSstevel static ddi_eventcookie_t	sf_remove_eid;
2523d19cdaeSstevel 
2533d19cdaeSstevel static ndi_event_definition_t	sf_event_defs[] = {
2543d19cdaeSstevel { SF_EVENT_TAG_INSERT, FCAL_INSERT_EVENT, EPL_KERNEL, 0 },
2553d19cdaeSstevel { SF_EVENT_TAG_REMOVE, FCAL_REMOVE_EVENT, EPL_INTERRUPT, 0 }
2563d19cdaeSstevel };
2573d19cdaeSstevel 
2583d19cdaeSstevel #define	SF_N_NDI_EVENTS	\
2593d19cdaeSstevel 	(sizeof (sf_event_defs) / sizeof (ndi_event_definition_t))
2603d19cdaeSstevel 
2613d19cdaeSstevel #ifdef DEBUG
2623d19cdaeSstevel static int sf_lip_flag = 1;		/* bool: to allow LIPs */
2633d19cdaeSstevel static int sf_reset_flag = 1;		/* bool: to allow reset after LIP */
2643d19cdaeSstevel static int sf_abort_flag = 0;		/* bool: to do just one abort */
2653d19cdaeSstevel #endif
2663d19cdaeSstevel 
267d3d50737SRafael Vanoni extern int64_t ddi_get_lbolt64(void);
2683d19cdaeSstevel 
2693d19cdaeSstevel /*
2703d19cdaeSstevel  * for converting between target number (switch) and hard address/AL_PA
2713d19cdaeSstevel  */
2723d19cdaeSstevel static uchar_t sf_switch_to_alpa[] = {
2733d19cdaeSstevel 	0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6,
2743d19cdaeSstevel 	0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce, 0xcd, 0xcc, 0xcb, 0xca,
2753d19cdaeSstevel 	0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5,
2763d19cdaeSstevel 	0xb4, 0xb3, 0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
2773d19cdaeSstevel 	0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b, 0x98, 0x97,
2783d19cdaeSstevel 	0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79,
2793d19cdaeSstevel 	0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b,
2803d19cdaeSstevel 	0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
2813d19cdaeSstevel 	0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a,
2823d19cdaeSstevel 	0x49, 0x47, 0x46, 0x45, 0x43, 0x3c, 0x3a, 0x39, 0x36, 0x35,
2833d19cdaeSstevel 	0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
2843d19cdaeSstevel 	0x27, 0x26, 0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17,
2853d19cdaeSstevel 	0x10, 0x0f, 0x08, 0x04, 0x02, 0x01
2863d19cdaeSstevel };
2873d19cdaeSstevel 
2883d19cdaeSstevel static uchar_t sf_alpa_to_switch[] = {
2893d19cdaeSstevel 	0x00, 0x7d, 0x7c, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00,
2903d19cdaeSstevel 	0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x78, 0x00, 0x00, 0x00,
2913d19cdaeSstevel 	0x00, 0x00, 0x00, 0x77, 0x76, 0x00, 0x00, 0x75, 0x00, 0x74,
2923d19cdaeSstevel 	0x73, 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x70, 0x6f, 0x6e,
2933d19cdaeSstevel 	0x00, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x00, 0x00, 0x67,
2943d19cdaeSstevel 	0x66, 0x65, 0x64, 0x63, 0x62, 0x00, 0x00, 0x61, 0x60, 0x00,
2953d19cdaeSstevel 	0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d,
2963d19cdaeSstevel 	0x5c, 0x5b, 0x00, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x00,
2973d19cdaeSstevel 	0x00, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x00, 0x00, 0x4e,
2983d19cdaeSstevel 	0x4d, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b,
2993d19cdaeSstevel 	0x00, 0x4a, 0x49, 0x48, 0x00, 0x47, 0x46, 0x45, 0x44, 0x43,
3003d19cdaeSstevel 	0x42, 0x00, 0x00, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x00,
3013d19cdaeSstevel 	0x00, 0x3b, 0x3a, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x37,
3023d19cdaeSstevel 	0x36, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
3033d19cdaeSstevel 	0x00, 0x00, 0x00, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
3043d19cdaeSstevel 	0x00, 0x31, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x2e, 0x2d, 0x2c,
3053d19cdaeSstevel 	0x00, 0x00, 0x00, 0x2b, 0x00, 0x2a, 0x29, 0x28, 0x00, 0x27,
3063d19cdaeSstevel 	0x26, 0x25, 0x24, 0x23, 0x22, 0x00, 0x00, 0x21, 0x20, 0x1f,
3073d19cdaeSstevel 	0x1e, 0x1d, 0x1c, 0x00, 0x00, 0x1b, 0x1a, 0x00, 0x19, 0x00,
3083d19cdaeSstevel 	0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x17, 0x16, 0x15,
3093d19cdaeSstevel 	0x00, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x00, 0x00, 0x0e,
3103d19cdaeSstevel 	0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x00, 0x00, 0x08, 0x07, 0x00,
3113d19cdaeSstevel 	0x06, 0x00, 0x00, 0x00, 0x05, 0x04, 0x03, 0x00, 0x02, 0x00,
3123d19cdaeSstevel 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3133d19cdaeSstevel };
3143d19cdaeSstevel 
3153d19cdaeSstevel /*
3163d19cdaeSstevel  * these macros call the proper transport-layer function given
3173d19cdaeSstevel  * a particular transport
3183d19cdaeSstevel  */
3193d19cdaeSstevel #define	soc_transport(a, b, c, d) (*a->fcal_ops->fcal_transport)(b, c, d)
3203d19cdaeSstevel #define	soc_transport_poll(a, b, c, d)\
3213d19cdaeSstevel 	(*a->fcal_ops->fcal_transport_poll)(b, c, d)
3223d19cdaeSstevel #define	soc_get_lilp_map(a, b, c, d, e)\
3233d19cdaeSstevel 	(*a->fcal_ops->fcal_lilp_map)(b, c, d, e)
3243d19cdaeSstevel #define	soc_force_lip(a, b, c, d, e)\
3253d19cdaeSstevel 	(*a->fcal_ops->fcal_force_lip)(b, c, d, e)
3263d19cdaeSstevel #define	soc_abort(a, b, c, d, e)\
3273d19cdaeSstevel 	(*a->fcal_ops->fcal_abort_cmd)(b, c, d, e)
3283d19cdaeSstevel #define	soc_force_reset(a, b, c, d)\
3293d19cdaeSstevel 	(*a->fcal_ops->fcal_force_reset)(b, c, d)
3303d19cdaeSstevel #define	soc_add_ulp(a, b, c, d, e, f, g, h)\
3313d19cdaeSstevel 	(*a->fcal_ops->fcal_add_ulp)(b, c, d, e, f, g, h)
3323d19cdaeSstevel #define	soc_remove_ulp(a, b, c, d, e)\
3333d19cdaeSstevel 	(*a->fcal_ops->fcal_remove_ulp)(b, c, d, e)
3343d19cdaeSstevel #define	soc_take_core(a, b) (*a->fcal_ops->fcal_take_core)(b)
3353d19cdaeSstevel 
3363d19cdaeSstevel 
3373d19cdaeSstevel /* power management property defines (should be in a common include file?) */
3383d19cdaeSstevel #define	PM_HARDWARE_STATE_PROP		"pm-hardware-state"
3393d19cdaeSstevel #define	PM_NEEDS_SUSPEND_RESUME		"needs-suspend-resume"
3403d19cdaeSstevel 
3413d19cdaeSstevel 
3423d19cdaeSstevel /* node properties */
3433d19cdaeSstevel #define	NODE_WWN_PROP			"node-wwn"
3443d19cdaeSstevel #define	PORT_WWN_PROP			"port-wwn"
3453d19cdaeSstevel #define	LIP_CNT_PROP			"lip-count"
3463d19cdaeSstevel #define	TARGET_PROP			"target"
3473d19cdaeSstevel #define	LUN_PROP			"lun"
3483d19cdaeSstevel 
3493d19cdaeSstevel 
3503d19cdaeSstevel /*
3513d19cdaeSstevel  * initialize this driver and install this module
3523d19cdaeSstevel  */
3533d19cdaeSstevel int
3543d19cdaeSstevel _init(void)
3553d19cdaeSstevel {
3563d19cdaeSstevel 	int	i;
3573d19cdaeSstevel 
3583d19cdaeSstevel 	i = ddi_soft_state_init(&sf_state, sizeof (struct sf),
3593d19cdaeSstevel 	    SF_INIT_ITEMS);
3603d19cdaeSstevel 	if (i != 0)
3613d19cdaeSstevel 		return (i);
3623d19cdaeSstevel 
3633d19cdaeSstevel 	if ((i = scsi_hba_init(&modlinkage)) != 0) {
3643d19cdaeSstevel 		ddi_soft_state_fini(&sf_state);
3653d19cdaeSstevel 		return (i);
3663d19cdaeSstevel 	}
3673d19cdaeSstevel 
3683d19cdaeSstevel 	mutex_init(&sf_global_mutex, NULL, MUTEX_DRIVER, NULL);
3693d19cdaeSstevel 	sf_watch_running = 0;
3703d19cdaeSstevel 	cv_init(&sf_watch_cv, NULL, CV_DRIVER, NULL);
3713d19cdaeSstevel 
3723d19cdaeSstevel 	if ((i = mod_install(&modlinkage)) != 0) {
3733d19cdaeSstevel 		mutex_destroy(&sf_global_mutex);
3743d19cdaeSstevel 		cv_destroy(&sf_watch_cv);
3753d19cdaeSstevel 		scsi_hba_fini(&modlinkage);
3763d19cdaeSstevel 		ddi_soft_state_fini(&sf_state);
3773d19cdaeSstevel 		return (i);
3783d19cdaeSstevel 	}
3793d19cdaeSstevel 
3803d19cdaeSstevel 	return (i);
3813d19cdaeSstevel }
3823d19cdaeSstevel 
3833d19cdaeSstevel 
3843d19cdaeSstevel /*
3853d19cdaeSstevel  * remove this driver module from the system
3863d19cdaeSstevel  */
3873d19cdaeSstevel int
3883d19cdaeSstevel _fini(void)
3893d19cdaeSstevel {
3903d19cdaeSstevel 	int	i;
3913d19cdaeSstevel 
3923d19cdaeSstevel 	if ((i = mod_remove(&modlinkage)) == 0) {
3933d19cdaeSstevel 		scsi_hba_fini(&modlinkage);
3943d19cdaeSstevel 		mutex_destroy(&sf_global_mutex);
3953d19cdaeSstevel 		cv_destroy(&sf_watch_cv);
3963d19cdaeSstevel 		ddi_soft_state_fini(&sf_state);
3973d19cdaeSstevel 	}
3983d19cdaeSstevel 	return (i);
3993d19cdaeSstevel }
4003d19cdaeSstevel 
4013d19cdaeSstevel 
4023d19cdaeSstevel int
4033d19cdaeSstevel _info(struct modinfo *modinfop)
4043d19cdaeSstevel {
4053d19cdaeSstevel 	return (mod_info(&modlinkage, modinfop));
4063d19cdaeSstevel }
4073d19cdaeSstevel 
4083d19cdaeSstevel /*
4093d19cdaeSstevel  * Given the device number return the devinfo pointer or instance
4103d19cdaeSstevel  */
4113d19cdaeSstevel /*ARGSUSED*/
4123d19cdaeSstevel static int
4133d19cdaeSstevel sf_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4143d19cdaeSstevel {
4153d19cdaeSstevel 	int		instance = SF_MINOR2INST(getminor((dev_t)arg));
4163d19cdaeSstevel 	struct sf	*sf;
4173d19cdaeSstevel 
4183d19cdaeSstevel 	switch (infocmd) {
4193d19cdaeSstevel 	case DDI_INFO_DEVT2DEVINFO:
4203d19cdaeSstevel 		sf = ddi_get_soft_state(sf_state, instance);
4213d19cdaeSstevel 		if (sf != NULL)
4223d19cdaeSstevel 			*result = sf->sf_dip;
4233d19cdaeSstevel 		else {
4243d19cdaeSstevel 			*result = NULL;
4253d19cdaeSstevel 			return (DDI_FAILURE);
4263d19cdaeSstevel 		}
4273d19cdaeSstevel 		break;
4283d19cdaeSstevel 
4293d19cdaeSstevel 	case DDI_INFO_DEVT2INSTANCE:
4303d19cdaeSstevel 		*result = (void *)(uintptr_t)instance;
4313d19cdaeSstevel 		break;
4323d19cdaeSstevel 	default:
4333d19cdaeSstevel 		return (DDI_FAILURE);
4343d19cdaeSstevel 	}
4353d19cdaeSstevel 	return (DDI_SUCCESS);
4363d19cdaeSstevel }
4373d19cdaeSstevel 
4383d19cdaeSstevel /*
4393d19cdaeSstevel  * either attach or resume this driver
4403d19cdaeSstevel  */
4413d19cdaeSstevel static int
4423d19cdaeSstevel sf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4433d19cdaeSstevel {
4443d19cdaeSstevel 	int instance;
4453d19cdaeSstevel 	int mutex_initted = FALSE;
4463d19cdaeSstevel 	uint_t ccount;
4473d19cdaeSstevel 	size_t i, real_size;
4483d19cdaeSstevel 	struct fcal_transport *handle;
4493d19cdaeSstevel 	char buf[64];
4503d19cdaeSstevel 	struct sf *sf, *tsf;
4513d19cdaeSstevel 	scsi_hba_tran_t *tran = NULL;
4523d19cdaeSstevel 	int	handle_bound = FALSE;
4533d19cdaeSstevel 	kthread_t *tp;
4543d19cdaeSstevel 
4553d19cdaeSstevel 
4563d19cdaeSstevel 	switch ((int)cmd) {
4573d19cdaeSstevel 
4583d19cdaeSstevel 	case DDI_RESUME:
4593d19cdaeSstevel 
4603d19cdaeSstevel 		/*
4613d19cdaeSstevel 		 * we've previously been SF_STATE_OFFLINEd by a DDI_SUSPEND,
4623d19cdaeSstevel 		 * so time to undo that and get going again by forcing a
4633d19cdaeSstevel 		 * lip
4643d19cdaeSstevel 		 */
4653d19cdaeSstevel 
4663d19cdaeSstevel 		instance = ddi_get_instance(dip);
4673d19cdaeSstevel 
4683d19cdaeSstevel 		sf = ddi_get_soft_state(sf_state, instance);
4693d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT,
4703d19cdaeSstevel 		    "sf_attach: DDI_RESUME for sf%d\n", instance));
4713d19cdaeSstevel 		if (sf == NULL) {
4723d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: bad soft state", instance);
4733d19cdaeSstevel 			return (DDI_FAILURE);
4743d19cdaeSstevel 		}
4753d19cdaeSstevel 
4763d19cdaeSstevel 		/*
4773d19cdaeSstevel 		 * clear suspended flag so that normal operations can resume
4783d19cdaeSstevel 		 */
4793d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
4803d19cdaeSstevel 		sf->sf_state &= ~SF_STATE_SUSPENDED;
4813d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
4823d19cdaeSstevel 
4833d19cdaeSstevel 		/*
4843d19cdaeSstevel 		 * force a login by setting our state to offline
4853d19cdaeSstevel 		 */
4863d19cdaeSstevel 		sf->sf_timer = sf_watchdog_time + SF_OFFLINE_TIMEOUT;
4873d19cdaeSstevel 		sf->sf_state = SF_STATE_OFFLINE;
4883d19cdaeSstevel 
4893d19cdaeSstevel 		/*
4903d19cdaeSstevel 		 * call transport routine to register state change and
4913d19cdaeSstevel 		 * ELS callback routines (to register us as a ULP)
4923d19cdaeSstevel 		 */
4933d19cdaeSstevel 		soc_add_ulp(sf->sf_sochandle, sf->sf_socp,
4943d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, TYPE_SCSI_FCP,
4953d19cdaeSstevel 		    sf_statec_callback, sf_unsol_els_callback, NULL, sf);
4963d19cdaeSstevel 
4973d19cdaeSstevel 		/*
4983d19cdaeSstevel 		 * call transport routine to force loop initialization
4993d19cdaeSstevel 		 */
5003d19cdaeSstevel 		(void) soc_force_lip(sf->sf_sochandle, sf->sf_socp,
5013d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, 0, FCAL_NO_LIP);
5023d19cdaeSstevel 
5033d19cdaeSstevel 		/*
5043d19cdaeSstevel 		 * increment watchdog init flag, setting watchdog timeout
5053d19cdaeSstevel 		 * if we are the first (since somebody has to do it)
5063d19cdaeSstevel 		 */
5073d19cdaeSstevel 		mutex_enter(&sf_global_mutex);
5083d19cdaeSstevel 		if (!sf_watchdog_init++) {
5093d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
5103d19cdaeSstevel 			sf_watchdog_id = timeout(sf_watch,
5113d19cdaeSstevel 			    (caddr_t)0, sf_watchdog_tick);
5123d19cdaeSstevel 		} else {
5133d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
5143d19cdaeSstevel 		}
5153d19cdaeSstevel 
5163d19cdaeSstevel 		return (DDI_SUCCESS);
5173d19cdaeSstevel 
5183d19cdaeSstevel 	case DDI_ATTACH:
5193d19cdaeSstevel 
5203d19cdaeSstevel 		/*
5213d19cdaeSstevel 		 * this instance attaching for the first time
5223d19cdaeSstevel 		 */
5233d19cdaeSstevel 
5243d19cdaeSstevel 		instance = ddi_get_instance(dip);
5253d19cdaeSstevel 
5263d19cdaeSstevel 		if (ddi_soft_state_zalloc(sf_state, instance) !=
5273d19cdaeSstevel 		    DDI_SUCCESS) {
5283d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: failed to allocate soft state",
5293d19cdaeSstevel 			    instance);
5303d19cdaeSstevel 			return (DDI_FAILURE);
5313d19cdaeSstevel 		}
5323d19cdaeSstevel 
5333d19cdaeSstevel 		sf = ddi_get_soft_state(sf_state, instance);
5343d19cdaeSstevel 		SF_DEBUG(4, (sf, CE_CONT,
5353d19cdaeSstevel 		    "sf_attach: DDI_ATTACH for sf%d\n", instance));
5363d19cdaeSstevel 		if (sf == NULL) {
5373d19cdaeSstevel 			/* this shouldn't happen since we just allocated it */
5383d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: bad soft state", instance);
5393d19cdaeSstevel 			return (DDI_FAILURE);
5403d19cdaeSstevel 		}
5413d19cdaeSstevel 
5423d19cdaeSstevel 		/*
5433d19cdaeSstevel 		 * from this point on, if there's an error, we must de-allocate
5443d19cdaeSstevel 		 * soft state before returning DDI_FAILURE
5453d19cdaeSstevel 		 */
5463d19cdaeSstevel 
5473d19cdaeSstevel 		if ((handle = ddi_get_parent_data(dip)) == NULL) {
5483d19cdaeSstevel 			cmn_err(CE_WARN,
5493d19cdaeSstevel 			    "sf%d: failed to obtain transport handle",
5503d19cdaeSstevel 			    instance);
5513d19cdaeSstevel 			goto fail;
5523d19cdaeSstevel 		}
5533d19cdaeSstevel 
5543d19cdaeSstevel 		/* fill in our soft state structure */
5553d19cdaeSstevel 		sf->sf_dip = dip;
5563d19cdaeSstevel 		sf->sf_state = SF_STATE_INIT;
5573d19cdaeSstevel 		sf->sf_throttle = handle->fcal_cmdmax;
5583d19cdaeSstevel 		sf->sf_sochandle = handle;
5593d19cdaeSstevel 		sf->sf_socp = handle->fcal_handle;
5603d19cdaeSstevel 		sf->sf_check_n_close = 0;
5613d19cdaeSstevel 
5623d19cdaeSstevel 		/* create a command/response buffer pool for this instance */
5633d19cdaeSstevel 		if (sf_add_cr_pool(sf) != DDI_SUCCESS) {
5643d19cdaeSstevel 			cmn_err(CE_WARN,
5653d19cdaeSstevel 			    "sf%d: failed to allocate command/response pool",
5663d19cdaeSstevel 			    instance);
5673d19cdaeSstevel 			goto fail;
5683d19cdaeSstevel 		}
5693d19cdaeSstevel 
5703d19cdaeSstevel 		/* create a a cache for this instance */
5713d19cdaeSstevel 		(void) sprintf(buf, "sf%d_cache", instance);
5723d19cdaeSstevel 		sf->sf_pkt_cache = kmem_cache_create(buf,
5733d19cdaeSstevel 		    sizeof (fcal_packet_t) + sizeof (struct sf_pkt) +
574602ca9eaScth 		    scsi_pkt_size(), 8,
5753d19cdaeSstevel 		    sf_kmem_cache_constructor, sf_kmem_cache_destructor,
5763d19cdaeSstevel 		    NULL, NULL, NULL, 0);
5773d19cdaeSstevel 		if (sf->sf_pkt_cache == NULL) {
5783d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: failed to allocate kmem cache",
5793d19cdaeSstevel 			    instance);
5803d19cdaeSstevel 			goto fail;
5813d19cdaeSstevel 		}
5823d19cdaeSstevel 
5833d19cdaeSstevel 		/* set up a handle and allocate memory for DMA */
5843d19cdaeSstevel 		if (ddi_dma_alloc_handle(sf->sf_dip, sf->sf_sochandle->
5853d19cdaeSstevel 		    fcal_dmaattr, DDI_DMA_DONTWAIT, NULL, &sf->
5863d19cdaeSstevel 		    sf_lilp_dmahandle) != DDI_SUCCESS) {
5873d19cdaeSstevel 			cmn_err(CE_WARN,
5883d19cdaeSstevel 			    "sf%d: failed to allocate dma handle for lilp map",
5893d19cdaeSstevel 			    instance);
5903d19cdaeSstevel 			goto fail;
5913d19cdaeSstevel 		}
5923d19cdaeSstevel 		i = sizeof (struct fcal_lilp_map) + 1;
5933d19cdaeSstevel 		if (ddi_dma_mem_alloc(sf->sf_lilp_dmahandle,
5943d19cdaeSstevel 		    i, sf->sf_sochandle->
5953d19cdaeSstevel 		    fcal_accattr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
5963d19cdaeSstevel 		    (caddr_t *)&sf->sf_lilp_map, &real_size,
5973d19cdaeSstevel 		    &sf->sf_lilp_acchandle) != DDI_SUCCESS) {
5983d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: failed to allocate lilp map",
5993d19cdaeSstevel 			    instance);
6003d19cdaeSstevel 			goto fail;
6013d19cdaeSstevel 		}
6023d19cdaeSstevel 		if (real_size < i) {
6033d19cdaeSstevel 			/* no error message ??? */
6043d19cdaeSstevel 			goto fail;		/* trouble allocating memory */
6053d19cdaeSstevel 		}
6063d19cdaeSstevel 
6073d19cdaeSstevel 		/*
6083d19cdaeSstevel 		 * set up the address for the DMA transfers (getting a cookie)
6093d19cdaeSstevel 		 */
6103d19cdaeSstevel 		if (ddi_dma_addr_bind_handle(sf->sf_lilp_dmahandle, NULL,
6113d19cdaeSstevel 		    (caddr_t)sf->sf_lilp_map, real_size,
6123d19cdaeSstevel 		    DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
6133d19cdaeSstevel 		    &sf->sf_lilp_dmacookie, &ccount) != DDI_DMA_MAPPED) {
6143d19cdaeSstevel 			cmn_err(CE_WARN,
6153d19cdaeSstevel 			    "sf%d: failed to bind dma handle for lilp map",
6163d19cdaeSstevel 			    instance);
6173d19cdaeSstevel 			goto fail;
6183d19cdaeSstevel 		}
6193d19cdaeSstevel 		handle_bound = TRUE;
6203d19cdaeSstevel 		/* ensure only one cookie was allocated */
6213d19cdaeSstevel 		if (ccount != 1) {
6223d19cdaeSstevel 			goto fail;
6233d19cdaeSstevel 		}
6243d19cdaeSstevel 
6253d19cdaeSstevel 		/* ensure LILP map and DMA cookie addresses are even?? */
6263d19cdaeSstevel 		sf->sf_lilp_map = (struct fcal_lilp_map *)(((uintptr_t)sf->
6273d19cdaeSstevel 		    sf_lilp_map + 1) & ~1);
6283d19cdaeSstevel 		sf->sf_lilp_dmacookie.dmac_address = (sf->
6293d19cdaeSstevel 		    sf_lilp_dmacookie.dmac_address + 1) & ~1;
6303d19cdaeSstevel 
6313d19cdaeSstevel 		/* set up all of our mutexes and condition variables */
6323d19cdaeSstevel 		mutex_init(&sf->sf_mutex, NULL, MUTEX_DRIVER, NULL);
6333d19cdaeSstevel 		mutex_init(&sf->sf_cmd_mutex, NULL, MUTEX_DRIVER, NULL);
6343d19cdaeSstevel 		mutex_init(&sf->sf_cr_mutex, NULL, MUTEX_DRIVER, NULL);
6353d19cdaeSstevel 		mutex_init(&sf->sf_hp_daemon_mutex, NULL, MUTEX_DRIVER, NULL);
6363d19cdaeSstevel 		cv_init(&sf->sf_cr_cv, NULL, CV_DRIVER, NULL);
6373d19cdaeSstevel 		cv_init(&sf->sf_hp_daemon_cv, NULL, CV_DRIVER, NULL);
6383d19cdaeSstevel 
6393d19cdaeSstevel 		mutex_initted = TRUE;
6403d19cdaeSstevel 
6413d19cdaeSstevel 		/* create our devctl minor node */
6423d19cdaeSstevel 		if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
6433d19cdaeSstevel 		    SF_INST2DEVCTL_MINOR(instance),
6443d19cdaeSstevel 		    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
6453d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: ddi_create_minor_node failed"
6463d19cdaeSstevel 			    " for devctl", instance);
6473d19cdaeSstevel 			goto fail;
6483d19cdaeSstevel 		}
6493d19cdaeSstevel 
6503d19cdaeSstevel 		/* create fc minor node */
6513d19cdaeSstevel 		if (ddi_create_minor_node(dip, "fc", S_IFCHR,
6523d19cdaeSstevel 		    SF_INST2FC_MINOR(instance), DDI_NT_FC_ATTACHMENT_POINT,
6533d19cdaeSstevel 		    0) != DDI_SUCCESS) {
6543d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: ddi_create_minor_node failed"
6553d19cdaeSstevel 			    " for fc", instance);
6563d19cdaeSstevel 			goto fail;
6573d19cdaeSstevel 		}
6583d19cdaeSstevel 		/* allocate a SCSI transport structure */
6593d19cdaeSstevel 		tran = scsi_hba_tran_alloc(dip, 0);
6603d19cdaeSstevel 		if (tran == NULL) {
6613d19cdaeSstevel 			/* remove all minor nodes created */
6623d19cdaeSstevel 			ddi_remove_minor_node(dip, NULL);
6633d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: scsi_hba_tran_alloc failed",
6643d19cdaeSstevel 			    instance);
6653d19cdaeSstevel 			goto fail;
6663d19cdaeSstevel 		}
6673d19cdaeSstevel 
668602ca9eaScth 		/* Indicate that we are 'sizeof (scsi_*(9S))' clean. */
669602ca9eaScth 		scsi_size_clean(dip);		/* SCSI_SIZE_CLEAN_VERIFY ok */
670602ca9eaScth 
6713d19cdaeSstevel 		/* save ptr to new transport structure and fill it in */
6723d19cdaeSstevel 		sf->sf_tran = tran;
6733d19cdaeSstevel 
6743d19cdaeSstevel 		tran->tran_hba_private		= sf;
6753d19cdaeSstevel 		tran->tran_tgt_private		= NULL;
6763d19cdaeSstevel 		tran->tran_tgt_init		= sf_scsi_tgt_init;
6773d19cdaeSstevel 		tran->tran_tgt_probe		= NULL;
6783d19cdaeSstevel 		tran->tran_tgt_free		= sf_scsi_tgt_free;
6793d19cdaeSstevel 
6803d19cdaeSstevel 		tran->tran_start		= sf_start;
6813d19cdaeSstevel 		tran->tran_abort		= sf_abort;
6823d19cdaeSstevel 		tran->tran_reset		= sf_reset;
6833d19cdaeSstevel 		tran->tran_getcap		= sf_getcap;
6843d19cdaeSstevel 		tran->tran_setcap		= sf_setcap;
6853d19cdaeSstevel 		tran->tran_init_pkt		= sf_scsi_init_pkt;
6863d19cdaeSstevel 		tran->tran_destroy_pkt		= sf_scsi_destroy_pkt;
6873d19cdaeSstevel 		tran->tran_dmafree		= sf_scsi_dmafree;
6883d19cdaeSstevel 		tran->tran_sync_pkt		= sf_scsi_sync_pkt;
6893d19cdaeSstevel 		tran->tran_reset_notify		= sf_scsi_reset_notify;
6903d19cdaeSstevel 
6913d19cdaeSstevel 		/*
6923d19cdaeSstevel 		 * register event notification routines with scsa
6933d19cdaeSstevel 		 */
6943d19cdaeSstevel 		tran->tran_get_eventcookie	= sf_bus_get_eventcookie;
6953d19cdaeSstevel 		tran->tran_add_eventcall	= sf_bus_add_eventcall;
6963d19cdaeSstevel 		tran->tran_remove_eventcall	= sf_bus_remove_eventcall;
6973d19cdaeSstevel 		tran->tran_post_event		= sf_bus_post_event;
6983d19cdaeSstevel 
6993d19cdaeSstevel 		/*
7003d19cdaeSstevel 		 * register bus configure/unconfigure
7013d19cdaeSstevel 		 */
7023d19cdaeSstevel 		tran->tran_bus_config		= sf_scsi_bus_config;
7033d19cdaeSstevel 		tran->tran_bus_unconfig		= sf_scsi_bus_unconfig;
7043d19cdaeSstevel 
7053d19cdaeSstevel 		/*
7063d19cdaeSstevel 		 * allocate an ndi event handle
7073d19cdaeSstevel 		 */
7083d19cdaeSstevel 		sf->sf_event_defs = (ndi_event_definition_t *)
7093d19cdaeSstevel 		    kmem_zalloc(sizeof (sf_event_defs), KM_SLEEP);
7103d19cdaeSstevel 
7113d19cdaeSstevel 		bcopy(sf_event_defs, sf->sf_event_defs,
7123d19cdaeSstevel 		    sizeof (sf_event_defs));
7133d19cdaeSstevel 
7143d19cdaeSstevel 		(void) ndi_event_alloc_hdl(dip, NULL,
7153d19cdaeSstevel 		    &sf->sf_event_hdl, NDI_SLEEP);
7163d19cdaeSstevel 
7173d19cdaeSstevel 		sf->sf_events.ndi_events_version = NDI_EVENTS_REV1;
7183d19cdaeSstevel 		sf->sf_events.ndi_n_events = SF_N_NDI_EVENTS;
7193d19cdaeSstevel 		sf->sf_events.ndi_event_defs = sf->sf_event_defs;
7203d19cdaeSstevel 
7213d19cdaeSstevel 		if (ndi_event_bind_set(sf->sf_event_hdl,
7223d19cdaeSstevel 		    &sf->sf_events, NDI_SLEEP) != NDI_SUCCESS) {
7233d19cdaeSstevel 			goto fail;
7243d19cdaeSstevel 		}
7253d19cdaeSstevel 
7263d19cdaeSstevel 		tran->tran_get_name		= sf_scsi_get_name;
7273d19cdaeSstevel 		tran->tran_get_bus_addr		= sf_scsi_get_bus_addr;
7283d19cdaeSstevel 
7293d19cdaeSstevel 		/* setup and attach SCSI hba transport */
7303d19cdaeSstevel 		if (scsi_hba_attach_setup(dip, sf->sf_sochandle->
7313d19cdaeSstevel 		    fcal_dmaattr, tran, SCSI_HBA_TRAN_CLONE) != DDI_SUCCESS) {
7323d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: scsi_hba_attach_setup failed",
7333d19cdaeSstevel 			    instance);
7343d19cdaeSstevel 			goto fail;
7353d19cdaeSstevel 		}
7363d19cdaeSstevel 
7373d19cdaeSstevel 		/* set up kstats */
7383d19cdaeSstevel 		if ((sf->sf_ksp = kstat_create("sf", instance, "statistics",
7393d19cdaeSstevel 		    "controller", KSTAT_TYPE_RAW, sizeof (struct sf_stats),
7403d19cdaeSstevel 		    KSTAT_FLAG_VIRTUAL)) == NULL) {
7413d19cdaeSstevel 			cmn_err(CE_WARN, "sf%d: failed to create kstat",
7423d19cdaeSstevel 			    instance);
7433d19cdaeSstevel 		} else {
7443d19cdaeSstevel 			sf->sf_stats.version = 2;
7453d19cdaeSstevel 			(void) sprintf(sf->sf_stats.drvr_name,
7463d19cdaeSstevel 			"%s: %s", SF_NAME, sf_version);
7473d19cdaeSstevel 			sf->sf_ksp->ks_data = (void *)&sf->sf_stats;
7483d19cdaeSstevel 			sf->sf_ksp->ks_private = sf;
7493d19cdaeSstevel 			sf->sf_ksp->ks_update = sf_kstat_update;
7503d19cdaeSstevel 			kstat_install(sf->sf_ksp);
7513d19cdaeSstevel 		}
7523d19cdaeSstevel 
7533d19cdaeSstevel 		/* create the hotplug thread */
7543d19cdaeSstevel 		mutex_enter(&sf->sf_hp_daemon_mutex);
7553d19cdaeSstevel 		tp = thread_create(NULL, 0,
7563d19cdaeSstevel 		    (void (*)())sf_hp_daemon, sf, 0, &p0, TS_RUN, minclsyspri);
7573d19cdaeSstevel 		sf->sf_hp_tid = tp->t_did;
7583d19cdaeSstevel 		mutex_exit(&sf->sf_hp_daemon_mutex);
7593d19cdaeSstevel 
7603d19cdaeSstevel 		/* add this soft state instance to the head of the list */
7613d19cdaeSstevel 		mutex_enter(&sf_global_mutex);
7623d19cdaeSstevel 		sf->sf_next = sf_head;
7633d19cdaeSstevel 		tsf = sf_head;
7643d19cdaeSstevel 		sf_head = sf;
7653d19cdaeSstevel 
7663d19cdaeSstevel 		/*
7673d19cdaeSstevel 		 * find entry in list that has the same FC-AL handle (if any)
7683d19cdaeSstevel 		 */
7693d19cdaeSstevel 		while (tsf != NULL) {
7703d19cdaeSstevel 			if (tsf->sf_socp == sf->sf_socp) {
7713d19cdaeSstevel 				break;		/* found matching entry */
7723d19cdaeSstevel 			}
7733d19cdaeSstevel 			tsf = tsf->sf_next;
7743d19cdaeSstevel 		}
7753d19cdaeSstevel 
7763d19cdaeSstevel 		if (tsf != NULL) {
7773d19cdaeSstevel 			/* if we found a matching entry keep track of it */
7783d19cdaeSstevel 			sf->sf_sibling = tsf;
7793d19cdaeSstevel 		}
7803d19cdaeSstevel 
7813d19cdaeSstevel 		/*
7823d19cdaeSstevel 		 * increment watchdog init flag, setting watchdog timeout
7833d19cdaeSstevel 		 * if we are the first (since somebody has to do it)
7843d19cdaeSstevel 		 */
7853d19cdaeSstevel 		if (!sf_watchdog_init++) {
7863d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
7873d19cdaeSstevel 			sf_watchdog_tick = sf_watchdog_timeout *
7883d19cdaeSstevel 			    drv_usectohz(1000000);
7893d19cdaeSstevel 			sf_watchdog_id = timeout(sf_watch,
7903d19cdaeSstevel 			    NULL, sf_watchdog_tick);
7913d19cdaeSstevel 		} else {
7923d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
7933d19cdaeSstevel 		}
7943d19cdaeSstevel 
7953d19cdaeSstevel 		if (tsf != NULL) {
7963d19cdaeSstevel 			/*
7973d19cdaeSstevel 			 * set up matching entry to be our sibling
7983d19cdaeSstevel 			 */
7993d19cdaeSstevel 			mutex_enter(&tsf->sf_mutex);
8003d19cdaeSstevel 			tsf->sf_sibling = sf;
8013d19cdaeSstevel 			mutex_exit(&tsf->sf_mutex);
8023d19cdaeSstevel 		}
8033d19cdaeSstevel 
8043d19cdaeSstevel 		/*
8053d19cdaeSstevel 		 * create this property so that PM code knows we want
8063d19cdaeSstevel 		 * to be suspended at PM time
8073d19cdaeSstevel 		 */
8083d19cdaeSstevel 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
8093d19cdaeSstevel 		    PM_HARDWARE_STATE_PROP, PM_NEEDS_SUSPEND_RESUME);
8103d19cdaeSstevel 
8113d19cdaeSstevel 		/* log the fact that we have a new device */
8123d19cdaeSstevel 		ddi_report_dev(dip);
8133d19cdaeSstevel 
8143d19cdaeSstevel 		/*
8153d19cdaeSstevel 		 * force a login by setting our state to offline
8163d19cdaeSstevel 		 */
8173d19cdaeSstevel 		sf->sf_timer = sf_watchdog_time + SF_OFFLINE_TIMEOUT;
8183d19cdaeSstevel 		sf->sf_state = SF_STATE_OFFLINE;
8193d19cdaeSstevel 
8203d19cdaeSstevel 		/*
8213d19cdaeSstevel 		 * call transport routine to register state change and
8223d19cdaeSstevel 		 * ELS callback routines (to register us as a ULP)
8233d19cdaeSstevel 		 */
8243d19cdaeSstevel 		soc_add_ulp(sf->sf_sochandle, sf->sf_socp,
8253d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, TYPE_SCSI_FCP,
8263d19cdaeSstevel 		    sf_statec_callback, sf_unsol_els_callback, NULL, sf);
8273d19cdaeSstevel 
8283d19cdaeSstevel 		/*
8293d19cdaeSstevel 		 * call transport routine to force loop initialization
8303d19cdaeSstevel 		 */
8313d19cdaeSstevel 		(void) soc_force_lip(sf->sf_sochandle, sf->sf_socp,
8323d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, 0, FCAL_NO_LIP);
833d3d50737SRafael Vanoni 		sf->sf_reset_time = ddi_get_lbolt64();
8343d19cdaeSstevel 		return (DDI_SUCCESS);
8353d19cdaeSstevel 
8363d19cdaeSstevel 	default:
8373d19cdaeSstevel 		return (DDI_FAILURE);
8383d19cdaeSstevel 	}
8393d19cdaeSstevel 
8403d19cdaeSstevel fail:
8413d19cdaeSstevel 	cmn_err(CE_WARN, "sf%d: failed to attach", instance);
8423d19cdaeSstevel 
8433d19cdaeSstevel 	/*
8443d19cdaeSstevel 	 * Unbind and free event set
8453d19cdaeSstevel 	 */
8463d19cdaeSstevel 	if (sf->sf_event_hdl) {
8473d19cdaeSstevel 		(void) ndi_event_unbind_set(sf->sf_event_hdl,
8483d19cdaeSstevel 		    &sf->sf_events, NDI_SLEEP);
8493d19cdaeSstevel 		(void) ndi_event_free_hdl(sf->sf_event_hdl);
8503d19cdaeSstevel 	}
8513d19cdaeSstevel 
8523d19cdaeSstevel 	if (sf->sf_event_defs) {
8533d19cdaeSstevel 		kmem_free(sf->sf_event_defs, sizeof (sf_event_defs));
8543d19cdaeSstevel 	}
8553d19cdaeSstevel 
8563d19cdaeSstevel 	if (sf->sf_tran != NULL) {
8573d19cdaeSstevel 		scsi_hba_tran_free(sf->sf_tran);
8583d19cdaeSstevel 	}
8593d19cdaeSstevel 	while (sf->sf_cr_pool != NULL) {
8603d19cdaeSstevel 		sf_crpool_free(sf);
8613d19cdaeSstevel 	}
8623d19cdaeSstevel 	if (sf->sf_lilp_dmahandle != NULL) {
8633d19cdaeSstevel 		if (handle_bound) {
8643d19cdaeSstevel 			(void) ddi_dma_unbind_handle(sf->sf_lilp_dmahandle);
8653d19cdaeSstevel 		}
8663d19cdaeSstevel 		ddi_dma_free_handle(&sf->sf_lilp_dmahandle);
8673d19cdaeSstevel 	}
8683d19cdaeSstevel 	if (sf->sf_pkt_cache != NULL) {
8693d19cdaeSstevel 		kmem_cache_destroy(sf->sf_pkt_cache);
8703d19cdaeSstevel 	}
8713d19cdaeSstevel 	if (sf->sf_lilp_map != NULL) {
8723d19cdaeSstevel 		ddi_dma_mem_free(&sf->sf_lilp_acchandle);
8733d19cdaeSstevel 	}
8743d19cdaeSstevel 	if (sf->sf_ksp != NULL) {
8753d19cdaeSstevel 		kstat_delete(sf->sf_ksp);
8763d19cdaeSstevel 	}
8773d19cdaeSstevel 	if (mutex_initted) {
8783d19cdaeSstevel 		mutex_destroy(&sf->sf_mutex);
8793d19cdaeSstevel 		mutex_destroy(&sf->sf_cmd_mutex);
8803d19cdaeSstevel 		mutex_destroy(&sf->sf_cr_mutex);
8813d19cdaeSstevel 		mutex_destroy(&sf->sf_hp_daemon_mutex);
8823d19cdaeSstevel 		cv_destroy(&sf->sf_cr_cv);
8833d19cdaeSstevel 		cv_destroy(&sf->sf_hp_daemon_cv);
8843d19cdaeSstevel 	}
8853d19cdaeSstevel 	mutex_enter(&sf_global_mutex);
8863d19cdaeSstevel 
8873d19cdaeSstevel 	/*
8883d19cdaeSstevel 	 * kill off the watchdog if we are the last instance
8893d19cdaeSstevel 	 */
8903d19cdaeSstevel 	if (!--sf_watchdog_init) {
8913d19cdaeSstevel 		timeout_id_t tid = sf_watchdog_id;
8923d19cdaeSstevel 		mutex_exit(&sf_global_mutex);
8933d19cdaeSstevel 		(void) untimeout(tid);
8943d19cdaeSstevel 	} else {
8953d19cdaeSstevel 		mutex_exit(&sf_global_mutex);
8963d19cdaeSstevel 	}
8973d19cdaeSstevel 
8983d19cdaeSstevel 	ddi_soft_state_free(sf_state, instance);
8993d19cdaeSstevel 
9003d19cdaeSstevel 	if (tran != NULL) {
9013d19cdaeSstevel 		/* remove all minor nodes */
9023d19cdaeSstevel 		ddi_remove_minor_node(dip, NULL);
9033d19cdaeSstevel 	}
9043d19cdaeSstevel 
9053d19cdaeSstevel 	return (DDI_FAILURE);
9063d19cdaeSstevel }
9073d19cdaeSstevel 
9083d19cdaeSstevel 
9093d19cdaeSstevel /* ARGSUSED */
9103d19cdaeSstevel static int
9113d19cdaeSstevel sf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
9123d19cdaeSstevel {
9133d19cdaeSstevel 	struct sf		*sf;
9143d19cdaeSstevel 	int			instance;
9153d19cdaeSstevel 	int			i;
9163d19cdaeSstevel 	struct sf_target	*target;
9173d19cdaeSstevel 	timeout_id_t		tid;
9183d19cdaeSstevel 
9193d19cdaeSstevel 
9203d19cdaeSstevel 
9213d19cdaeSstevel 	/* NO OTHER THREADS ARE RUNNING */
9223d19cdaeSstevel 
9233d19cdaeSstevel 	instance = ddi_get_instance(dip);
9243d19cdaeSstevel 
9253d19cdaeSstevel 	if ((sf = ddi_get_soft_state(sf_state, instance)) == NULL) {
9263d19cdaeSstevel 		cmn_err(CE_WARN, "sf_detach, sf%d: bad soft state", instance);
9273d19cdaeSstevel 		return (DDI_FAILURE);
9283d19cdaeSstevel 	}
9293d19cdaeSstevel 
9303d19cdaeSstevel 	switch (cmd) {
9313d19cdaeSstevel 
9323d19cdaeSstevel 	case DDI_SUSPEND:
9333d19cdaeSstevel 		/*
9343d19cdaeSstevel 		 * suspend our instance
9353d19cdaeSstevel 		 */
9363d19cdaeSstevel 
9373d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT,
9383d19cdaeSstevel 		    "sf_detach: DDI_SUSPEND for sf%d\n", instance));
9393d19cdaeSstevel 		/*
9403d19cdaeSstevel 		 * There is a race condition in socal where while doing
9413d19cdaeSstevel 		 * callbacks if a ULP removes it self from the callback list
9423d19cdaeSstevel 		 * the for loop in socal may panic as cblist is junk and
9433d19cdaeSstevel 		 * while trying to get cblist->next the system will panic.
9443d19cdaeSstevel 		 */
9453d19cdaeSstevel 
9463d19cdaeSstevel 		/* call transport to remove our unregister our callbacks */
9473d19cdaeSstevel 		soc_remove_ulp(sf->sf_sochandle, sf->sf_socp,
9483d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, TYPE_SCSI_FCP, sf);
9493d19cdaeSstevel 
9503d19cdaeSstevel 		/*
9513d19cdaeSstevel 		 * begin process of clearing outstanding commands
9523d19cdaeSstevel 		 * by issuing a lip
9533d19cdaeSstevel 		 */
9543d19cdaeSstevel 		sf_force_lip(sf);
9553d19cdaeSstevel 
9563d19cdaeSstevel 		/*
9573d19cdaeSstevel 		 * toggle the device OFFLINE in order to cause
9583d19cdaeSstevel 		 * outstanding commands to drain
9593d19cdaeSstevel 		 */
9603d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
9613d19cdaeSstevel 		sf->sf_lip_cnt++;
9623d19cdaeSstevel 		sf->sf_timer = sf_watchdog_time + SF_OFFLINE_TIMEOUT;
9633d19cdaeSstevel 		sf->sf_state = (SF_STATE_OFFLINE | SF_STATE_SUSPENDED);
9643d19cdaeSstevel 		for (i = 0; i < sf_max_targets; i++) {
9653d19cdaeSstevel 			target = sf->sf_targets[i];
9663d19cdaeSstevel 			if (target != NULL) {
9673d19cdaeSstevel 				struct sf_target *ntarget;
9683d19cdaeSstevel 
9693d19cdaeSstevel 				mutex_enter(&target->sft_mutex);
9703d19cdaeSstevel 				if (!(target->sft_state & SF_TARGET_OFFLINE)) {
9713d19cdaeSstevel 					target->sft_state |=
9723d19cdaeSstevel 					    (SF_TARGET_BUSY | SF_TARGET_MARK);
9733d19cdaeSstevel 				}
9743d19cdaeSstevel 				/* do this for all LUNs as well */
9753d19cdaeSstevel 				for (ntarget = target->sft_next_lun;
9763d19cdaeSstevel 				    ntarget;
9773d19cdaeSstevel 				    ntarget = ntarget->sft_next_lun) {
9783d19cdaeSstevel 					mutex_enter(&ntarget->sft_mutex);
9793d19cdaeSstevel 					if (!(ntarget->sft_state &
9803d19cdaeSstevel 					    SF_TARGET_OFFLINE)) {
9813d19cdaeSstevel 						ntarget->sft_state |=
9823d19cdaeSstevel 						    (SF_TARGET_BUSY |
9833d19cdaeSstevel 						    SF_TARGET_MARK);
9843d19cdaeSstevel 					}
9853d19cdaeSstevel 					mutex_exit(&ntarget->sft_mutex);
9863d19cdaeSstevel 				}
9873d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
9883d19cdaeSstevel 			}
9893d19cdaeSstevel 		}
9903d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
9913d19cdaeSstevel 		mutex_enter(&sf_global_mutex);
9923d19cdaeSstevel 
9933d19cdaeSstevel 		/*
9943d19cdaeSstevel 		 * kill off the watchdog if we are the last instance
9953d19cdaeSstevel 		 */
9963d19cdaeSstevel 		if (!--sf_watchdog_init) {
9973d19cdaeSstevel 			tid = sf_watchdog_id;
9983d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
9993d19cdaeSstevel 			(void) untimeout(tid);
10003d19cdaeSstevel 		} else {
10013d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
10023d19cdaeSstevel 		}
10033d19cdaeSstevel 
10043d19cdaeSstevel 		return (DDI_SUCCESS);
10053d19cdaeSstevel 
10063d19cdaeSstevel 	case DDI_DETACH:
10073d19cdaeSstevel 		/*
10083d19cdaeSstevel 		 * detach this instance
10093d19cdaeSstevel 		 */
10103d19cdaeSstevel 
10113d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT,
10123d19cdaeSstevel 		    "sf_detach: DDI_DETACH for sf%d\n", instance));
10133d19cdaeSstevel 
10143d19cdaeSstevel 		/* remove this "sf" from the list of sf softstates */
10153d19cdaeSstevel 		sf_softstate_unlink(sf);
10163d19cdaeSstevel 
10173d19cdaeSstevel 		/*
10183d19cdaeSstevel 		 * prior to taking any DDI_DETACH actions, toggle the
10193d19cdaeSstevel 		 * device OFFLINE in order to cause outstanding
10203d19cdaeSstevel 		 * commands to drain
10213d19cdaeSstevel 		 */
10223d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
10233d19cdaeSstevel 		sf->sf_lip_cnt++;
10243d19cdaeSstevel 		sf->sf_timer = sf_watchdog_time + SF_OFFLINE_TIMEOUT;
10253d19cdaeSstevel 		sf->sf_state = SF_STATE_OFFLINE;
10263d19cdaeSstevel 		for (i = 0; i < sf_max_targets; i++) {
10273d19cdaeSstevel 			target = sf->sf_targets[i];
10283d19cdaeSstevel 			if (target != NULL) {
10293d19cdaeSstevel 				struct sf_target *ntarget;
10303d19cdaeSstevel 
10313d19cdaeSstevel 				mutex_enter(&target->sft_mutex);
10323d19cdaeSstevel 				if (!(target->sft_state & SF_TARGET_OFFLINE)) {
10333d19cdaeSstevel 					target->sft_state |=
10343d19cdaeSstevel 					    (SF_TARGET_BUSY | SF_TARGET_MARK);
10353d19cdaeSstevel 				}
10363d19cdaeSstevel 				for (ntarget = target->sft_next_lun;
10373d19cdaeSstevel 				    ntarget;
10383d19cdaeSstevel 				    ntarget = ntarget->sft_next_lun) {
10393d19cdaeSstevel 					mutex_enter(&ntarget->sft_mutex);
10403d19cdaeSstevel 					if (!(ntarget->sft_state &
10413d19cdaeSstevel 					    SF_TARGET_OFFLINE)) {
10423d19cdaeSstevel 						ntarget->sft_state |=
10433d19cdaeSstevel 						    (SF_TARGET_BUSY |
10443d19cdaeSstevel 						    SF_TARGET_MARK);
10453d19cdaeSstevel 					}
10463d19cdaeSstevel 					mutex_exit(&ntarget->sft_mutex);
10473d19cdaeSstevel 				}
10483d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
10493d19cdaeSstevel 			}
10503d19cdaeSstevel 		}
10513d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
10523d19cdaeSstevel 
10533d19cdaeSstevel 		/* call transport to remove and unregister our callbacks */
10543d19cdaeSstevel 		soc_remove_ulp(sf->sf_sochandle, sf->sf_socp,
10553d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, TYPE_SCSI_FCP, sf);
10563d19cdaeSstevel 
10573d19cdaeSstevel 		/*
10583d19cdaeSstevel 		 * kill off the watchdog if we are the last instance
10593d19cdaeSstevel 		 */
10603d19cdaeSstevel 		mutex_enter(&sf_global_mutex);
10613d19cdaeSstevel 		if (!--sf_watchdog_init) {
10623d19cdaeSstevel 			tid = sf_watchdog_id;
10633d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
10643d19cdaeSstevel 			(void) untimeout(tid);
10653d19cdaeSstevel 		} else {
10663d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
10673d19cdaeSstevel 		}
10683d19cdaeSstevel 
10693d19cdaeSstevel 		/* signal sf_hp_daemon() to exit and wait for exit */
10703d19cdaeSstevel 		mutex_enter(&sf->sf_hp_daemon_mutex);
10713d19cdaeSstevel 		ASSERT(sf->sf_hp_tid);
10723d19cdaeSstevel 		sf->sf_hp_exit = 1;		/* flag exit */
10733d19cdaeSstevel 		cv_signal(&sf->sf_hp_daemon_cv);
10743d19cdaeSstevel 		mutex_exit(&sf->sf_hp_daemon_mutex);
10753d19cdaeSstevel 		thread_join(sf->sf_hp_tid);	/* wait for hotplug to exit */
10763d19cdaeSstevel 
10773d19cdaeSstevel 		/*
10783d19cdaeSstevel 		 * Unbind and free event set
10793d19cdaeSstevel 		 */
10803d19cdaeSstevel 		if (sf->sf_event_hdl) {
10813d19cdaeSstevel 			(void) ndi_event_unbind_set(sf->sf_event_hdl,
10823d19cdaeSstevel 			    &sf->sf_events, NDI_SLEEP);
10833d19cdaeSstevel 			(void) ndi_event_free_hdl(sf->sf_event_hdl);
10843d19cdaeSstevel 		}
10853d19cdaeSstevel 
10863d19cdaeSstevel 		if (sf->sf_event_defs) {
10873d19cdaeSstevel 			kmem_free(sf->sf_event_defs, sizeof (sf_event_defs));
10883d19cdaeSstevel 		}
10893d19cdaeSstevel 
10903d19cdaeSstevel 		/* detach this instance of the HBA driver */
1091c1374a13SSurya Prakki 		(void) scsi_hba_detach(dip);
10923d19cdaeSstevel 		scsi_hba_tran_free(sf->sf_tran);
10933d19cdaeSstevel 
10943d19cdaeSstevel 		/* deallocate/unbind DMA handle for lilp map */
10953d19cdaeSstevel 		if (sf->sf_lilp_map != NULL) {
10963d19cdaeSstevel 			(void) ddi_dma_unbind_handle(sf->sf_lilp_dmahandle);
10973d19cdaeSstevel 			if (sf->sf_lilp_dmahandle != NULL) {
10983d19cdaeSstevel 				ddi_dma_free_handle(&sf->sf_lilp_dmahandle);
10993d19cdaeSstevel 			}
11003d19cdaeSstevel 			ddi_dma_mem_free(&sf->sf_lilp_acchandle);
11013d19cdaeSstevel 		}
11023d19cdaeSstevel 
11033d19cdaeSstevel 		/*
11043d19cdaeSstevel 		 * the kmem cache must be destroyed before free'ing
11053d19cdaeSstevel 		 * up the crpools
11063d19cdaeSstevel 		 *
11073d19cdaeSstevel 		 * our finagle of "ntot" and "nfree"
11083d19cdaeSstevel 		 * causes an ASSERT failure in "sf_cr_free()"
11093d19cdaeSstevel 		 * if the kmem cache is free'd after invoking
11103d19cdaeSstevel 		 * "sf_crpool_free()".
11113d19cdaeSstevel 		 */
11123d19cdaeSstevel 		kmem_cache_destroy(sf->sf_pkt_cache);
11133d19cdaeSstevel 
11143d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT,
11153d19cdaeSstevel 		    "sf_detach: sf_crpool_free() for instance 0x%x\n",
11163d19cdaeSstevel 		    instance));
11173d19cdaeSstevel 		while (sf->sf_cr_pool != NULL) {
11183d19cdaeSstevel 			/*
11193d19cdaeSstevel 			 * set ntot to nfree for this particular entry
11203d19cdaeSstevel 			 *
11213d19cdaeSstevel 			 * this causes sf_crpool_free() to update
11223d19cdaeSstevel 			 * the cr_pool list when deallocating this entry
11233d19cdaeSstevel 			 */
11243d19cdaeSstevel 			sf->sf_cr_pool->ntot = sf->sf_cr_pool->nfree;
11253d19cdaeSstevel 			sf_crpool_free(sf);
11263d19cdaeSstevel 		}
11273d19cdaeSstevel 
11283d19cdaeSstevel 		/*
11293d19cdaeSstevel 		 * now that the cr_pool's are gone it's safe
11303d19cdaeSstevel 		 * to destroy all softstate mutex's and cv's
11313d19cdaeSstevel 		 */
11323d19cdaeSstevel 		mutex_destroy(&sf->sf_mutex);
11333d19cdaeSstevel 		mutex_destroy(&sf->sf_cmd_mutex);
11343d19cdaeSstevel 		mutex_destroy(&sf->sf_cr_mutex);
11353d19cdaeSstevel 		mutex_destroy(&sf->sf_hp_daemon_mutex);
11363d19cdaeSstevel 		cv_destroy(&sf->sf_cr_cv);
11373d19cdaeSstevel 		cv_destroy(&sf->sf_hp_daemon_cv);
11383d19cdaeSstevel 
11393d19cdaeSstevel 		/* remove all minor nodes from the device tree */
11403d19cdaeSstevel 		ddi_remove_minor_node(dip, NULL);
11413d19cdaeSstevel 
11423d19cdaeSstevel 		/* remove properties created during attach() */
11433d19cdaeSstevel 		ddi_prop_remove_all(dip);
11443d19cdaeSstevel 
11453d19cdaeSstevel 		/* remove kstat's if present */
11463d19cdaeSstevel 		if (sf->sf_ksp != NULL) {
11473d19cdaeSstevel 			kstat_delete(sf->sf_ksp);
11483d19cdaeSstevel 		}
11493d19cdaeSstevel 
11503d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT,
11513d19cdaeSstevel 		    "sf_detach: ddi_soft_state_free() for instance 0x%x\n",
11523d19cdaeSstevel 		    instance));
11533d19cdaeSstevel 		ddi_soft_state_free(sf_state, instance);
11543d19cdaeSstevel 		return (DDI_SUCCESS);
11553d19cdaeSstevel 
11563d19cdaeSstevel 	default:
11573d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT, "sf_detach: sf%d unknown cmd %x\n",
11583d19cdaeSstevel 		    instance, (int)cmd));
11593d19cdaeSstevel 		return (DDI_FAILURE);
11603d19cdaeSstevel 	}
11613d19cdaeSstevel }
11623d19cdaeSstevel 
11633d19cdaeSstevel 
11643d19cdaeSstevel /*
11653d19cdaeSstevel  * sf_softstate_unlink() - remove an sf instance from the list of softstates
11663d19cdaeSstevel  */
11673d19cdaeSstevel static void
11683d19cdaeSstevel sf_softstate_unlink(struct sf *sf)
11693d19cdaeSstevel {
11703d19cdaeSstevel 	struct sf	*sf_ptr;
11713d19cdaeSstevel 	struct sf	*sf_found_sibling;
11723d19cdaeSstevel 	struct sf	*sf_reposition = NULL;
11733d19cdaeSstevel 
11743d19cdaeSstevel 
11753d19cdaeSstevel 	mutex_enter(&sf_global_mutex);
11763d19cdaeSstevel 	while (sf_watch_running) {
11773d19cdaeSstevel 		/* Busy working the list -- wait */
11783d19cdaeSstevel 		cv_wait(&sf_watch_cv, &sf_global_mutex);
11793d19cdaeSstevel 	}
11803d19cdaeSstevel 	if ((sf_found_sibling = sf->sf_sibling) != NULL) {
11813d19cdaeSstevel 		/*
11823d19cdaeSstevel 		 * we have a sibling so NULL out its reference to us
11833d19cdaeSstevel 		 */
11843d19cdaeSstevel 		mutex_enter(&sf_found_sibling->sf_mutex);
11853d19cdaeSstevel 		sf_found_sibling->sf_sibling = NULL;
11863d19cdaeSstevel 		mutex_exit(&sf_found_sibling->sf_mutex);
11873d19cdaeSstevel 	}
11883d19cdaeSstevel 
11893d19cdaeSstevel 	/* remove our instance from the global list */
11903d19cdaeSstevel 	if (sf == sf_head) {
11913d19cdaeSstevel 		/* we were at at head of the list */
11923d19cdaeSstevel 		sf_head = sf->sf_next;
11933d19cdaeSstevel 	} else {
11943d19cdaeSstevel 		/* find us in the list */
11953d19cdaeSstevel 		for (sf_ptr = sf_head;
11963d19cdaeSstevel 		    sf_ptr != NULL;
11973d19cdaeSstevel 		    sf_ptr = sf_ptr->sf_next) {
11983d19cdaeSstevel 			if (sf_ptr == sf) {
11993d19cdaeSstevel 				break;
12003d19cdaeSstevel 			}
12013d19cdaeSstevel 			/* remember this place */
12023d19cdaeSstevel 			sf_reposition = sf_ptr;
12033d19cdaeSstevel 		}
12043d19cdaeSstevel 		ASSERT(sf_ptr == sf);
12053d19cdaeSstevel 		ASSERT(sf_reposition != NULL);
12063d19cdaeSstevel 
12073d19cdaeSstevel 		sf_reposition->sf_next = sf_ptr->sf_next;
12083d19cdaeSstevel 	}
12093d19cdaeSstevel 	mutex_exit(&sf_global_mutex);
12103d19cdaeSstevel }
12113d19cdaeSstevel 
12123d19cdaeSstevel 
12133d19cdaeSstevel static int
12143d19cdaeSstevel sf_scsi_bus_config(dev_info_t *parent, uint_t flag,
12153d19cdaeSstevel     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
12163d19cdaeSstevel {
12173d19cdaeSstevel 	int64_t		reset_delay;
12183d19cdaeSstevel 	struct sf	*sf;
12193d19cdaeSstevel 
12203d19cdaeSstevel 	sf = ddi_get_soft_state(sf_state, ddi_get_instance(parent));
12213d19cdaeSstevel 	ASSERT(sf);
12223d19cdaeSstevel 
12233d19cdaeSstevel 	reset_delay = (int64_t)(USEC_TO_TICK(SF_INIT_WAIT_TIMEOUT)) -
1224d3d50737SRafael Vanoni 	    (ddi_get_lbolt64() - sf->sf_reset_time);
12253d19cdaeSstevel 	if (reset_delay < 0)
12263d19cdaeSstevel 		reset_delay = 0;
12273d19cdaeSstevel 
12283d19cdaeSstevel 	if (sf_bus_config_debug)
12293d19cdaeSstevel 		flag |= NDI_DEVI_DEBUG;
12303d19cdaeSstevel 
12313d19cdaeSstevel 	return (ndi_busop_bus_config(parent, flag, op,
12323d19cdaeSstevel 	    arg, childp, (clock_t)reset_delay));
12333d19cdaeSstevel }
12343d19cdaeSstevel 
12353d19cdaeSstevel static int
12363d19cdaeSstevel sf_scsi_bus_unconfig(dev_info_t *parent, uint_t flag,
12373d19cdaeSstevel     ddi_bus_config_op_t op, void *arg)
12383d19cdaeSstevel {
12393d19cdaeSstevel 	if (sf_bus_config_debug)
12403d19cdaeSstevel 		flag |= NDI_DEVI_DEBUG;
12413d19cdaeSstevel 
12423d19cdaeSstevel 	return (ndi_busop_bus_unconfig(parent, flag, op, arg));
12433d19cdaeSstevel }
12443d19cdaeSstevel 
12453d19cdaeSstevel 
12463d19cdaeSstevel /*
12473d19cdaeSstevel  * called by transport to initialize a SCSI target
12483d19cdaeSstevel  */
12493d19cdaeSstevel /* ARGSUSED */
12503d19cdaeSstevel static int
12513d19cdaeSstevel sf_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
12523d19cdaeSstevel     scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
12533d19cdaeSstevel {
12543d19cdaeSstevel #ifdef RAID_LUNS
12553d19cdaeSstevel 	int lun;
12563d19cdaeSstevel #else
12573d19cdaeSstevel 	int64_t lun;
12583d19cdaeSstevel #endif
12593d19cdaeSstevel 	struct sf_target *target;
12603d19cdaeSstevel 	struct sf *sf = (struct sf *)hba_tran->tran_hba_private;
12613d19cdaeSstevel 	int i, t_len;
12623d19cdaeSstevel 	unsigned int lip_cnt;
12633d19cdaeSstevel 	unsigned char wwn[FC_WWN_SIZE];
12643d19cdaeSstevel 
12653d19cdaeSstevel 
12663d19cdaeSstevel 	/* get and validate our SCSI target ID */
12673d19cdaeSstevel 	i = sd->sd_address.a_target;
12683d19cdaeSstevel 	if (i >= sf_max_targets) {
12693d19cdaeSstevel 		return (DDI_NOT_WELL_FORMED);
12703d19cdaeSstevel 	}
12713d19cdaeSstevel 
12723d19cdaeSstevel 	/* get our port WWN property */
12733d19cdaeSstevel 	t_len = sizeof (wwn);
12743d19cdaeSstevel 	if (ddi_prop_op(DDI_DEV_T_ANY, tgt_dip, PROP_LEN_AND_VAL_BUF,
12753d19cdaeSstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, PORT_WWN_PROP,
12763d19cdaeSstevel 	    (caddr_t)&wwn, &t_len) != DDI_SUCCESS) {
12773d19cdaeSstevel 		/* no port WWN property - ignore the OBP stub node */
12783d19cdaeSstevel 		return (DDI_NOT_WELL_FORMED);
12793d19cdaeSstevel 	}
12803d19cdaeSstevel 
12813d19cdaeSstevel 	/* get our LIP count property */
12823d19cdaeSstevel 	t_len = sizeof (lip_cnt);
12833d19cdaeSstevel 	if (ddi_prop_op(DDI_DEV_T_ANY, tgt_dip, PROP_LEN_AND_VAL_BUF,
12843d19cdaeSstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, LIP_CNT_PROP,
12853d19cdaeSstevel 	    (caddr_t)&lip_cnt, &t_len) != DDI_SUCCESS) {
12863d19cdaeSstevel 		return (DDI_FAILURE);
12873d19cdaeSstevel 	}
12883d19cdaeSstevel 	/* and our LUN property */
12893d19cdaeSstevel 	t_len = sizeof (lun);
12903d19cdaeSstevel 	if (ddi_prop_op(DDI_DEV_T_ANY, tgt_dip, PROP_LEN_AND_VAL_BUF,
12913d19cdaeSstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "lun",
12923d19cdaeSstevel 	    (caddr_t)&lun, &t_len) != DDI_SUCCESS) {
12933d19cdaeSstevel 		return (DDI_FAILURE);
12943d19cdaeSstevel 	}
12953d19cdaeSstevel 
12963d19cdaeSstevel 	/* find the target structure for this instance */
12973d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
12983d19cdaeSstevel 	if ((target = sf_lookup_target(sf, wwn, lun)) == NULL) {
12993d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
13003d19cdaeSstevel 		return (DDI_FAILURE);
13013d19cdaeSstevel 	}
13023d19cdaeSstevel 
13033d19cdaeSstevel 	mutex_enter(&target->sft_mutex);
13043d19cdaeSstevel 	if ((sf->sf_lip_cnt == lip_cnt) && !(target->sft_state
13053d19cdaeSstevel 	    & SF_TARGET_INIT_DONE)) {
13063d19cdaeSstevel 		/*
13073d19cdaeSstevel 		 * set links between HBA transport and target structures
13083d19cdaeSstevel 		 * and set done flag
13093d19cdaeSstevel 		 */
13103d19cdaeSstevel 		hba_tran->tran_tgt_private = target;
13113d19cdaeSstevel 		target->sft_tran = hba_tran;
13123d19cdaeSstevel 		target->sft_state |= SF_TARGET_INIT_DONE;
13133d19cdaeSstevel 	} else {
13143d19cdaeSstevel 		/* already initialized ?? */
13153d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
13163d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
13173d19cdaeSstevel 		return (DDI_FAILURE);
13183d19cdaeSstevel 	}
13193d19cdaeSstevel 	mutex_exit(&target->sft_mutex);
13203d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
13213d19cdaeSstevel 
13223d19cdaeSstevel 	return (DDI_SUCCESS);
13233d19cdaeSstevel }
13243d19cdaeSstevel 
13253d19cdaeSstevel 
13263d19cdaeSstevel /*
13273d19cdaeSstevel  * called by transport to free a target
13283d19cdaeSstevel  */
13293d19cdaeSstevel /* ARGSUSED */
13303d19cdaeSstevel static void
13313d19cdaeSstevel sf_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
13323d19cdaeSstevel     scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
13333d19cdaeSstevel {
13343d19cdaeSstevel 	struct sf_target *target = hba_tran->tran_tgt_private;
13353d19cdaeSstevel 
13363d19cdaeSstevel 	if (target != NULL) {
13373d19cdaeSstevel 		mutex_enter(&target->sft_mutex);
13383d19cdaeSstevel 		target->sft_tran = NULL;
13393d19cdaeSstevel 		target->sft_state &= ~SF_TARGET_INIT_DONE;
13403d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
13413d19cdaeSstevel 	}
13423d19cdaeSstevel }
13433d19cdaeSstevel 
13443d19cdaeSstevel 
13453d19cdaeSstevel /*
13463d19cdaeSstevel  * allocator for non-std size cdb/pkt_private/status -- return TRUE iff
13473d19cdaeSstevel  * success, else return FALSE
13483d19cdaeSstevel  */
13493d19cdaeSstevel /*ARGSUSED*/
13503d19cdaeSstevel static int
13513d19cdaeSstevel sf_pkt_alloc_extern(struct sf *sf, struct sf_pkt *cmd,
13523d19cdaeSstevel     int tgtlen, int statuslen, int kf)
13533d19cdaeSstevel {
13543d19cdaeSstevel 	caddr_t scbp, tgt;
13553d19cdaeSstevel 	int failure = FALSE;
13563d19cdaeSstevel 	struct scsi_pkt *pkt = CMD2PKT(cmd);
13573d19cdaeSstevel 
13583d19cdaeSstevel 
13593d19cdaeSstevel 	tgt = scbp = NULL;
13603d19cdaeSstevel 
13613d19cdaeSstevel 	if (tgtlen > PKT_PRIV_LEN) {
13623d19cdaeSstevel 		if ((tgt = kmem_zalloc(tgtlen, kf)) == NULL) {
13633d19cdaeSstevel 			failure = TRUE;
13643d19cdaeSstevel 		} else {
13653d19cdaeSstevel 			cmd->cmd_flags |= CFLAG_PRIVEXTERN;
13663d19cdaeSstevel 			pkt->pkt_private = tgt;
13673d19cdaeSstevel 		}
13683d19cdaeSstevel 	}
13693d19cdaeSstevel 	if (statuslen > EXTCMDS_STATUS_SIZE) {
13703d19cdaeSstevel 		if ((scbp = kmem_zalloc((size_t)statuslen, kf)) == NULL) {
13713d19cdaeSstevel 			failure = TRUE;
13723d19cdaeSstevel 		} else {
13733d19cdaeSstevel 			cmd->cmd_flags |= CFLAG_SCBEXTERN;
13743d19cdaeSstevel 			pkt->pkt_scbp = (opaque_t)scbp;
13753d19cdaeSstevel 		}
13763d19cdaeSstevel 	}
13773d19cdaeSstevel 	if (failure) {
13783d19cdaeSstevel 		sf_pkt_destroy_extern(sf, cmd);
13793d19cdaeSstevel 	}
13803d19cdaeSstevel 	return (failure);
13813d19cdaeSstevel }
13823d19cdaeSstevel 
13833d19cdaeSstevel 
13843d19cdaeSstevel /*
13853d19cdaeSstevel  * deallocator for non-std size cdb/pkt_private/status
13863d19cdaeSstevel  */
13873d19cdaeSstevel static void
13883d19cdaeSstevel sf_pkt_destroy_extern(struct sf *sf, struct sf_pkt *cmd)
13893d19cdaeSstevel {
13903d19cdaeSstevel 	struct scsi_pkt *pkt = CMD2PKT(cmd);
13913d19cdaeSstevel 
13923d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_FREE) {
13933d19cdaeSstevel 		cmn_err(CE_PANIC,
13943d19cdaeSstevel 		    "sf_scsi_impl_pktfree: freeing free packet");
13953d19cdaeSstevel 		_NOTE(NOT_REACHED)
13963d19cdaeSstevel 		/* NOTREACHED */
13973d19cdaeSstevel 	}
13983d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_SCBEXTERN) {
13993d19cdaeSstevel 		kmem_free((caddr_t)pkt->pkt_scbp,
14003d19cdaeSstevel 		    (size_t)cmd->cmd_scblen);
14013d19cdaeSstevel 	}
14023d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_PRIVEXTERN) {
14033d19cdaeSstevel 		kmem_free((caddr_t)pkt->pkt_private,
14043d19cdaeSstevel 		    (size_t)cmd->cmd_privlen);
14053d19cdaeSstevel 	}
14063d19cdaeSstevel 
14073d19cdaeSstevel 	cmd->cmd_flags = CFLAG_FREE;
14083d19cdaeSstevel 	kmem_cache_free(sf->sf_pkt_cache, (void *)cmd);
14093d19cdaeSstevel }
14103d19cdaeSstevel 
14113d19cdaeSstevel 
14123d19cdaeSstevel /*
14133d19cdaeSstevel  * create or initialize a SCSI packet -- called internally and
14143d19cdaeSstevel  * by the transport
14153d19cdaeSstevel  */
14163d19cdaeSstevel static struct scsi_pkt *
14173d19cdaeSstevel sf_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
14183d19cdaeSstevel     struct buf *bp, int cmdlen, int statuslen, int tgtlen,
14193d19cdaeSstevel     int flags, int (*callback)(), caddr_t arg)
14203d19cdaeSstevel {
14213d19cdaeSstevel 	int kf;
14223d19cdaeSstevel 	int failure = FALSE;
14233d19cdaeSstevel 	struct sf_pkt *cmd;
14243d19cdaeSstevel 	struct sf *sf = ADDR2SF(ap);
14253d19cdaeSstevel 	struct sf_target *target = ADDR2TARGET(ap);
14263d19cdaeSstevel 	struct sf_pkt	*new_cmd = NULL;
14273d19cdaeSstevel 	struct fcal_packet	*fpkt;
14283d19cdaeSstevel 	fc_frame_header_t	*hp;
14293d19cdaeSstevel 	struct fcp_cmd *fcmd;
14303d19cdaeSstevel 
14313d19cdaeSstevel 
14323d19cdaeSstevel 	/*
14333d19cdaeSstevel 	 * If we've already allocated a pkt once,
14343d19cdaeSstevel 	 * this request is for dma allocation only.
14353d19cdaeSstevel 	 */
14363d19cdaeSstevel 	if (pkt == NULL) {
14373d19cdaeSstevel 
14383d19cdaeSstevel 		/*
14393d19cdaeSstevel 		 * First step of sf_scsi_init_pkt:  pkt allocation
14403d19cdaeSstevel 		 */
14413d19cdaeSstevel 		if (cmdlen > FCP_CDB_SIZE) {
14423d19cdaeSstevel 			return (NULL);
14433d19cdaeSstevel 		}
14443d19cdaeSstevel 
14453d19cdaeSstevel 		kf = (callback == SLEEP_FUNC)? KM_SLEEP: KM_NOSLEEP;
14463d19cdaeSstevel 
14473d19cdaeSstevel 		if ((cmd = kmem_cache_alloc(sf->sf_pkt_cache, kf)) != NULL) {
14483d19cdaeSstevel 			/*
14493d19cdaeSstevel 			 * Selective zeroing of the pkt.
14503d19cdaeSstevel 			 */
14513d19cdaeSstevel 
14523d19cdaeSstevel 			cmd->cmd_flags = 0;
14533d19cdaeSstevel 			cmd->cmd_forw = 0;
14543d19cdaeSstevel 			cmd->cmd_back = 0;
14553d19cdaeSstevel 			cmd->cmd_next = 0;
14563d19cdaeSstevel 			cmd->cmd_pkt = (struct scsi_pkt *)((char *)cmd +
14573d19cdaeSstevel 			    sizeof (struct sf_pkt) + sizeof (struct
14583d19cdaeSstevel 			    fcal_packet));
14593d19cdaeSstevel 			cmd->cmd_fp_pkt = (struct fcal_packet *)((char *)cmd +
14603d19cdaeSstevel 			    sizeof (struct sf_pkt));
14613d19cdaeSstevel 			cmd->cmd_fp_pkt->fcal_pkt_private = (opaque_t)cmd;
14623d19cdaeSstevel 			cmd->cmd_state = SF_STATE_IDLE;
14633d19cdaeSstevel 			cmd->cmd_pkt->pkt_ha_private = (opaque_t)cmd;
14643d19cdaeSstevel 			cmd->cmd_pkt->pkt_scbp = (opaque_t)cmd->cmd_scsi_scb;
14653d19cdaeSstevel 			cmd->cmd_pkt->pkt_comp	= NULL;
14663d19cdaeSstevel 			cmd->cmd_pkt->pkt_flags	= 0;
14673d19cdaeSstevel 			cmd->cmd_pkt->pkt_time	= 0;
14683d19cdaeSstevel 			cmd->cmd_pkt->pkt_resid	= 0;
14693d19cdaeSstevel 			cmd->cmd_pkt->pkt_reason = 0;
14703d19cdaeSstevel 			cmd->cmd_cdblen = (uchar_t)cmdlen;
14713d19cdaeSstevel 			cmd->cmd_scblen		= statuslen;
14723d19cdaeSstevel 			cmd->cmd_privlen	= tgtlen;
14733d19cdaeSstevel 			cmd->cmd_pkt->pkt_address = *ap;
14743d19cdaeSstevel 
14753d19cdaeSstevel 			/* zero pkt_private */
14763d19cdaeSstevel 			(int *)(cmd->cmd_pkt->pkt_private =
14773d19cdaeSstevel 			    cmd->cmd_pkt_private);
14783d19cdaeSstevel 			bzero((caddr_t)cmd->cmd_pkt->pkt_private,
14793d19cdaeSstevel 			    PKT_PRIV_LEN);
14803d19cdaeSstevel 		} else {
14813d19cdaeSstevel 			failure = TRUE;
14823d19cdaeSstevel 		}
14833d19cdaeSstevel 
14843d19cdaeSstevel 		if (failure ||
14853d19cdaeSstevel 		    (tgtlen > PKT_PRIV_LEN) ||
14863d19cdaeSstevel 		    (statuslen > EXTCMDS_STATUS_SIZE)) {
14873d19cdaeSstevel 			if (!failure) {
14883d19cdaeSstevel 				/* need to allocate more space */
14893d19cdaeSstevel 				failure = sf_pkt_alloc_extern(sf, cmd,
14903d19cdaeSstevel 				    tgtlen, statuslen, kf);
14913d19cdaeSstevel 			}
14923d19cdaeSstevel 			if (failure) {
14933d19cdaeSstevel 				return (NULL);
14943d19cdaeSstevel 			}
14953d19cdaeSstevel 		}
14963d19cdaeSstevel 
14973d19cdaeSstevel 		fpkt = cmd->cmd_fp_pkt;
14983d19cdaeSstevel 		if (cmd->cmd_block == NULL) {
14993d19cdaeSstevel 
15003d19cdaeSstevel 			/* allocate cmd/response pool buffers */
15013d19cdaeSstevel 			if (sf_cr_alloc(sf, cmd, callback) == DDI_FAILURE) {
15023d19cdaeSstevel 				sf_pkt_destroy_extern(sf, cmd);
15033d19cdaeSstevel 				return (NULL);
15043d19cdaeSstevel 			}
15053d19cdaeSstevel 
15063d19cdaeSstevel 			/* fill in the FC-AL packet */
15073d19cdaeSstevel 			fpkt->fcal_pkt_cookie = sf->sf_socp;
15083d19cdaeSstevel 			fpkt->fcal_pkt_comp = sf_cmd_callback;
15093d19cdaeSstevel 			fpkt->fcal_pkt_flags = 0;
15103d19cdaeSstevel 			fpkt->fcal_magic = FCALP_MAGIC;
15113d19cdaeSstevel 			fpkt->fcal_socal_request.sr_soc_hdr.sh_flags =
15123d19cdaeSstevel 			    (ushort_t)(SOC_FC_HEADER |
15133d19cdaeSstevel 			    sf->sf_sochandle->fcal_portno);
15143d19cdaeSstevel 			fpkt->fcal_socal_request.sr_soc_hdr.sh_class = 3;
15153d19cdaeSstevel 			fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_count = 1;
15163d19cdaeSstevel 			fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_flags = 0;
15173d19cdaeSstevel 			fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_seqno = 0;
15183d19cdaeSstevel 			fpkt->fcal_socal_request.sr_dataseg[0].fc_base =
15193d19cdaeSstevel 			    (uint32_t)cmd->cmd_dmac;
15203d19cdaeSstevel 			fpkt->fcal_socal_request.sr_dataseg[0].fc_count =
15213d19cdaeSstevel 			    sizeof (struct fcp_cmd);
15223d19cdaeSstevel 			fpkt->fcal_socal_request.sr_dataseg[1].fc_base =
15233d19cdaeSstevel 			    (uint32_t)cmd->cmd_rsp_dmac;
15243d19cdaeSstevel 			fpkt->fcal_socal_request.sr_dataseg[1].fc_count =
15253d19cdaeSstevel 			    FCP_MAX_RSP_IU_SIZE;
15263d19cdaeSstevel 
15273d19cdaeSstevel 			/* Fill in the Fabric Channel Header */
15283d19cdaeSstevel 			hp = &fpkt->fcal_socal_request.sr_fc_frame_hdr;
15293d19cdaeSstevel 			hp->r_ctl = R_CTL_COMMAND;
15303d19cdaeSstevel 			hp->type = TYPE_SCSI_FCP;
15313d19cdaeSstevel 			hp->f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
15323d19cdaeSstevel 			hp->reserved1 = 0;
15333d19cdaeSstevel 			hp->seq_id = 0;
15343d19cdaeSstevel 			hp->df_ctl  = 0;
15353d19cdaeSstevel 			hp->seq_cnt = 0;
15363d19cdaeSstevel 			hp->ox_id = 0xffff;
15373d19cdaeSstevel 			hp->rx_id = 0xffff;
15383d19cdaeSstevel 			hp->ro = 0;
15393d19cdaeSstevel 
15403d19cdaeSstevel 			/* Establish the LUN */
15413d19cdaeSstevel 			bcopy((caddr_t)&target->sft_lun.b,
15423d19cdaeSstevel 			    (caddr_t)&cmd->cmd_block->fcp_ent_addr,
15433d19cdaeSstevel 			    FCP_LUN_SIZE);
15443d19cdaeSstevel 			*((int32_t *)&cmd->cmd_block->fcp_cntl) = 0;
15453d19cdaeSstevel 		}
15463d19cdaeSstevel 		cmd->cmd_pkt->pkt_cdbp = cmd->cmd_block->fcp_cdb;
15473d19cdaeSstevel 
15483d19cdaeSstevel 		mutex_enter(&target->sft_pkt_mutex);
15493d19cdaeSstevel 
15503d19cdaeSstevel 		target->sft_pkt_tail->cmd_forw = cmd;
15513d19cdaeSstevel 		cmd->cmd_back = target->sft_pkt_tail;
15523d19cdaeSstevel 		cmd->cmd_forw = (struct sf_pkt *)&target->sft_pkt_head;
15533d19cdaeSstevel 		target->sft_pkt_tail = cmd;
15543d19cdaeSstevel 
15553d19cdaeSstevel 		mutex_exit(&target->sft_pkt_mutex);
15563d19cdaeSstevel 		new_cmd = cmd;		/* for later cleanup if needed */
15573d19cdaeSstevel 	} else {
15583d19cdaeSstevel 		/* pkt already exists -- just a request for DMA allocation */
1559602ca9eaScth 		cmd = PKT2CMD(pkt);
15603d19cdaeSstevel 		fpkt = cmd->cmd_fp_pkt;
15613d19cdaeSstevel 	}
15623d19cdaeSstevel 
15633d19cdaeSstevel 	/* zero cdb (bzero is too slow) */
15643d19cdaeSstevel 	bzero((caddr_t)cmd->cmd_pkt->pkt_cdbp, cmdlen);
15653d19cdaeSstevel 
15663d19cdaeSstevel 	/*
15673d19cdaeSstevel 	 * Second step of sf_scsi_init_pkt:  dma allocation
15683d19cdaeSstevel 	 * Set up dma info
15693d19cdaeSstevel 	 */
15703d19cdaeSstevel 	if ((bp != NULL) && (bp->b_bcount != 0)) {
15713d19cdaeSstevel 		int cmd_flags, dma_flags;
15723d19cdaeSstevel 		int rval = 0;
15733d19cdaeSstevel 		uint_t dmacookie_count;
15743d19cdaeSstevel 
15753d19cdaeSstevel 		/* there is a buffer and some data to transfer */
15763d19cdaeSstevel 
15773d19cdaeSstevel 		/* set up command and DMA flags */
15783d19cdaeSstevel 		cmd_flags = cmd->cmd_flags;
15793d19cdaeSstevel 		if (bp->b_flags & B_READ) {
15803d19cdaeSstevel 			/* a read */
15813d19cdaeSstevel 			cmd_flags &= ~CFLAG_DMASEND;
15823d19cdaeSstevel 			dma_flags = DDI_DMA_READ;
15833d19cdaeSstevel 		} else {
15843d19cdaeSstevel 			/* a write */
15853d19cdaeSstevel 			cmd_flags |= CFLAG_DMASEND;
15863d19cdaeSstevel 			dma_flags = DDI_DMA_WRITE;
15873d19cdaeSstevel 		}
15883d19cdaeSstevel 		if (flags & PKT_CONSISTENT) {
15893d19cdaeSstevel 			cmd_flags |= CFLAG_CMDIOPB;
15903d19cdaeSstevel 			dma_flags |= DDI_DMA_CONSISTENT;
15913d19cdaeSstevel 		}
15923d19cdaeSstevel 
15933d19cdaeSstevel 		/* ensure we have a DMA handle */
15943d19cdaeSstevel 		if (cmd->cmd_dmahandle == NULL) {
15953d19cdaeSstevel 			rval = ddi_dma_alloc_handle(sf->sf_dip,
15963d19cdaeSstevel 			    sf->sf_sochandle->fcal_dmaattr, callback, arg,
15973d19cdaeSstevel 			    &cmd->cmd_dmahandle);
15983d19cdaeSstevel 		}
15993d19cdaeSstevel 
16003d19cdaeSstevel 		if (rval == 0) {
16013d19cdaeSstevel 			/* bind our DMA handle to our buffer */
16023d19cdaeSstevel 			rval = ddi_dma_buf_bind_handle(cmd->cmd_dmahandle, bp,
16033d19cdaeSstevel 			    dma_flags, callback, arg, &cmd->cmd_dmacookie,
16043d19cdaeSstevel 			    &dmacookie_count);
16053d19cdaeSstevel 		}
16063d19cdaeSstevel 
16073d19cdaeSstevel 		if (rval != 0) {
16083d19cdaeSstevel 			/* DMA failure */
16093d19cdaeSstevel 			SF_DEBUG(2, (sf, CE_CONT, "ddi_dma_buf.. failed\n"));
16103d19cdaeSstevel 			switch (rval) {
16113d19cdaeSstevel 			case DDI_DMA_NORESOURCES:
16123d19cdaeSstevel 				bioerror(bp, 0);
16133d19cdaeSstevel 				break;
16143d19cdaeSstevel 			case DDI_DMA_BADATTR:
16153d19cdaeSstevel 			case DDI_DMA_NOMAPPING:
16163d19cdaeSstevel 				bioerror(bp, EFAULT);
16173d19cdaeSstevel 				break;
16183d19cdaeSstevel 			case DDI_DMA_TOOBIG:
16193d19cdaeSstevel 			default:
16203d19cdaeSstevel 				bioerror(bp, EINVAL);
16213d19cdaeSstevel 				break;
16223d19cdaeSstevel 			}
16233d19cdaeSstevel 			/* clear valid flag */
16243d19cdaeSstevel 			cmd->cmd_flags = cmd_flags & ~CFLAG_DMAVALID;
16253d19cdaeSstevel 			if (new_cmd != NULL) {
16263d19cdaeSstevel 				/* destroy packet if we just created it */
16273d19cdaeSstevel 				sf_scsi_destroy_pkt(ap, new_cmd->cmd_pkt);
16283d19cdaeSstevel 			}
16293d19cdaeSstevel 			return (NULL);
16303d19cdaeSstevel 		}
16313d19cdaeSstevel 
16323d19cdaeSstevel 		ASSERT(dmacookie_count == 1);
16333d19cdaeSstevel 		/* set up amt to transfer and set valid flag */
16343d19cdaeSstevel 		cmd->cmd_dmacount = bp->b_bcount;
16353d19cdaeSstevel 		cmd->cmd_flags = cmd_flags | CFLAG_DMAVALID;
16363d19cdaeSstevel 
16373d19cdaeSstevel 		ASSERT(cmd->cmd_dmahandle != NULL);
16383d19cdaeSstevel 	}
16393d19cdaeSstevel 
16403d19cdaeSstevel 	/* set up FC-AL packet */
16413d19cdaeSstevel 	fcmd = cmd->cmd_block;
16423d19cdaeSstevel 
16433d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_DMAVALID) {
16443d19cdaeSstevel 		if (cmd->cmd_flags & CFLAG_DMASEND) {
16453d19cdaeSstevel 			/* DMA write */
16463d19cdaeSstevel 			fcmd->fcp_cntl.cntl_read_data = 0;
16473d19cdaeSstevel 			fcmd->fcp_cntl.cntl_write_data = 1;
16483d19cdaeSstevel 			fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_type =
16493d19cdaeSstevel 			    CQ_TYPE_IO_WRITE;
16503d19cdaeSstevel 		} else {
16513d19cdaeSstevel 			/* DMA read */
16523d19cdaeSstevel 			fcmd->fcp_cntl.cntl_read_data = 1;
16533d19cdaeSstevel 			fcmd->fcp_cntl.cntl_write_data = 0;
16543d19cdaeSstevel 			fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_type =
16553d19cdaeSstevel 			    CQ_TYPE_IO_READ;
16563d19cdaeSstevel 		}
16573d19cdaeSstevel 		fpkt->fcal_socal_request.sr_dataseg[2].fc_base =
16583d19cdaeSstevel 		    (uint32_t)cmd->cmd_dmacookie.dmac_address;
16593d19cdaeSstevel 		fpkt->fcal_socal_request.sr_dataseg[2].fc_count =
16603d19cdaeSstevel 		    cmd->cmd_dmacookie.dmac_size;
16613d19cdaeSstevel 		fpkt->fcal_socal_request.sr_soc_hdr.sh_seg_cnt = 3;
16623d19cdaeSstevel 		fpkt->fcal_socal_request.sr_soc_hdr.sh_byte_cnt =
16633d19cdaeSstevel 		    cmd->cmd_dmacookie.dmac_size;
16643d19cdaeSstevel 		fcmd->fcp_data_len = cmd->cmd_dmacookie.dmac_size;
16653d19cdaeSstevel 	} else {
16663d19cdaeSstevel 		/* not a read or write */
16673d19cdaeSstevel 		fcmd->fcp_cntl.cntl_read_data = 0;
16683d19cdaeSstevel 		fcmd->fcp_cntl.cntl_write_data = 0;
16693d19cdaeSstevel 		fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_type = CQ_TYPE_SIMPLE;
16703d19cdaeSstevel 		fpkt->fcal_socal_request.sr_soc_hdr.sh_seg_cnt = 2;
16713d19cdaeSstevel 		fpkt->fcal_socal_request.sr_soc_hdr.sh_byte_cnt =
16723d19cdaeSstevel 		    sizeof (struct fcp_cmd);
16733d19cdaeSstevel 		fcmd->fcp_data_len = 0;
16743d19cdaeSstevel 	}
16753d19cdaeSstevel 	fcmd->fcp_cntl.cntl_qtype = FCP_QTYPE_SIMPLE;
16763d19cdaeSstevel 
16773d19cdaeSstevel 	return (cmd->cmd_pkt);
16783d19cdaeSstevel }
16793d19cdaeSstevel 
16803d19cdaeSstevel 
16813d19cdaeSstevel /*
16823d19cdaeSstevel  * destroy a SCSI packet -- called internally and by the transport
16833d19cdaeSstevel  */
16843d19cdaeSstevel static void
16853d19cdaeSstevel sf_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
16863d19cdaeSstevel {
1687602ca9eaScth 	struct sf_pkt *cmd = PKT2CMD(pkt);
16883d19cdaeSstevel 	struct sf *sf = ADDR2SF(ap);
16893d19cdaeSstevel 	struct sf_target *target = ADDR2TARGET(ap);
16903d19cdaeSstevel 	struct fcal_packet	*fpkt = cmd->cmd_fp_pkt;
16913d19cdaeSstevel 
16923d19cdaeSstevel 
16933d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_DMAVALID) {
16943d19cdaeSstevel 		/* DMA was set up -- clean up */
16953d19cdaeSstevel 		(void) ddi_dma_unbind_handle(cmd->cmd_dmahandle);
16963d19cdaeSstevel 		cmd->cmd_flags ^= CFLAG_DMAVALID;
16973d19cdaeSstevel 	}
16983d19cdaeSstevel 
16993d19cdaeSstevel 	/* take this packet off the doubly-linked list */
17003d19cdaeSstevel 	mutex_enter(&target->sft_pkt_mutex);
17013d19cdaeSstevel 	cmd->cmd_back->cmd_forw = cmd->cmd_forw;
17023d19cdaeSstevel 	cmd->cmd_forw->cmd_back = cmd->cmd_back;
17033d19cdaeSstevel 	mutex_exit(&target->sft_pkt_mutex);
17043d19cdaeSstevel 
17053d19cdaeSstevel 	fpkt->fcal_pkt_flags = 0;
17063d19cdaeSstevel 	/* free the packet */
17073d19cdaeSstevel 	if ((cmd->cmd_flags &
17083d19cdaeSstevel 	    (CFLAG_FREE | CFLAG_PRIVEXTERN | CFLAG_SCBEXTERN)) == 0) {
17093d19cdaeSstevel 		/* just a regular packet */
17103d19cdaeSstevel 		ASSERT(cmd->cmd_state != SF_STATE_ISSUED);
17113d19cdaeSstevel 		cmd->cmd_flags = CFLAG_FREE;
17123d19cdaeSstevel 		kmem_cache_free(sf->sf_pkt_cache, (void *)cmd);
17133d19cdaeSstevel 	} else {
17143d19cdaeSstevel 		/* a packet with extra memory */
17153d19cdaeSstevel 		sf_pkt_destroy_extern(sf, cmd);
17163d19cdaeSstevel 	}
17173d19cdaeSstevel }
17183d19cdaeSstevel 
17193d19cdaeSstevel 
17203d19cdaeSstevel /*
17213d19cdaeSstevel  * called by transport to unbind DMA handle
17223d19cdaeSstevel  */
17233d19cdaeSstevel /* ARGSUSED */
17243d19cdaeSstevel static void
17253d19cdaeSstevel sf_scsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
17263d19cdaeSstevel {
1727602ca9eaScth 	struct sf_pkt *cmd = PKT2CMD(pkt);
17283d19cdaeSstevel 
17293d19cdaeSstevel 
17303d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_DMAVALID) {
17313d19cdaeSstevel 		(void) ddi_dma_unbind_handle(cmd->cmd_dmahandle);
17323d19cdaeSstevel 		cmd->cmd_flags ^= CFLAG_DMAVALID;
17333d19cdaeSstevel 	}
17343d19cdaeSstevel 
17353d19cdaeSstevel }
17363d19cdaeSstevel 
17373d19cdaeSstevel 
17383d19cdaeSstevel /*
17393d19cdaeSstevel  * called by transport to synchronize CPU and I/O views of memory
17403d19cdaeSstevel  */
17413d19cdaeSstevel /* ARGSUSED */
17423d19cdaeSstevel static void
17433d19cdaeSstevel sf_scsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
17443d19cdaeSstevel {
1745602ca9eaScth 	struct sf_pkt *cmd = PKT2CMD(pkt);
17463d19cdaeSstevel 
17473d19cdaeSstevel 
17483d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_DMAVALID) {
17493d19cdaeSstevel 		if (ddi_dma_sync(cmd->cmd_dmahandle, (off_t)0, (size_t)0,
17503d19cdaeSstevel 		    (cmd->cmd_flags & CFLAG_DMASEND) ?
17513d19cdaeSstevel 		    DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU) !=
17523d19cdaeSstevel 		    DDI_SUCCESS) {
17533d19cdaeSstevel 			cmn_err(CE_WARN, "sf: sync pkt failed");
17543d19cdaeSstevel 		}
17553d19cdaeSstevel 	}
17563d19cdaeSstevel }
17573d19cdaeSstevel 
17583d19cdaeSstevel 
17593d19cdaeSstevel /*
17603d19cdaeSstevel  * routine for reset notification setup, to register or cancel. -- called
17613d19cdaeSstevel  * by transport
17623d19cdaeSstevel  */
17633d19cdaeSstevel static int
17643d19cdaeSstevel sf_scsi_reset_notify(struct scsi_address *ap, int flag,
17653d19cdaeSstevel     void (*callback)(caddr_t), caddr_t arg)
17663d19cdaeSstevel {
17673d19cdaeSstevel 	struct sf	*sf = ADDR2SF(ap);
17683d19cdaeSstevel 
17693d19cdaeSstevel 	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
17703d19cdaeSstevel 	    &sf->sf_mutex, &sf->sf_reset_notify_listf));
17713d19cdaeSstevel }
17723d19cdaeSstevel 
17733d19cdaeSstevel 
17743d19cdaeSstevel /*
17753d19cdaeSstevel  * called by transport to get port WWN property (except sun4u)
17763d19cdaeSstevel  */
17773d19cdaeSstevel /* ARGSUSED */
17783d19cdaeSstevel static int
17793d19cdaeSstevel sf_scsi_get_name(struct scsi_device *sd, char *name, int len)
17803d19cdaeSstevel {
17813d19cdaeSstevel 	char tbuf[(FC_WWN_SIZE*2)+1];
17823d19cdaeSstevel 	unsigned char wwn[FC_WWN_SIZE];
17833d19cdaeSstevel 	int i, lun;
17843d19cdaeSstevel 	dev_info_t *tgt_dip;
17853d19cdaeSstevel 
17863d19cdaeSstevel 	tgt_dip = sd->sd_dev;
17873d19cdaeSstevel 	i = sizeof (wwn);
17883d19cdaeSstevel 	if (ddi_prop_op(DDI_DEV_T_ANY, tgt_dip, PROP_LEN_AND_VAL_BUF,
17893d19cdaeSstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, PORT_WWN_PROP,
17903d19cdaeSstevel 	    (caddr_t)&wwn, &i) != DDI_SUCCESS) {
17913d19cdaeSstevel 		name[0] = '\0';
17923d19cdaeSstevel 		return (0);
17933d19cdaeSstevel 	}
17943d19cdaeSstevel 	i = sizeof (lun);
17953d19cdaeSstevel 	if (ddi_prop_op(DDI_DEV_T_ANY, tgt_dip, PROP_LEN_AND_VAL_BUF,
17963d19cdaeSstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "lun",
17973d19cdaeSstevel 	    (caddr_t)&lun, &i) != DDI_SUCCESS) {
17983d19cdaeSstevel 		name[0] = '\0';
17993d19cdaeSstevel 		return (0);
18003d19cdaeSstevel 	}
18013d19cdaeSstevel 	for (i = 0; i < FC_WWN_SIZE; i++)
18023d19cdaeSstevel 		(void) sprintf(&tbuf[i << 1], "%02x", wwn[i]);
18033d19cdaeSstevel 	(void) sprintf(name, "w%s,%x", tbuf, lun);
18043d19cdaeSstevel 	return (1);
18053d19cdaeSstevel }
18063d19cdaeSstevel 
18073d19cdaeSstevel 
18083d19cdaeSstevel /*
18093d19cdaeSstevel  * called by transport to get target soft AL-PA (except sun4u)
18103d19cdaeSstevel  */
18113d19cdaeSstevel /* ARGSUSED */
18123d19cdaeSstevel static int
18133d19cdaeSstevel sf_scsi_get_bus_addr(struct scsi_device *sd, char *name, int len)
18143d19cdaeSstevel {
18153d19cdaeSstevel 	struct sf_target *target = ADDR2TARGET(&sd->sd_address);
18163d19cdaeSstevel 
18173d19cdaeSstevel 	if (target == NULL)
18183d19cdaeSstevel 		return (0);
18193d19cdaeSstevel 
18203d19cdaeSstevel 	(void) sprintf(name, "%x", target->sft_al_pa);
18213d19cdaeSstevel 	return (1);
18223d19cdaeSstevel }
18233d19cdaeSstevel 
18243d19cdaeSstevel 
18253d19cdaeSstevel /*
18263d19cdaeSstevel  * add to the command/response buffer pool for this sf instance
18273d19cdaeSstevel  */
18283d19cdaeSstevel static int
18293d19cdaeSstevel sf_add_cr_pool(struct sf *sf)
18303d19cdaeSstevel {
18313d19cdaeSstevel 	int		cmd_buf_size;
18323d19cdaeSstevel 	size_t		real_cmd_buf_size;
18333d19cdaeSstevel 	int		rsp_buf_size;
18343d19cdaeSstevel 	size_t		real_rsp_buf_size;
18353d19cdaeSstevel 	uint_t		i, ccount;
18363d19cdaeSstevel 	struct sf_cr_pool	*ptr;
18373d19cdaeSstevel 	struct sf_cr_free_elem *cptr;
18383d19cdaeSstevel 	caddr_t	dptr, eptr;
18393d19cdaeSstevel 	ddi_dma_cookie_t	cmd_cookie;
18403d19cdaeSstevel 	ddi_dma_cookie_t	rsp_cookie;
18413d19cdaeSstevel 	int		cmd_bound = FALSE, rsp_bound = FALSE;
18423d19cdaeSstevel 
18433d19cdaeSstevel 
18443d19cdaeSstevel 	/* allocate room for the pool */
18453d19cdaeSstevel 	if ((ptr = kmem_zalloc(sizeof (struct sf_cr_pool), KM_NOSLEEP)) ==
18463d19cdaeSstevel 	    NULL) {
18473d19cdaeSstevel 		return (DDI_FAILURE);
18483d19cdaeSstevel 	}
18493d19cdaeSstevel 
18503d19cdaeSstevel 	/* allocate a DMA handle for the command pool */
18513d19cdaeSstevel 	if (ddi_dma_alloc_handle(sf->sf_dip, sf->sf_sochandle->fcal_dmaattr,
18523d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &ptr->cmd_dma_handle) != DDI_SUCCESS) {
18533d19cdaeSstevel 		goto fail;
18543d19cdaeSstevel 	}
18553d19cdaeSstevel 
18563d19cdaeSstevel 	/*
18573d19cdaeSstevel 	 * Get a piece of memory in which to put commands
18583d19cdaeSstevel 	 */
18593d19cdaeSstevel 	cmd_buf_size = (sizeof (struct fcp_cmd) * SF_ELEMS_IN_POOL + 7) & ~7;
18603d19cdaeSstevel 	if (ddi_dma_mem_alloc(ptr->cmd_dma_handle, cmd_buf_size,
18613d19cdaeSstevel 	    sf->sf_sochandle->fcal_accattr, DDI_DMA_CONSISTENT,
18623d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, (caddr_t *)&ptr->cmd_base,
18633d19cdaeSstevel 	    &real_cmd_buf_size, &ptr->cmd_acc_handle) != DDI_SUCCESS) {
18643d19cdaeSstevel 		goto fail;
18653d19cdaeSstevel 	}
18663d19cdaeSstevel 
18673d19cdaeSstevel 	/* bind the DMA handle to an address */
18683d19cdaeSstevel 	if (ddi_dma_addr_bind_handle(ptr->cmd_dma_handle, NULL,
18693d19cdaeSstevel 	    ptr->cmd_base, real_cmd_buf_size,
18703d19cdaeSstevel 	    DDI_DMA_WRITE | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
18713d19cdaeSstevel 	    NULL, &cmd_cookie, &ccount) != DDI_DMA_MAPPED) {
18723d19cdaeSstevel 		goto fail;
18733d19cdaeSstevel 	}
18743d19cdaeSstevel 	cmd_bound = TRUE;
18753d19cdaeSstevel 	/* ensure only one cookie was allocated */
18763d19cdaeSstevel 	if (ccount != 1) {
18773d19cdaeSstevel 		goto fail;
18783d19cdaeSstevel 	}
18793d19cdaeSstevel 
18803d19cdaeSstevel 	/* allocate a DMA handle for the response pool */
18813d19cdaeSstevel 	if (ddi_dma_alloc_handle(sf->sf_dip, sf->sf_sochandle->fcal_dmaattr,
18823d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &ptr->rsp_dma_handle) != DDI_SUCCESS) {
18833d19cdaeSstevel 		goto fail;
18843d19cdaeSstevel 	}
18853d19cdaeSstevel 
18863d19cdaeSstevel 	/*
18873d19cdaeSstevel 	 * Get a piece of memory in which to put responses
18883d19cdaeSstevel 	 */
18893d19cdaeSstevel 	rsp_buf_size = FCP_MAX_RSP_IU_SIZE * SF_ELEMS_IN_POOL;
18903d19cdaeSstevel 	if (ddi_dma_mem_alloc(ptr->rsp_dma_handle, rsp_buf_size,
18913d19cdaeSstevel 	    sf->sf_sochandle->fcal_accattr, DDI_DMA_CONSISTENT,
18923d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, (caddr_t *)&ptr->rsp_base,
18933d19cdaeSstevel 	    &real_rsp_buf_size, &ptr->rsp_acc_handle) != DDI_SUCCESS) {
18943d19cdaeSstevel 		goto fail;
18953d19cdaeSstevel 	}
18963d19cdaeSstevel 
18973d19cdaeSstevel 	/* bind the DMA handle to an address */
18983d19cdaeSstevel 	if (ddi_dma_addr_bind_handle(ptr->rsp_dma_handle, NULL,
18993d19cdaeSstevel 	    ptr->rsp_base, real_rsp_buf_size,
19003d19cdaeSstevel 	    DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
19013d19cdaeSstevel 	    NULL, &rsp_cookie, &ccount) != DDI_DMA_MAPPED) {
19023d19cdaeSstevel 		goto fail;
19033d19cdaeSstevel 	}
19043d19cdaeSstevel 	rsp_bound = TRUE;
19053d19cdaeSstevel 	/* ensure only one cookie was allocated */
19063d19cdaeSstevel 	if (ccount != 1) {
19073d19cdaeSstevel 		goto fail;
19083d19cdaeSstevel 	}
19093d19cdaeSstevel 
19103d19cdaeSstevel 	/*
19113d19cdaeSstevel 	 * Generate a (cmd/rsp structure) free list
19123d19cdaeSstevel 	 */
19133d19cdaeSstevel 	/* ensure ptr points to start of long word (8-byte block) */
19143d19cdaeSstevel 	dptr = (caddr_t)((uintptr_t)(ptr->cmd_base) + 7 & ~7);
19153d19cdaeSstevel 	/* keep track of actual size after moving pointer */
19163d19cdaeSstevel 	real_cmd_buf_size -= (dptr - ptr->cmd_base);
19173d19cdaeSstevel 	eptr = ptr->rsp_base;
19183d19cdaeSstevel 
19193d19cdaeSstevel 	/* set actual total number of entries */
19203d19cdaeSstevel 	ptr->ntot = min((real_cmd_buf_size / sizeof (struct fcp_cmd)),
19213d19cdaeSstevel 	    (real_rsp_buf_size / FCP_MAX_RSP_IU_SIZE));
19223d19cdaeSstevel 	ptr->nfree = ptr->ntot;
19233d19cdaeSstevel 	ptr->free = (struct sf_cr_free_elem *)ptr->cmd_base;
19243d19cdaeSstevel 	ptr->sf = sf;
19253d19cdaeSstevel 
19263d19cdaeSstevel 	/* set up DMA for each pair of entries */
19273d19cdaeSstevel 	i = 0;
19283d19cdaeSstevel 	while (i < ptr->ntot) {
19293d19cdaeSstevel 		cptr = (struct sf_cr_free_elem *)dptr;
19303d19cdaeSstevel 		dptr += sizeof (struct fcp_cmd);
19313d19cdaeSstevel 
19323d19cdaeSstevel 		cptr->next = (struct sf_cr_free_elem *)dptr;
19333d19cdaeSstevel 		cptr->rsp = eptr;
19343d19cdaeSstevel 
19353d19cdaeSstevel 		cptr->cmd_dmac = cmd_cookie.dmac_address +
19363d19cdaeSstevel 		    (uint32_t)((caddr_t)cptr - ptr->cmd_base);
19373d19cdaeSstevel 
19383d19cdaeSstevel 		cptr->rsp_dmac = rsp_cookie.dmac_address +
19393d19cdaeSstevel 		    (uint32_t)((caddr_t)eptr - ptr->rsp_base);
19403d19cdaeSstevel 
19413d19cdaeSstevel 		eptr += FCP_MAX_RSP_IU_SIZE;
19423d19cdaeSstevel 		i++;
19433d19cdaeSstevel 	}
19443d19cdaeSstevel 
19453d19cdaeSstevel 	/* terminate the list */
19463d19cdaeSstevel 	cptr->next = NULL;
19473d19cdaeSstevel 
19483d19cdaeSstevel 	/* add this list at front of current one */
19493d19cdaeSstevel 	mutex_enter(&sf->sf_cr_mutex);
19503d19cdaeSstevel 	ptr->next = sf->sf_cr_pool;
19513d19cdaeSstevel 	sf->sf_cr_pool = ptr;
19523d19cdaeSstevel 	sf->sf_cr_pool_cnt++;
19533d19cdaeSstevel 	mutex_exit(&sf->sf_cr_mutex);
19543d19cdaeSstevel 
19553d19cdaeSstevel 	return (DDI_SUCCESS);
19563d19cdaeSstevel 
19573d19cdaeSstevel fail:
19583d19cdaeSstevel 	/* we failed so clean up */
19593d19cdaeSstevel 	if (ptr->cmd_dma_handle != NULL) {
19603d19cdaeSstevel 		if (cmd_bound) {
19613d19cdaeSstevel 			(void) ddi_dma_unbind_handle(ptr->cmd_dma_handle);
19623d19cdaeSstevel 		}
19633d19cdaeSstevel 		ddi_dma_free_handle(&ptr->cmd_dma_handle);
19643d19cdaeSstevel 	}
19653d19cdaeSstevel 
19663d19cdaeSstevel 	if (ptr->rsp_dma_handle != NULL) {
19673d19cdaeSstevel 		if (rsp_bound) {
19683d19cdaeSstevel 			(void) ddi_dma_unbind_handle(ptr->rsp_dma_handle);
19693d19cdaeSstevel 		}
19703d19cdaeSstevel 		ddi_dma_free_handle(&ptr->rsp_dma_handle);
19713d19cdaeSstevel 	}
19723d19cdaeSstevel 
19733d19cdaeSstevel 	if (ptr->cmd_base != NULL) {
19743d19cdaeSstevel 		ddi_dma_mem_free(&ptr->cmd_acc_handle);
19753d19cdaeSstevel 	}
19763d19cdaeSstevel 
19773d19cdaeSstevel 	if (ptr->rsp_base != NULL) {
19783d19cdaeSstevel 		ddi_dma_mem_free(&ptr->rsp_acc_handle);
19793d19cdaeSstevel 	}
19803d19cdaeSstevel 
19813d19cdaeSstevel 	kmem_free((caddr_t)ptr, sizeof (struct sf_cr_pool));
19823d19cdaeSstevel 	return (DDI_FAILURE);
19833d19cdaeSstevel }
19843d19cdaeSstevel 
19853d19cdaeSstevel 
19863d19cdaeSstevel /*
19873d19cdaeSstevel  * allocate a command/response buffer from the pool, allocating more
19883d19cdaeSstevel  * in the pool as needed
19893d19cdaeSstevel  */
19903d19cdaeSstevel static int
19913d19cdaeSstevel sf_cr_alloc(struct sf *sf, struct sf_pkt *cmd, int (*func)())
19923d19cdaeSstevel {
19933d19cdaeSstevel 	struct sf_cr_pool *ptr;
19943d19cdaeSstevel 	struct sf_cr_free_elem *cptr;
19953d19cdaeSstevel 
19963d19cdaeSstevel 
19973d19cdaeSstevel 	mutex_enter(&sf->sf_cr_mutex);
19983d19cdaeSstevel 
19993d19cdaeSstevel try_again:
20003d19cdaeSstevel 
20013d19cdaeSstevel 	/* find a free buffer in the existing pool */
20023d19cdaeSstevel 	ptr = sf->sf_cr_pool;
20033d19cdaeSstevel 	while (ptr != NULL) {
20043d19cdaeSstevel 		if (ptr->nfree != 0) {
20053d19cdaeSstevel 			ptr->nfree--;
20063d19cdaeSstevel 			break;
20073d19cdaeSstevel 		} else {
20083d19cdaeSstevel 			ptr = ptr->next;
20093d19cdaeSstevel 		}
20103d19cdaeSstevel 	}
20113d19cdaeSstevel 
20123d19cdaeSstevel 	/* did we find a free buffer ? */
20133d19cdaeSstevel 	if (ptr != NULL) {
20143d19cdaeSstevel 		/* we found a free buffer -- take it off the free list */
20153d19cdaeSstevel 		cptr = ptr->free;
20163d19cdaeSstevel 		ptr->free = cptr->next;
20173d19cdaeSstevel 		mutex_exit(&sf->sf_cr_mutex);
20183d19cdaeSstevel 		/* set up the command to use the buffer pair */
20193d19cdaeSstevel 		cmd->cmd_block = (struct fcp_cmd *)cptr;
20203d19cdaeSstevel 		cmd->cmd_dmac = cptr->cmd_dmac;
20213d19cdaeSstevel 		cmd->cmd_rsp_dmac = cptr->rsp_dmac;
20223d19cdaeSstevel 		cmd->cmd_rsp_block = (struct fcp_rsp *)cptr->rsp;
20233d19cdaeSstevel 		cmd->cmd_cr_pool = ptr;
20243d19cdaeSstevel 		return (DDI_SUCCESS);		/* success */
20253d19cdaeSstevel 	}
20263d19cdaeSstevel 
20273d19cdaeSstevel 	/* no free buffer available -- can we allocate more ? */
20283d19cdaeSstevel 	if (sf->sf_cr_pool_cnt < SF_CR_POOL_MAX) {
20293d19cdaeSstevel 		/* we need to allocate more buffer pairs */
20303d19cdaeSstevel 		if (sf->sf_cr_flag) {
20313d19cdaeSstevel 			/* somebody already allocating for this instance */
20323d19cdaeSstevel 			if (func == SLEEP_FUNC) {
20333d19cdaeSstevel 				/* user wants to wait */
20343d19cdaeSstevel 				cv_wait(&sf->sf_cr_cv, &sf->sf_cr_mutex);
20353d19cdaeSstevel 				/* we've been woken so go try again */
20363d19cdaeSstevel 				goto try_again;
20373d19cdaeSstevel 			}
20383d19cdaeSstevel 			/* user does not want to wait */
20393d19cdaeSstevel 			mutex_exit(&sf->sf_cr_mutex);
20403d19cdaeSstevel 			sf->sf_stats.cralloc_failures++;
20413d19cdaeSstevel 			return (DDI_FAILURE);	/* give up */
20423d19cdaeSstevel 		}
20433d19cdaeSstevel 		/* set flag saying we're allocating */
20443d19cdaeSstevel 		sf->sf_cr_flag = 1;
20453d19cdaeSstevel 		mutex_exit(&sf->sf_cr_mutex);
20463d19cdaeSstevel 		/* add to our pool */
20473d19cdaeSstevel 		if (sf_add_cr_pool(sf) != DDI_SUCCESS) {
20483d19cdaeSstevel 			/* couldn't add to our pool for some reason */
20493d19cdaeSstevel 			mutex_enter(&sf->sf_cr_mutex);
20503d19cdaeSstevel 			sf->sf_cr_flag = 0;
20513d19cdaeSstevel 			cv_broadcast(&sf->sf_cr_cv);
20523d19cdaeSstevel 			mutex_exit(&sf->sf_cr_mutex);
20533d19cdaeSstevel 			sf->sf_stats.cralloc_failures++;
20543d19cdaeSstevel 			return (DDI_FAILURE);	/* give up */
20553d19cdaeSstevel 		}
20563d19cdaeSstevel 		/*
20573d19cdaeSstevel 		 * clear flag saying we're allocating and tell all other
20583d19cdaeSstevel 		 * that care
20593d19cdaeSstevel 		 */
20603d19cdaeSstevel 		mutex_enter(&sf->sf_cr_mutex);
20613d19cdaeSstevel 		sf->sf_cr_flag = 0;
20623d19cdaeSstevel 		cv_broadcast(&sf->sf_cr_cv);
20633d19cdaeSstevel 		/* now that we have more buffers try again */
20643d19cdaeSstevel 		goto try_again;
20653d19cdaeSstevel 	}
20663d19cdaeSstevel 
20673d19cdaeSstevel 	/* we don't have room to allocate any more buffers */
20683d19cdaeSstevel 	mutex_exit(&sf->sf_cr_mutex);
20693d19cdaeSstevel 	sf->sf_stats.cralloc_failures++;
20703d19cdaeSstevel 	return (DDI_FAILURE);			/* give up */
20713d19cdaeSstevel }
20723d19cdaeSstevel 
20733d19cdaeSstevel 
20743d19cdaeSstevel /*
20753d19cdaeSstevel  * free a cmd/response buffer pair in our pool
20763d19cdaeSstevel  */
20773d19cdaeSstevel static void
20783d19cdaeSstevel sf_cr_free(struct sf_cr_pool *cp, struct sf_pkt *cmd)
20793d19cdaeSstevel {
20803d19cdaeSstevel 	struct sf *sf = cp->sf;
20813d19cdaeSstevel 	struct sf_cr_free_elem *elem;
20823d19cdaeSstevel 
20833d19cdaeSstevel 	elem = (struct sf_cr_free_elem *)cmd->cmd_block;
20843d19cdaeSstevel 	elem->rsp = (caddr_t)cmd->cmd_rsp_block;
20853d19cdaeSstevel 	elem->cmd_dmac = cmd->cmd_dmac;
20863d19cdaeSstevel 	elem->rsp_dmac = cmd->cmd_rsp_dmac;
20873d19cdaeSstevel 
20883d19cdaeSstevel 	mutex_enter(&sf->sf_cr_mutex);
20893d19cdaeSstevel 	cp->nfree++;
20903d19cdaeSstevel 	ASSERT(cp->nfree <= cp->ntot);
20913d19cdaeSstevel 
20923d19cdaeSstevel 	elem->next = cp->free;
20933d19cdaeSstevel 	cp->free = elem;
20943d19cdaeSstevel 	mutex_exit(&sf->sf_cr_mutex);
20953d19cdaeSstevel }
20963d19cdaeSstevel 
20973d19cdaeSstevel 
20983d19cdaeSstevel /*
20993d19cdaeSstevel  * free our pool of cmd/response buffers
21003d19cdaeSstevel  */
21013d19cdaeSstevel static void
21023d19cdaeSstevel sf_crpool_free(struct sf *sf)
21033d19cdaeSstevel {
21043d19cdaeSstevel 	struct sf_cr_pool *cp, *prev;
21053d19cdaeSstevel 
21063d19cdaeSstevel 	prev = NULL;
21073d19cdaeSstevel 	mutex_enter(&sf->sf_cr_mutex);
21083d19cdaeSstevel 	cp = sf->sf_cr_pool;
21093d19cdaeSstevel 	while (cp != NULL) {
21103d19cdaeSstevel 		if (cp->nfree == cp->ntot) {
21113d19cdaeSstevel 			if (prev != NULL) {
21123d19cdaeSstevel 				prev->next = cp->next;
21133d19cdaeSstevel 			} else {
21143d19cdaeSstevel 				sf->sf_cr_pool = cp->next;
21153d19cdaeSstevel 			}
21163d19cdaeSstevel 			sf->sf_cr_pool_cnt--;
21173d19cdaeSstevel 			mutex_exit(&sf->sf_cr_mutex);
21183d19cdaeSstevel 
21193d19cdaeSstevel 			(void) ddi_dma_unbind_handle(cp->cmd_dma_handle);
21203d19cdaeSstevel 			ddi_dma_free_handle(&cp->cmd_dma_handle);
21213d19cdaeSstevel 			(void) ddi_dma_unbind_handle(cp->rsp_dma_handle);
21223d19cdaeSstevel 			ddi_dma_free_handle(&cp->rsp_dma_handle);
21233d19cdaeSstevel 			ddi_dma_mem_free(&cp->cmd_acc_handle);
21243d19cdaeSstevel 			ddi_dma_mem_free(&cp->rsp_acc_handle);
21253d19cdaeSstevel 			kmem_free((caddr_t)cp, sizeof (struct sf_cr_pool));
21263d19cdaeSstevel 			return;
21273d19cdaeSstevel 		}
21283d19cdaeSstevel 		prev = cp;
21293d19cdaeSstevel 		cp = cp->next;
21303d19cdaeSstevel 	}
21313d19cdaeSstevel 	mutex_exit(&sf->sf_cr_mutex);
21323d19cdaeSstevel }
21333d19cdaeSstevel 
21343d19cdaeSstevel 
21353d19cdaeSstevel /* ARGSUSED */
21363d19cdaeSstevel static int
21373d19cdaeSstevel sf_kmem_cache_constructor(void *buf, void *arg, int size)
21383d19cdaeSstevel {
21393d19cdaeSstevel 	struct sf_pkt *cmd = buf;
21403d19cdaeSstevel 
21413d19cdaeSstevel 	mutex_init(&cmd->cmd_abort_mutex, NULL, MUTEX_DRIVER, NULL);
21423d19cdaeSstevel 	cmd->cmd_block = NULL;
21433d19cdaeSstevel 	cmd->cmd_dmahandle = NULL;
21443d19cdaeSstevel 	return (0);
21453d19cdaeSstevel }
21463d19cdaeSstevel 
21473d19cdaeSstevel 
21483d19cdaeSstevel /* ARGSUSED */
21493d19cdaeSstevel static void
21503d19cdaeSstevel sf_kmem_cache_destructor(void *buf, void *size)
21513d19cdaeSstevel {
21523d19cdaeSstevel 	struct sf_pkt *cmd = buf;
21533d19cdaeSstevel 
21543d19cdaeSstevel 	if (cmd->cmd_dmahandle != NULL) {
21553d19cdaeSstevel 		ddi_dma_free_handle(&cmd->cmd_dmahandle);
21563d19cdaeSstevel 	}
21573d19cdaeSstevel 
21583d19cdaeSstevel 	if (cmd->cmd_block != NULL) {
21593d19cdaeSstevel 		sf_cr_free(cmd->cmd_cr_pool, cmd);
21603d19cdaeSstevel 	}
21613d19cdaeSstevel 	mutex_destroy(&cmd->cmd_abort_mutex);
21623d19cdaeSstevel }
21633d19cdaeSstevel 
21643d19cdaeSstevel 
21653d19cdaeSstevel /*
21663d19cdaeSstevel  * called by transport when a state change occurs
21673d19cdaeSstevel  */
21683d19cdaeSstevel static void
21693d19cdaeSstevel sf_statec_callback(void *arg, int msg)
21703d19cdaeSstevel {
21713d19cdaeSstevel 	struct sf *sf = (struct sf *)arg;
21723d19cdaeSstevel 	struct sf_target	*target;
21733d19cdaeSstevel 	int i;
21743d19cdaeSstevel 	struct sf_pkt *cmd;
21753d19cdaeSstevel 	struct scsi_pkt *pkt;
21763d19cdaeSstevel 
21773d19cdaeSstevel 
21783d19cdaeSstevel 
21793d19cdaeSstevel 	switch (msg) {
21803d19cdaeSstevel 
21813d19cdaeSstevel 	case FCAL_STATUS_LOOP_ONLINE: {
21823d19cdaeSstevel 		uchar_t		al_pa;		/* to save AL-PA */
21833d19cdaeSstevel 		int		ret;		/* ret value from getmap */
21843d19cdaeSstevel 		int		lip_cnt;	/* to save current count */
21853d19cdaeSstevel 		int		cnt;		/* map length */
21863d19cdaeSstevel 
21873d19cdaeSstevel 		/*
21883d19cdaeSstevel 		 * the loop has gone online
21893d19cdaeSstevel 		 */
21903d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_CONT, "sf%d: loop online\n",
21913d19cdaeSstevel 		    ddi_get_instance(sf->sf_dip)));
21923d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
21933d19cdaeSstevel 		sf->sf_lip_cnt++;
21943d19cdaeSstevel 		sf->sf_state = SF_STATE_ONLINING;
21953d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
21963d19cdaeSstevel 
21973d19cdaeSstevel 		/* scan each target hash queue */
21983d19cdaeSstevel 		for (i = 0; i < SF_NUM_HASH_QUEUES; i++) {
21993d19cdaeSstevel 			target = sf->sf_wwn_lists[i];
22003d19cdaeSstevel 			while (target != NULL) {
22013d19cdaeSstevel 				/*
22023d19cdaeSstevel 				 * foreach target, if it's not offline then
22033d19cdaeSstevel 				 * mark it as busy
22043d19cdaeSstevel 				 */
22053d19cdaeSstevel 				mutex_enter(&target->sft_mutex);
22063d19cdaeSstevel 				if (!(target->sft_state & SF_TARGET_OFFLINE))
22073d19cdaeSstevel 					target->sft_state |= (SF_TARGET_BUSY
22083d19cdaeSstevel 					    | SF_TARGET_MARK);
22093d19cdaeSstevel #ifdef DEBUG
22103d19cdaeSstevel 				/*
22113d19cdaeSstevel 				 * for debugging, print out info on any
22123d19cdaeSstevel 				 * pending commands (left hanging)
22133d19cdaeSstevel 				 */
22143d19cdaeSstevel 				cmd = target->sft_pkt_head;
22153d19cdaeSstevel 				while (cmd != (struct sf_pkt *)&target->
22163d19cdaeSstevel 				    sft_pkt_head) {
22173d19cdaeSstevel 					if (cmd->cmd_state ==
22183d19cdaeSstevel 					    SF_STATE_ISSUED) {
22193d19cdaeSstevel 						SF_DEBUG(1, (sf, CE_CONT,
22203d19cdaeSstevel 						    "cmd 0x%p pending "
22213d19cdaeSstevel 						    "after lip\n",
22223d19cdaeSstevel 						    (void *)cmd->cmd_fp_pkt));
22233d19cdaeSstevel 					}
22243d19cdaeSstevel 					cmd = cmd->cmd_forw;
22253d19cdaeSstevel 				}
22263d19cdaeSstevel #endif
22273d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
22283d19cdaeSstevel 				target = target->sft_next;
22293d19cdaeSstevel 			}
22303d19cdaeSstevel 		}
22313d19cdaeSstevel 
22323d19cdaeSstevel 		/*
22333d19cdaeSstevel 		 * since the loop has just gone online get a new map from
22343d19cdaeSstevel 		 * the transport
22353d19cdaeSstevel 		 */
22363d19cdaeSstevel 		if ((ret = soc_get_lilp_map(sf->sf_sochandle, sf->sf_socp,
22373d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, (uint32_t)sf->
22383d19cdaeSstevel 		    sf_lilp_dmacookie.dmac_address, 1)) != FCAL_SUCCESS) {
22393d19cdaeSstevel 			if (sf_core && (sf_core & SF_CORE_LILP_FAILED)) {
22403d19cdaeSstevel 				(void) soc_take_core(sf->sf_sochandle,
22413d19cdaeSstevel 				    sf->sf_socp);
22423d19cdaeSstevel 				sf_core = 0;
22433d19cdaeSstevel 			}
22443d19cdaeSstevel 			sf_log(sf, CE_WARN,
22453d19cdaeSstevel 			    "!soc lilp map failed status=0x%x\n", ret);
22463d19cdaeSstevel 			mutex_enter(&sf->sf_mutex);
22473d19cdaeSstevel 			sf->sf_timer = sf_watchdog_time + SF_OFFLINE_TIMEOUT;
22483d19cdaeSstevel 			sf->sf_lip_cnt++;
22493d19cdaeSstevel 			sf->sf_state = SF_STATE_OFFLINE;
22503d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
22513d19cdaeSstevel 			return;
22523d19cdaeSstevel 		}
22533d19cdaeSstevel 
22543d19cdaeSstevel 		/* ensure consistent view of DMA memory */
22553d19cdaeSstevel 		(void) ddi_dma_sync(sf->sf_lilp_dmahandle, (off_t)0, (size_t)0,
22563d19cdaeSstevel 		    DDI_DMA_SYNC_FORKERNEL);
22573d19cdaeSstevel 
22583d19cdaeSstevel 		/* how many entries in map ? */
22593d19cdaeSstevel 		cnt = sf->sf_lilp_map->lilp_length;
22603d19cdaeSstevel 		if (cnt >= SF_MAX_LILP_ENTRIES) {
22613d19cdaeSstevel 			sf_log(sf, CE_WARN, "invalid lilp map\n");
22623d19cdaeSstevel 			return;
22633d19cdaeSstevel 		}
22643d19cdaeSstevel 
22653d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
22663d19cdaeSstevel 		sf->sf_device_count = cnt - 1;
22673d19cdaeSstevel 		sf->sf_al_pa = sf->sf_lilp_map->lilp_myalpa;
22683d19cdaeSstevel 		lip_cnt = sf->sf_lip_cnt;
22693d19cdaeSstevel 		al_pa = sf->sf_al_pa;
22703d19cdaeSstevel 
22713d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_CONT,
22723d19cdaeSstevel 		    "!lilp map has %d entries, al_pa is %x\n", cnt, al_pa));
22733d19cdaeSstevel 
22743d19cdaeSstevel 		/*
22753d19cdaeSstevel 		 * since the last entry of the map may be mine (common) check
22763d19cdaeSstevel 		 * for that, and if it is we have one less entry to look at
22773d19cdaeSstevel 		 */
22783d19cdaeSstevel 		if (sf->sf_lilp_map->lilp_alpalist[cnt-1] == al_pa) {
22793d19cdaeSstevel 			cnt--;
22803d19cdaeSstevel 		}
22813d19cdaeSstevel 		/* If we didn't get a valid loop map enable all targets */
22823d19cdaeSstevel 		if (sf->sf_lilp_map->lilp_magic == FCAL_BADLILP_MAGIC) {
22833d19cdaeSstevel 			for (i = 0; i < sizeof (sf_switch_to_alpa); i++)
22843d19cdaeSstevel 				sf->sf_lilp_map->lilp_alpalist[i] =
22853d19cdaeSstevel 				    sf_switch_to_alpa[i];
22863d19cdaeSstevel 			cnt = i;
22873d19cdaeSstevel 			sf->sf_device_count = cnt - 1;
22883d19cdaeSstevel 		}
22893d19cdaeSstevel 		if (sf->sf_device_count == 0) {
22903d19cdaeSstevel 			sf_finish_init(sf, lip_cnt);
22913d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
22923d19cdaeSstevel 			break;
22933d19cdaeSstevel 		}
22943d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
22953d19cdaeSstevel 
22963d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_WARN,
22973d19cdaeSstevel 		    "!statec_callback: starting with %d targets\n",
22983d19cdaeSstevel 		    sf->sf_device_count));
22993d19cdaeSstevel 
23003d19cdaeSstevel 		/* scan loop map, logging into all ports (except mine) */
23013d19cdaeSstevel 		for (i = 0; i < cnt; i++) {
23023d19cdaeSstevel 			SF_DEBUG(1, (sf, CE_CONT,
23033d19cdaeSstevel 			    "!lilp map entry %d = %x,%x\n", i,
23043d19cdaeSstevel 			    sf->sf_lilp_map->lilp_alpalist[i],
23053d19cdaeSstevel 			    sf_alpa_to_switch[
23063d19cdaeSstevel 			    sf->sf_lilp_map->lilp_alpalist[i]]));
23073d19cdaeSstevel 			/* is this entry for somebody else ? */
23083d19cdaeSstevel 			if (sf->sf_lilp_map->lilp_alpalist[i] != al_pa) {
23093d19cdaeSstevel 				/* do a PLOGI to this port */
23103d19cdaeSstevel 				if (!sf_login(sf, LA_ELS_PLOGI,
23113d19cdaeSstevel 				    sf->sf_lilp_map->lilp_alpalist[i],
23123d19cdaeSstevel 				    sf->sf_lilp_map->lilp_alpalist[cnt-1],
23133d19cdaeSstevel 				    lip_cnt)) {
23143d19cdaeSstevel 					/* a problem logging in */
23153d19cdaeSstevel 					mutex_enter(&sf->sf_mutex);
23163d19cdaeSstevel 					if (lip_cnt == sf->sf_lip_cnt) {
23173d19cdaeSstevel 						/*
23183d19cdaeSstevel 						 * problem not from a new LIP
23193d19cdaeSstevel 						 */
23203d19cdaeSstevel 						sf->sf_device_count--;
23213d19cdaeSstevel 						ASSERT(sf->sf_device_count
23223d19cdaeSstevel 						    >= 0);
23233d19cdaeSstevel 						if (sf->sf_device_count == 0) {
23243d19cdaeSstevel 							sf_finish_init(sf,
23253d19cdaeSstevel 							    lip_cnt);
23263d19cdaeSstevel 						}
23273d19cdaeSstevel 					}
23283d19cdaeSstevel 					mutex_exit(&sf->sf_mutex);
23293d19cdaeSstevel 				}
23303d19cdaeSstevel 			}
23313d19cdaeSstevel 		}
23323d19cdaeSstevel 		break;
23333d19cdaeSstevel 	}
23343d19cdaeSstevel 
23353d19cdaeSstevel 	case FCAL_STATUS_ERR_OFFLINE:
23363d19cdaeSstevel 		/*
23373d19cdaeSstevel 		 * loop has gone offline due to an error
23383d19cdaeSstevel 		 */
23393d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_CONT, "sf%d: loop offline\n",
23403d19cdaeSstevel 		    ddi_get_instance(sf->sf_dip)));
23413d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
23423d19cdaeSstevel 		sf->sf_lip_cnt++;
23433d19cdaeSstevel 		sf->sf_timer = sf_watchdog_time + SF_OFFLINE_TIMEOUT;
23443d19cdaeSstevel 		if (!sf->sf_online_timer) {
23453d19cdaeSstevel 			sf->sf_online_timer = sf_watchdog_time +
23463d19cdaeSstevel 			    SF_ONLINE_TIMEOUT;
23473d19cdaeSstevel 		}
23483d19cdaeSstevel 		/*
23493d19cdaeSstevel 		 * if we are suspended, preserve the SF_STATE_SUSPENDED flag,
23503d19cdaeSstevel 		 * since throttling logic in sf_watch() depends on
23513d19cdaeSstevel 		 * preservation of this flag while device is suspended
23523d19cdaeSstevel 		 */
23533d19cdaeSstevel 		if (sf->sf_state & SF_STATE_SUSPENDED) {
23543d19cdaeSstevel 			sf->sf_state |= SF_STATE_OFFLINE;
23553d19cdaeSstevel 			SF_DEBUG(1, (sf, CE_CONT,
23563d19cdaeSstevel 			    "sf_statec_callback, sf%d: "
23573d19cdaeSstevel 			    "got FCAL_STATE_OFFLINE during DDI_SUSPEND\n",
23583d19cdaeSstevel 			    ddi_get_instance(sf->sf_dip)));
23593d19cdaeSstevel 		} else {
23603d19cdaeSstevel 			sf->sf_state = SF_STATE_OFFLINE;
23613d19cdaeSstevel 		}
23623d19cdaeSstevel 
23633d19cdaeSstevel 		/* scan each possible target on the loop */
23643d19cdaeSstevel 		for (i = 0; i < sf_max_targets; i++) {
23653d19cdaeSstevel 			target = sf->sf_targets[i];
23663d19cdaeSstevel 			while (target != NULL) {
23673d19cdaeSstevel 				mutex_enter(&target->sft_mutex);
23683d19cdaeSstevel 				if (!(target->sft_state & SF_TARGET_OFFLINE))
23693d19cdaeSstevel 					target->sft_state |= (SF_TARGET_BUSY
23703d19cdaeSstevel 					    | SF_TARGET_MARK);
23713d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
23723d19cdaeSstevel 				target = target->sft_next_lun;
23733d19cdaeSstevel 			}
23743d19cdaeSstevel 		}
23753d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
23763d19cdaeSstevel 		break;
23773d19cdaeSstevel 
23783d19cdaeSstevel 	case FCAL_STATE_RESET: {
23793d19cdaeSstevel 		struct sf_els_hdr	*privp;	/* ptr to private list */
23803d19cdaeSstevel 		struct sf_els_hdr	*tmpp1;	/* tmp prev hdr ptr */
23813d19cdaeSstevel 		struct sf_els_hdr	*tmpp2;	/* tmp next hdr ptr */
23823d19cdaeSstevel 		struct sf_els_hdr	*head;	/* to save our private list */
23833d19cdaeSstevel 		struct fcal_packet	*fpkt;	/* ptr to pkt in hdr */
23843d19cdaeSstevel 
23853d19cdaeSstevel 		/*
23863d19cdaeSstevel 		 * a transport reset
23873d19cdaeSstevel 		 */
23883d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_CONT, "!sf%d: soc reset\n",
23893d19cdaeSstevel 		    ddi_get_instance(sf->sf_dip)));
23903d19cdaeSstevel 		tmpp1 = head = NULL;
23913d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
23923d19cdaeSstevel 		sf->sf_lip_cnt++;
23933d19cdaeSstevel 		sf->sf_timer = sf_watchdog_time + SF_RESET_TIMEOUT;
23943d19cdaeSstevel 		/*
23953d19cdaeSstevel 		 * if we are suspended, preserve the SF_STATE_SUSPENDED flag,
23963d19cdaeSstevel 		 * since throttling logic in sf_watch() depends on
23973d19cdaeSstevel 		 * preservation of this flag while device is suspended
23983d19cdaeSstevel 		 */
23993d19cdaeSstevel 		if (sf->sf_state & SF_STATE_SUSPENDED) {
24003d19cdaeSstevel 			sf->sf_state |= SF_STATE_OFFLINE;
24013d19cdaeSstevel 			SF_DEBUG(1, (sf, CE_CONT,
24023d19cdaeSstevel 			    "sf_statec_callback, sf%d: "
24033d19cdaeSstevel 			    "got FCAL_STATE_RESET during DDI_SUSPEND\n",
24043d19cdaeSstevel 			    ddi_get_instance(sf->sf_dip)));
24053d19cdaeSstevel 		} else {
24063d19cdaeSstevel 			sf->sf_state = SF_STATE_OFFLINE;
24073d19cdaeSstevel 		}
24083d19cdaeSstevel 
24093d19cdaeSstevel 		/*
24103d19cdaeSstevel 		 * scan each possible target on the loop, looking for targets
24113d19cdaeSstevel 		 * that need callbacks ran
24123d19cdaeSstevel 		 */
24133d19cdaeSstevel 		for (i = 0; i < sf_max_targets; i++) {
24143d19cdaeSstevel 			target = sf->sf_targets[i];
24153d19cdaeSstevel 			while (target != NULL) {
24163d19cdaeSstevel 				if (!(target->sft_state & SF_TARGET_OFFLINE)) {
24173d19cdaeSstevel 					target->sft_state |= (SF_TARGET_BUSY
24183d19cdaeSstevel 					    | SF_TARGET_MARK);
24193d19cdaeSstevel 					mutex_exit(&sf->sf_mutex);
24203d19cdaeSstevel 					/*
24213d19cdaeSstevel 					 * run remove event callbacks for lun
24223d19cdaeSstevel 					 *
24233d19cdaeSstevel 					 * We have a nasty race condition here
24243d19cdaeSstevel 					 * 'cause we're dropping this mutex to
24253d19cdaeSstevel 					 * run the callback and expect the
24263d19cdaeSstevel 					 * linked list to be the same.
24273d19cdaeSstevel 					 */
24283d19cdaeSstevel 					(void) ndi_event_retrieve_cookie(
24293d19cdaeSstevel 					    sf->sf_event_hdl, target->sft_dip,
24303d19cdaeSstevel 					    FCAL_REMOVE_EVENT, &sf_remove_eid,
24313d19cdaeSstevel 					    NDI_EVENT_NOPASS);
24323d19cdaeSstevel 					(void) ndi_event_run_callbacks(
24333d19cdaeSstevel 					    sf->sf_event_hdl,
24343d19cdaeSstevel 					    target->sft_dip,
24353d19cdaeSstevel 					    sf_remove_eid, NULL);
24363d19cdaeSstevel 					mutex_enter(&sf->sf_mutex);
24373d19cdaeSstevel 				}
24383d19cdaeSstevel 				target = target->sft_next_lun;
24393d19cdaeSstevel 			}
24403d19cdaeSstevel 		}
24413d19cdaeSstevel 
24423d19cdaeSstevel 		/*
24433d19cdaeSstevel 		 * scan for ELS commands that are in transport, not complete,
24443d19cdaeSstevel 		 * and have a valid timeout, building a private list
24453d19cdaeSstevel 		 */
24463d19cdaeSstevel 		privp = sf->sf_els_list;
24473d19cdaeSstevel 		while (privp != NULL) {
24483d19cdaeSstevel 			fpkt = privp->fpkt;
24493d19cdaeSstevel 			if ((fpkt->fcal_cmd_state & FCAL_CMD_IN_TRANSPORT) &&
24503d19cdaeSstevel 			    (!(fpkt->fcal_cmd_state & FCAL_CMD_COMPLETE)) &&
24513d19cdaeSstevel 			    (privp->timeout != SF_INVALID_TIMEOUT)) {
24523d19cdaeSstevel 				/*
24533d19cdaeSstevel 				 * cmd in transport && not complete &&
24543d19cdaeSstevel 				 * timeout valid
24553d19cdaeSstevel 				 *
24563d19cdaeSstevel 				 * move this entry from ELS input list to our
24573d19cdaeSstevel 				 * private list
24583d19cdaeSstevel 				 */
24593d19cdaeSstevel 
24603d19cdaeSstevel 				tmpp2 = privp->next; /* save ptr to next */
24613d19cdaeSstevel 
24623d19cdaeSstevel 				/* push this on private list head */
24633d19cdaeSstevel 				privp->next = head;
24643d19cdaeSstevel 				head = privp;
24653d19cdaeSstevel 
24663d19cdaeSstevel 				/* remove this entry from input list */
24673d19cdaeSstevel 				if (tmpp1 != NULL) {
24683d19cdaeSstevel 					/*
24693d19cdaeSstevel 					 * remove this entry from somewhere in
24703d19cdaeSstevel 					 * the middle of the list
24713d19cdaeSstevel 					 */
24723d19cdaeSstevel 					tmpp1->next = tmpp2;
24733d19cdaeSstevel 					if (tmpp2 != NULL) {
24743d19cdaeSstevel 						tmpp2->prev = tmpp1;
24753d19cdaeSstevel 					}
24763d19cdaeSstevel 				} else {
24773d19cdaeSstevel 					/*
24783d19cdaeSstevel 					 * remove this entry from the head
24793d19cdaeSstevel 					 * of the list
24803d19cdaeSstevel 					 */
24813d19cdaeSstevel 					sf->sf_els_list = tmpp2;
24823d19cdaeSstevel 					if (tmpp2 != NULL) {
24833d19cdaeSstevel 						tmpp2->prev = NULL;
24843d19cdaeSstevel 					}
24853d19cdaeSstevel 				}
24863d19cdaeSstevel 				privp = tmpp2;	/* skip to next entry */
24873d19cdaeSstevel 			} else {
24883d19cdaeSstevel 				tmpp1 = privp;	/* save ptr to prev entry */
24893d19cdaeSstevel 				privp = privp->next; /* skip to next entry */
24903d19cdaeSstevel 			}
24913d19cdaeSstevel 		}
24923d19cdaeSstevel 
24933d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
24943d19cdaeSstevel 
24953d19cdaeSstevel 		/*
24963d19cdaeSstevel 		 * foreach cmd in our list free the ELS packet associated
24973d19cdaeSstevel 		 * with it
24983d19cdaeSstevel 		 */
24993d19cdaeSstevel 		privp = head;
25003d19cdaeSstevel 		while (privp != NULL) {
25013d19cdaeSstevel 			fpkt = privp->fpkt;
25023d19cdaeSstevel 			privp = privp->next;
25033d19cdaeSstevel 			sf_els_free(fpkt);
25043d19cdaeSstevel 		}
25053d19cdaeSstevel 
25063d19cdaeSstevel 		/*
25073d19cdaeSstevel 		 * scan for commands from each possible target
25083d19cdaeSstevel 		 */
25093d19cdaeSstevel 		for (i = 0; i < sf_max_targets; i++) {
25103d19cdaeSstevel 			target = sf->sf_targets[i];
25113d19cdaeSstevel 			while (target != NULL) {
25123d19cdaeSstevel 				/*
25133d19cdaeSstevel 				 * scan all active commands for this target,
25143d19cdaeSstevel 				 * looking for commands that have been issued,
25153d19cdaeSstevel 				 * are in transport, and are not yet complete
25163d19cdaeSstevel 				 * (so we can terminate them because of the
25173d19cdaeSstevel 				 * reset)
25183d19cdaeSstevel 				 */
25193d19cdaeSstevel 				mutex_enter(&target->sft_pkt_mutex);
25203d19cdaeSstevel 				cmd = target->sft_pkt_head;
25213d19cdaeSstevel 				while (cmd != (struct sf_pkt *)&target->
25223d19cdaeSstevel 				    sft_pkt_head) {
25233d19cdaeSstevel 					fpkt = cmd->cmd_fp_pkt;
25243d19cdaeSstevel 					mutex_enter(&cmd->cmd_abort_mutex);
25253d19cdaeSstevel 					if ((cmd->cmd_state ==
25263d19cdaeSstevel 					    SF_STATE_ISSUED) &&
25273d19cdaeSstevel 					    (fpkt->fcal_cmd_state &
25283d19cdaeSstevel 					    FCAL_CMD_IN_TRANSPORT) &&
25293d19cdaeSstevel 					    (!(fpkt->fcal_cmd_state &
25303d19cdaeSstevel 					    FCAL_CMD_COMPLETE))) {
25313d19cdaeSstevel 						/* a command to be reset */
25323d19cdaeSstevel 						pkt = cmd->cmd_pkt;
25333d19cdaeSstevel 						pkt->pkt_reason = CMD_RESET;
25343d19cdaeSstevel 						pkt->pkt_statistics |=
25353d19cdaeSstevel 						    STAT_BUS_RESET;
25363d19cdaeSstevel 						cmd->cmd_state = SF_STATE_IDLE;
25373d19cdaeSstevel 						mutex_exit(&cmd->
25383d19cdaeSstevel 						    cmd_abort_mutex);
25393d19cdaeSstevel 						mutex_exit(&target->
25403d19cdaeSstevel 						    sft_pkt_mutex);
25413d19cdaeSstevel 						if (pkt->pkt_comp != NULL) {
25423d19cdaeSstevel 							(*pkt->pkt_comp)(pkt);
25433d19cdaeSstevel 						}
25443d19cdaeSstevel 						mutex_enter(&target->
25453d19cdaeSstevel 						    sft_pkt_mutex);
25463d19cdaeSstevel 						cmd = target->sft_pkt_head;
25473d19cdaeSstevel 					} else {
25483d19cdaeSstevel 						mutex_exit(&cmd->
25493d19cdaeSstevel 						    cmd_abort_mutex);
25503d19cdaeSstevel 						/* get next command */
25513d19cdaeSstevel 						cmd = cmd->cmd_forw;
25523d19cdaeSstevel 					}
25533d19cdaeSstevel 				}
25543d19cdaeSstevel 				mutex_exit(&target->sft_pkt_mutex);
25553d19cdaeSstevel 				target = target->sft_next_lun;
25563d19cdaeSstevel 			}
25573d19cdaeSstevel 		}
25583d19cdaeSstevel 
25593d19cdaeSstevel 		/*
25603d19cdaeSstevel 		 * get packet queue for this target, resetting all remaining
25613d19cdaeSstevel 		 * commands
25623d19cdaeSstevel 		 */
25633d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
25643d19cdaeSstevel 		cmd = sf->sf_pkt_head;
25653d19cdaeSstevel 		sf->sf_pkt_head = NULL;
25663d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
25673d19cdaeSstevel 
25683d19cdaeSstevel 		while (cmd != NULL) {
25693d19cdaeSstevel 			pkt = cmd->cmd_pkt;
25703d19cdaeSstevel 			cmd = cmd->cmd_next;
25713d19cdaeSstevel 			pkt->pkt_reason = CMD_RESET;
25723d19cdaeSstevel 			pkt->pkt_statistics |= STAT_BUS_RESET;
25733d19cdaeSstevel 			if (pkt->pkt_comp != NULL) {
25743d19cdaeSstevel 				(*pkt->pkt_comp)(pkt);
25753d19cdaeSstevel 			}
25763d19cdaeSstevel 		}
25773d19cdaeSstevel 		break;
25783d19cdaeSstevel 	}
25793d19cdaeSstevel 
25803d19cdaeSstevel 	default:
25813d19cdaeSstevel 		break;
25823d19cdaeSstevel 	}
25833d19cdaeSstevel }
25843d19cdaeSstevel 
25853d19cdaeSstevel 
25863d19cdaeSstevel /*
25873d19cdaeSstevel  * called to send a PLOGI (N_port login) ELS request to a destination ID,
25883d19cdaeSstevel  * returning TRUE upon success, else returning FALSE
25893d19cdaeSstevel  */
25903d19cdaeSstevel static int
25913d19cdaeSstevel sf_login(struct sf *sf, uchar_t els_code, uchar_t dest_id, uint_t arg1,
25923d19cdaeSstevel     int lip_cnt)
25933d19cdaeSstevel {
25943d19cdaeSstevel 	struct la_els_logi	*logi;
25953d19cdaeSstevel 	struct	sf_els_hdr	*privp;
25963d19cdaeSstevel 
25973d19cdaeSstevel 
25983d19cdaeSstevel 	if (sf_els_alloc(sf, dest_id, sizeof (struct sf_els_hdr),
25993d19cdaeSstevel 	    sizeof (union sf_els_cmd), sizeof (union sf_els_rsp),
26003d19cdaeSstevel 	    (caddr_t *)&privp, (caddr_t *)&logi) == NULL) {
26013d19cdaeSstevel 		sf_log(sf, CE_WARN, "Cannot allocate PLOGI for target %x "
26023d19cdaeSstevel 		    "due to DVMA shortage.\n", sf_alpa_to_switch[dest_id]);
26033d19cdaeSstevel 		return (FALSE);
26043d19cdaeSstevel 	}
26053d19cdaeSstevel 
26063d19cdaeSstevel 	privp->lip_cnt = lip_cnt;
26073d19cdaeSstevel 	if (els_code == LA_ELS_PLOGI) {
26083d19cdaeSstevel 		bcopy((caddr_t)sf->sf_sochandle->fcal_loginparms,
26093d19cdaeSstevel 		    (caddr_t)&logi->common_service, sizeof (struct la_els_logi)
26103d19cdaeSstevel 		    - 4);
26113d19cdaeSstevel 		bcopy((caddr_t)&sf->sf_sochandle->fcal_p_wwn,
26123d19cdaeSstevel 		    (caddr_t)&logi->nport_ww_name, sizeof (la_wwn_t));
26133d19cdaeSstevel 		bcopy((caddr_t)&sf->sf_sochandle->fcal_n_wwn,
26143d19cdaeSstevel 		    (caddr_t)&logi->node_ww_name, sizeof (la_wwn_t));
26153d19cdaeSstevel 		bzero((caddr_t)&logi->reserved, 16);
26163d19cdaeSstevel 	} else if (els_code == LA_ELS_LOGO) {
26173d19cdaeSstevel 		bcopy((caddr_t)&sf->sf_sochandle->fcal_p_wwn,
26183d19cdaeSstevel 		    (caddr_t)&(((struct la_els_logo *)logi)->nport_ww_name), 8);
26193d19cdaeSstevel 		((struct la_els_logo	*)logi)->reserved = 0;
26203d19cdaeSstevel 		((struct la_els_logo	*)logi)->nport_id[0] = 0;
26213d19cdaeSstevel 		((struct la_els_logo	*)logi)->nport_id[1] = 0;
26223d19cdaeSstevel 		((struct la_els_logo	*)logi)->nport_id[2] = arg1;
26233d19cdaeSstevel 	}
26243d19cdaeSstevel 
26253d19cdaeSstevel 	privp->els_code = els_code;
26263d19cdaeSstevel 	logi->ls_code = els_code;
26273d19cdaeSstevel 	logi->mbz[0] = 0;
26283d19cdaeSstevel 	logi->mbz[1] = 0;
26293d19cdaeSstevel 	logi->mbz[2] = 0;
26303d19cdaeSstevel 
26313d19cdaeSstevel 	privp->timeout = sf_watchdog_time + SF_ELS_TIMEOUT;
26323d19cdaeSstevel 	return (sf_els_transport(sf, privp));
26333d19cdaeSstevel }
26343d19cdaeSstevel 
26353d19cdaeSstevel 
26363d19cdaeSstevel /*
26373d19cdaeSstevel  * send an ELS IU via the transport,
26383d19cdaeSstevel  * returning TRUE upon success, else returning FALSE
26393d19cdaeSstevel  */
26403d19cdaeSstevel static int
26413d19cdaeSstevel sf_els_transport(struct sf *sf, struct sf_els_hdr *privp)
26423d19cdaeSstevel {
26433d19cdaeSstevel 	struct fcal_packet *fpkt = privp->fpkt;
26443d19cdaeSstevel 
26453d19cdaeSstevel 
26463d19cdaeSstevel 	(void) ddi_dma_sync(privp->cmd_dma_handle, (off_t)0, (size_t)0,
26473d19cdaeSstevel 	    DDI_DMA_SYNC_FORDEV);
26483d19cdaeSstevel 	privp->prev = NULL;
26493d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
26503d19cdaeSstevel 	privp->next = sf->sf_els_list;
26513d19cdaeSstevel 	if (sf->sf_els_list != NULL) {
26523d19cdaeSstevel 		sf->sf_els_list->prev = privp;
26533d19cdaeSstevel 	}
26543d19cdaeSstevel 	sf->sf_els_list = privp;
26553d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
26563d19cdaeSstevel 
26573d19cdaeSstevel 	/* call the transport to send a packet */
26583d19cdaeSstevel 	if (soc_transport(sf->sf_sochandle, fpkt, FCAL_NOSLEEP,
26593d19cdaeSstevel 	    CQ_REQUEST_1) != FCAL_TRANSPORT_SUCCESS) {
26603d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
26613d19cdaeSstevel 		if (privp->prev != NULL) {
26623d19cdaeSstevel 			privp->prev->next = privp->next;
26633d19cdaeSstevel 		}
26643d19cdaeSstevel 		if (privp->next != NULL) {
26653d19cdaeSstevel 			privp->next->prev = privp->prev;
26663d19cdaeSstevel 		}
26673d19cdaeSstevel 		if (sf->sf_els_list == privp) {
26683d19cdaeSstevel 			sf->sf_els_list = privp->next;
26693d19cdaeSstevel 		}
26703d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
26713d19cdaeSstevel 		sf_els_free(fpkt);
26723d19cdaeSstevel 		return (FALSE);			/* failure */
26733d19cdaeSstevel 	}
26743d19cdaeSstevel 	return (TRUE);				/* success */
26753d19cdaeSstevel }
26763d19cdaeSstevel 
26773d19cdaeSstevel 
26783d19cdaeSstevel /*
26793d19cdaeSstevel  * called as the pkt_comp routine for ELS FC packets
26803d19cdaeSstevel  */
26813d19cdaeSstevel static void
26823d19cdaeSstevel sf_els_callback(struct fcal_packet *fpkt)
26833d19cdaeSstevel {
26843d19cdaeSstevel 	struct sf_els_hdr *privp = fpkt->fcal_pkt_private;
26853d19cdaeSstevel 	struct sf *sf = privp->sf;
26863d19cdaeSstevel 	struct sf *tsf;
26873d19cdaeSstevel 	int tgt_id;
26883d19cdaeSstevel 	struct la_els_logi *ptr = (struct la_els_logi *)privp->rsp;
26893d19cdaeSstevel 	struct la_els_adisc *adisc = (struct la_els_adisc *)ptr;
26903d19cdaeSstevel 	struct	sf_target *target;
26913d19cdaeSstevel 	short	ncmds;
26923d19cdaeSstevel 	short	free_pkt = TRUE;
26933d19cdaeSstevel 
26943d19cdaeSstevel 
26953d19cdaeSstevel 	/*
26963d19cdaeSstevel 	 * we've received an ELS callback, i.e. an ELS packet has arrived
26973d19cdaeSstevel 	 */
26983d19cdaeSstevel 
26993d19cdaeSstevel 	/* take the current packet off of the queue */
27003d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
27013d19cdaeSstevel 	if (privp->timeout == SF_INVALID_TIMEOUT) {
27023d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
27033d19cdaeSstevel 		return;
27043d19cdaeSstevel 	}
27053d19cdaeSstevel 	if (privp->prev != NULL) {
27063d19cdaeSstevel 		privp->prev->next = privp->next;
27073d19cdaeSstevel 	}
27083d19cdaeSstevel 	if (privp->next != NULL) {
27093d19cdaeSstevel 		privp->next->prev = privp->prev;
27103d19cdaeSstevel 	}
27113d19cdaeSstevel 	if (sf->sf_els_list == privp) {
27123d19cdaeSstevel 		sf->sf_els_list = privp->next;
27133d19cdaeSstevel 	}
27143d19cdaeSstevel 	privp->prev = privp->next = NULL;
27153d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
27163d19cdaeSstevel 
27173d19cdaeSstevel 	/* get # pkts in this callback */
27183d19cdaeSstevel 	ncmds = fpkt->fcal_ncmds;
27193d19cdaeSstevel 	ASSERT(ncmds >= 0);
27203d19cdaeSstevel 	mutex_enter(&sf->sf_cmd_mutex);
27213d19cdaeSstevel 	sf->sf_ncmds = ncmds;
27223d19cdaeSstevel 	mutex_exit(&sf->sf_cmd_mutex);
27233d19cdaeSstevel 
27243d19cdaeSstevel 	/* sync idea of memory */
27253d19cdaeSstevel 	(void) ddi_dma_sync(privp->rsp_dma_handle, (off_t)0, (size_t)0,
27263d19cdaeSstevel 	    DDI_DMA_SYNC_FORKERNEL);
27273d19cdaeSstevel 
27283d19cdaeSstevel 	/* was this an OK ACC msg ?? */
27293d19cdaeSstevel 	if ((fpkt->fcal_pkt_status == FCAL_STATUS_OK) &&
27303d19cdaeSstevel 	    (ptr->ls_code == LA_ELS_ACC)) {
27313d19cdaeSstevel 
27323d19cdaeSstevel 		/*
27333d19cdaeSstevel 		 * this was an OK ACC pkt
27343d19cdaeSstevel 		 */
27353d19cdaeSstevel 
27363d19cdaeSstevel 		switch (privp->els_code) {
27373d19cdaeSstevel 		case LA_ELS_PLOGI:
27383d19cdaeSstevel 			/*
27393d19cdaeSstevel 			 * was able to to an N_port login
27403d19cdaeSstevel 			 */
27413d19cdaeSstevel 			SF_DEBUG(2, (sf, CE_CONT,
27423d19cdaeSstevel 			    "!PLOGI to al_pa %x succeeded, wwn %x%x\n",
27433d19cdaeSstevel 			    privp->dest_nport_id,
27443d19cdaeSstevel 			    *((int *)&ptr->nport_ww_name.raw_wwn[0]),
27453d19cdaeSstevel 			    *((int *)&ptr->nport_ww_name.raw_wwn[4])));
27463d19cdaeSstevel 			/* try to do a process login */
27473d19cdaeSstevel 			if (!sf_do_prli(sf, privp, ptr)) {
27483d19cdaeSstevel 				free_pkt = FALSE;
27493d19cdaeSstevel 				goto fail;	/* PRLI failed */
27503d19cdaeSstevel 			}
27513d19cdaeSstevel 			break;
27523d19cdaeSstevel 		case LA_ELS_PRLI:
27533d19cdaeSstevel 			/*
27543d19cdaeSstevel 			 * was able to do a process login
27553d19cdaeSstevel 			 */
27563d19cdaeSstevel 			SF_DEBUG(2, (sf, CE_CONT,
27573d19cdaeSstevel 			    "!PRLI to al_pa %x succeeded\n",
27583d19cdaeSstevel 			    privp->dest_nport_id));
27593d19cdaeSstevel 			/* try to do address discovery */
27603d19cdaeSstevel 			if (sf_do_adisc(sf, privp) != 1) {
27613d19cdaeSstevel 				free_pkt = FALSE;
27623d19cdaeSstevel 				goto fail;	/* ADISC failed */
27633d19cdaeSstevel 			}
27643d19cdaeSstevel 			break;
27653d19cdaeSstevel 		case LA_ELS_ADISC:
27663d19cdaeSstevel 			/*
27673d19cdaeSstevel 			 * found a target via ADISC
27683d19cdaeSstevel 			 */
27693d19cdaeSstevel 
27703d19cdaeSstevel 			SF_DEBUG(2, (sf, CE_CONT,
27713d19cdaeSstevel 			    "!ADISC to al_pa %x succeeded\n",
27723d19cdaeSstevel 			    privp->dest_nport_id));
27733d19cdaeSstevel 
27743d19cdaeSstevel 			/* create the target info */
27753d19cdaeSstevel 			if ((target = sf_create_target(sf, privp,
27763d19cdaeSstevel 			    sf_alpa_to_switch[(uchar_t)adisc->hard_address],
27773d19cdaeSstevel 			    (int64_t)0))
27783d19cdaeSstevel 			    == NULL) {
27793d19cdaeSstevel 				goto fail;	/* can't create target */
27803d19cdaeSstevel 			}
27813d19cdaeSstevel 
27823d19cdaeSstevel 			/*
27833d19cdaeSstevel 			 * ensure address discovered matches what we thought
27843d19cdaeSstevel 			 * it would be
27853d19cdaeSstevel 			 */
27863d19cdaeSstevel 			if ((uchar_t)adisc->hard_address !=
27873d19cdaeSstevel 			    privp->dest_nport_id) {
27883d19cdaeSstevel 				sf_log(sf, CE_WARN,
27893d19cdaeSstevel 				    "target 0x%x, AL-PA 0x%x and "
27903d19cdaeSstevel 				    "hard address 0x%x don't match\n",
27913d19cdaeSstevel 				    sf_alpa_to_switch[
27923d19cdaeSstevel 				    (uchar_t)privp->dest_nport_id],
27933d19cdaeSstevel 				    privp->dest_nport_id,
27943d19cdaeSstevel 				    (uchar_t)adisc->hard_address);
27953d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
27963d19cdaeSstevel 				sf_offline_target(sf, target);
27973d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
27983d19cdaeSstevel 				goto fail;	/* addr doesn't match */
27993d19cdaeSstevel 			}
28003d19cdaeSstevel 			/*
28013d19cdaeSstevel 			 * get inquiry data from the target
28023d19cdaeSstevel 			 */
28033d19cdaeSstevel 			if (!sf_do_reportlun(sf, privp, target)) {
28043d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
28053d19cdaeSstevel 				sf_offline_target(sf, target);
28063d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
28073d19cdaeSstevel 				free_pkt = FALSE;
28083d19cdaeSstevel 				goto fail;	/* inquiry failed */
28093d19cdaeSstevel 			}
28103d19cdaeSstevel 			break;
28113d19cdaeSstevel 		default:
28123d19cdaeSstevel 			SF_DEBUG(2, (sf, CE_CONT,
28133d19cdaeSstevel 			    "!ELS %x to al_pa %x succeeded\n",
28143d19cdaeSstevel 			    privp->els_code, privp->dest_nport_id));
28153d19cdaeSstevel 			sf_els_free(fpkt);
28163d19cdaeSstevel 			break;
28173d19cdaeSstevel 		}
28183d19cdaeSstevel 
28193d19cdaeSstevel 	} else {
28203d19cdaeSstevel 
28213d19cdaeSstevel 		/*
28223d19cdaeSstevel 		 * oh oh -- this was not an OK ACC packet
28233d19cdaeSstevel 		 */
28243d19cdaeSstevel 
28253d19cdaeSstevel 		/* get target ID from dest loop address */
28263d19cdaeSstevel 		tgt_id = sf_alpa_to_switch[(uchar_t)privp->dest_nport_id];
28273d19cdaeSstevel 
28283d19cdaeSstevel 		/* keep track of failures */
28293d19cdaeSstevel 		sf->sf_stats.tstats[tgt_id].els_failures++;
28303d19cdaeSstevel 		if (++(privp->retries) < sf_els_retries &&
28313d19cdaeSstevel 		    fpkt->fcal_pkt_status != FCAL_STATUS_OPEN_FAIL) {
28323d19cdaeSstevel 			if (fpkt->fcal_pkt_status ==
28333d19cdaeSstevel 			    FCAL_STATUS_MAX_XCHG_EXCEEDED)  {
28343d19cdaeSstevel 				tsf = sf->sf_sibling;
28353d19cdaeSstevel 				if (tsf != NULL) {
28363d19cdaeSstevel 					mutex_enter(&tsf->sf_cmd_mutex);
28373d19cdaeSstevel 					tsf->sf_flag = 1;
28383d19cdaeSstevel 					tsf->sf_throttle = SF_DECR_DELTA;
28393d19cdaeSstevel 					mutex_exit(&tsf->sf_cmd_mutex);
28403d19cdaeSstevel 				}
28413d19cdaeSstevel 			}
28423d19cdaeSstevel 			privp->timeout = sf_watchdog_time + SF_ELS_TIMEOUT;
28433d19cdaeSstevel 			privp->prev = NULL;
28443d19cdaeSstevel 
28453d19cdaeSstevel 			mutex_enter(&sf->sf_mutex);
28463d19cdaeSstevel 
28473d19cdaeSstevel 			if (privp->lip_cnt == sf->sf_lip_cnt) {
28483d19cdaeSstevel 				SF_DEBUG(1, (sf, CE_WARN,
28493d19cdaeSstevel 				    "!ELS %x to al_pa %x failed, retrying",
28503d19cdaeSstevel 				    privp->els_code, privp->dest_nport_id));
28513d19cdaeSstevel 				privp->next = sf->sf_els_list;
28523d19cdaeSstevel 				if (sf->sf_els_list != NULL) {
28533d19cdaeSstevel 					sf->sf_els_list->prev = privp;
28543d19cdaeSstevel 				}
28553d19cdaeSstevel 
28563d19cdaeSstevel 				sf->sf_els_list = privp;
28573d19cdaeSstevel 
28583d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
28593d19cdaeSstevel 				/* device busy?  wait a bit ... */
28603d19cdaeSstevel 				if (fpkt->fcal_pkt_status ==
28613d19cdaeSstevel 				    FCAL_STATUS_MAX_XCHG_EXCEEDED)  {
28623d19cdaeSstevel 					privp->delayed_retry = 1;
28633d19cdaeSstevel 					return;
28643d19cdaeSstevel 				}
28653d19cdaeSstevel 				/* call the transport to send a pkt */
28663d19cdaeSstevel 				if (soc_transport(sf->sf_sochandle, fpkt,
28673d19cdaeSstevel 				    FCAL_NOSLEEP, CQ_REQUEST_1) !=
28683d19cdaeSstevel 				    FCAL_TRANSPORT_SUCCESS) {
28693d19cdaeSstevel 					mutex_enter(&sf->sf_mutex);
28703d19cdaeSstevel 					if (privp->prev != NULL) {
28713d19cdaeSstevel 						privp->prev->next =
28723d19cdaeSstevel 						    privp->next;
28733d19cdaeSstevel 					}
28743d19cdaeSstevel 					if (privp->next != NULL) {
28753d19cdaeSstevel 						privp->next->prev =
28763d19cdaeSstevel 						    privp->prev;
28773d19cdaeSstevel 					}
28783d19cdaeSstevel 					if (sf->sf_els_list == privp) {
28793d19cdaeSstevel 						sf->sf_els_list = privp->next;
28803d19cdaeSstevel 					}
28813d19cdaeSstevel 					mutex_exit(&sf->sf_mutex);
28823d19cdaeSstevel 					goto fail;
28833d19cdaeSstevel 				} else
28843d19cdaeSstevel 					return;
28853d19cdaeSstevel 			} else {
28863d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
28873d19cdaeSstevel 				goto fail;
28883d19cdaeSstevel 			}
28893d19cdaeSstevel 		} else {
28903d19cdaeSstevel #ifdef	DEBUG
28913d19cdaeSstevel 			if (fpkt->fcal_pkt_status != 0x36 || sfdebug > 4) {
28923d19cdaeSstevel 			SF_DEBUG(2, (sf, CE_NOTE, "ELS %x to al_pa %x failed",
28933d19cdaeSstevel 			    privp->els_code, privp->dest_nport_id));
28943d19cdaeSstevel 			if (fpkt->fcal_pkt_status == FCAL_STATUS_OK) {
28953d19cdaeSstevel 				SF_DEBUG(2, (sf, CE_NOTE,
28963d19cdaeSstevel 				    "els reply code = %x", ptr->ls_code));
28973d19cdaeSstevel 				if (ptr->ls_code == LA_ELS_RJT)
28983d19cdaeSstevel 					SF_DEBUG(1, (sf, CE_CONT,
28993d19cdaeSstevel 					    "LS_RJT reason = %x\n",
29003d19cdaeSstevel 					    *(((uint_t *)ptr) + 1)));
29013d19cdaeSstevel 			} else
29023d19cdaeSstevel 				SF_DEBUG(2, (sf, CE_NOTE,
29033d19cdaeSstevel 				    "fc packet status = %x",
29043d19cdaeSstevel 				    fpkt->fcal_pkt_status));
29053d19cdaeSstevel 			}
29063d19cdaeSstevel #endif
29073d19cdaeSstevel 			goto fail;
29083d19cdaeSstevel 		}
29093d19cdaeSstevel 	}
29103d19cdaeSstevel 	return;					/* success */
29113d19cdaeSstevel fail:
29123d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
29133d19cdaeSstevel 	if (sf->sf_lip_cnt == privp->lip_cnt) {
29143d19cdaeSstevel 		sf->sf_device_count--;
29153d19cdaeSstevel 		ASSERT(sf->sf_device_count >= 0);
29163d19cdaeSstevel 		if (sf->sf_device_count == 0) {
29173d19cdaeSstevel 			sf_finish_init(sf, privp->lip_cnt);
29183d19cdaeSstevel 		}
29193d19cdaeSstevel 	}
29203d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
29213d19cdaeSstevel 	if (free_pkt) {
29223d19cdaeSstevel 		sf_els_free(fpkt);
29233d19cdaeSstevel 	}
29243d19cdaeSstevel }
29253d19cdaeSstevel 
29263d19cdaeSstevel 
29273d19cdaeSstevel /*
29283d19cdaeSstevel  * send a PRLI (process login) ELS IU via the transport,
29293d19cdaeSstevel  * returning TRUE upon success, else returning FALSE
29303d19cdaeSstevel  */
29313d19cdaeSstevel static int
29323d19cdaeSstevel sf_do_prli(struct sf *sf, struct sf_els_hdr *privp, struct la_els_logi *ptr)
29333d19cdaeSstevel {
29343d19cdaeSstevel 	struct la_els_prli	*prli = (struct la_els_prli *)privp->cmd;
29353d19cdaeSstevel 	struct fcp_prli		*fprli;
29363d19cdaeSstevel 	struct  fcal_packet	*fpkt = privp->fpkt;
29373d19cdaeSstevel 
29383d19cdaeSstevel 
29393d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[0].fc_count =
29403d19cdaeSstevel 	    sizeof (struct la_els_prli);
29413d19cdaeSstevel 	privp->els_code = LA_ELS_PRLI;
29423d19cdaeSstevel 	fprli = (struct fcp_prli *)prli->service_params;
29433d19cdaeSstevel 	prli->ls_code = LA_ELS_PRLI;
29443d19cdaeSstevel 	prli->page_length = 0x10;
29453d19cdaeSstevel 	prli->payload_length = sizeof (struct la_els_prli);
29463d19cdaeSstevel 	fprli->type = 0x08;			/* no define here? */
29473d19cdaeSstevel 	fprli->resvd1 = 0;
29483d19cdaeSstevel 	fprli->orig_process_assoc_valid = 0;
29493d19cdaeSstevel 	fprli->resp_process_assoc_valid = 0;
29503d19cdaeSstevel 	fprli->establish_image_pair = 1;
29513d19cdaeSstevel 	fprli->resvd2 = 0;
29523d19cdaeSstevel 	fprli->resvd3 = 0;
29533d19cdaeSstevel 	fprli->data_overlay_allowed = 0;
29543d19cdaeSstevel 	fprli->initiator_fn = 1;
29553d19cdaeSstevel 	fprli->target_fn = 0;
29563d19cdaeSstevel 	fprli->cmd_data_mixed = 0;
29573d19cdaeSstevel 	fprli->data_resp_mixed = 0;
29583d19cdaeSstevel 	fprli->read_xfer_rdy_disabled = 1;
29593d19cdaeSstevel 	fprli->write_xfer_rdy_disabled = 0;
29603d19cdaeSstevel 
29613d19cdaeSstevel 	bcopy((caddr_t)&ptr->nport_ww_name, (caddr_t)&privp->port_wwn,
29623d19cdaeSstevel 	    sizeof (privp->port_wwn));
29633d19cdaeSstevel 	bcopy((caddr_t)&ptr->node_ww_name, (caddr_t)&privp->node_wwn,
29643d19cdaeSstevel 	    sizeof (privp->node_wwn));
29653d19cdaeSstevel 
29663d19cdaeSstevel 	privp->timeout = sf_watchdog_time + SF_ELS_TIMEOUT;
29673d19cdaeSstevel 	return (sf_els_transport(sf, privp));
29683d19cdaeSstevel }
29693d19cdaeSstevel 
29703d19cdaeSstevel 
29713d19cdaeSstevel /*
29723d19cdaeSstevel  * send an ADISC (address discovery) ELS IU via the transport,
29733d19cdaeSstevel  * returning TRUE upon success, else returning FALSE
29743d19cdaeSstevel  */
29753d19cdaeSstevel static int
29763d19cdaeSstevel sf_do_adisc(struct sf *sf, struct sf_els_hdr *privp)
29773d19cdaeSstevel {
29783d19cdaeSstevel 	struct la_els_adisc	*adisc = (struct la_els_adisc *)privp->cmd;
29793d19cdaeSstevel 	struct	fcal_packet	*fpkt = privp->fpkt;
29803d19cdaeSstevel 
29813d19cdaeSstevel 	privp->els_code = LA_ELS_ADISC;
29823d19cdaeSstevel 	adisc->ls_code = LA_ELS_ADISC;
29833d19cdaeSstevel 	adisc->mbz[0] = 0;
29843d19cdaeSstevel 	adisc->mbz[1] = 0;
29853d19cdaeSstevel 	adisc->mbz[2] = 0;
29863d19cdaeSstevel 	adisc->hard_address = 0; /* ??? */
29873d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[0].fc_count =
29883d19cdaeSstevel 	    sizeof (struct la_els_adisc);
29893d19cdaeSstevel 	bcopy((caddr_t)&sf->sf_sochandle->fcal_p_wwn,
29903d19cdaeSstevel 	    (caddr_t)&adisc->port_wwn, sizeof (adisc->port_wwn));
29913d19cdaeSstevel 	bcopy((caddr_t)&sf->sf_sochandle->fcal_n_wwn,
29923d19cdaeSstevel 	    (caddr_t)&adisc->node_wwn, sizeof (adisc->node_wwn));
29933d19cdaeSstevel 	adisc->nport_id = sf->sf_al_pa;
29943d19cdaeSstevel 
29953d19cdaeSstevel 	privp->timeout = sf_watchdog_time + SF_ELS_TIMEOUT;
29963d19cdaeSstevel 	return (sf_els_transport(sf, privp));
29973d19cdaeSstevel }
29983d19cdaeSstevel 
29993d19cdaeSstevel 
30003d19cdaeSstevel static struct fcal_packet *
30013d19cdaeSstevel sf_els_alloc(struct sf *sf, uchar_t dest_id, int priv_size, int cmd_size,
30023d19cdaeSstevel     int rsp_size, caddr_t *rprivp, caddr_t *cmd_buf)
30033d19cdaeSstevel {
30043d19cdaeSstevel 	struct	fcal_packet	*fpkt;
30053d19cdaeSstevel 	ddi_dma_cookie_t	pcookie;
30063d19cdaeSstevel 	ddi_dma_cookie_t	rcookie;
30073d19cdaeSstevel 	struct	sf_els_hdr	*privp;
30083d19cdaeSstevel 	ddi_dma_handle_t	cmd_dma_handle = NULL;
30093d19cdaeSstevel 	ddi_dma_handle_t	rsp_dma_handle = NULL;
30103d19cdaeSstevel 	ddi_acc_handle_t	cmd_acc_handle = NULL;
30113d19cdaeSstevel 	ddi_acc_handle_t	rsp_acc_handle = NULL;
30123d19cdaeSstevel 	size_t			real_size;
30133d19cdaeSstevel 	uint_t			ccount;
30143d19cdaeSstevel 	fc_frame_header_t	*hp;
30153d19cdaeSstevel 	int			cmd_bound = FALSE, rsp_bound = FALSE;
30163d19cdaeSstevel 	caddr_t			cmd = NULL;
30173d19cdaeSstevel 	caddr_t			rsp = NULL;
30183d19cdaeSstevel 
30193d19cdaeSstevel 	if ((fpkt = (struct fcal_packet *)kmem_zalloc(
30203d19cdaeSstevel 	    sizeof (struct fcal_packet), KM_NOSLEEP)) == NULL) {
30213d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30223d19cdaeSstevel 			"Could not allocate fcal_packet for ELS\n"));
30233d19cdaeSstevel 		return (NULL);
30243d19cdaeSstevel 	}
30253d19cdaeSstevel 
30263d19cdaeSstevel 	if ((privp = (struct sf_els_hdr *)kmem_zalloc(priv_size,
30273d19cdaeSstevel 	    KM_NOSLEEP)) == NULL) {
30283d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30293d19cdaeSstevel 		    "Could not allocate sf_els_hdr for ELS\n"));
30303d19cdaeSstevel 		goto fail;
30313d19cdaeSstevel 	}
30323d19cdaeSstevel 
30333d19cdaeSstevel 	privp->size = priv_size;
30343d19cdaeSstevel 	fpkt->fcal_pkt_private = (caddr_t)privp;
30353d19cdaeSstevel 
30363d19cdaeSstevel 	if (ddi_dma_alloc_handle(sf->sf_dip, sf->sf_sochandle->fcal_dmaattr,
30373d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &cmd_dma_handle) != DDI_SUCCESS) {
30383d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30393d19cdaeSstevel 		    "Could not allocate DMA handle for ELS\n"));
30403d19cdaeSstevel 		goto fail;
30413d19cdaeSstevel 	}
30423d19cdaeSstevel 
30433d19cdaeSstevel 	if (ddi_dma_mem_alloc(cmd_dma_handle, cmd_size,
30443d19cdaeSstevel 	    sf->sf_sochandle->fcal_accattr, DDI_DMA_CONSISTENT,
30453d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &cmd,
30463d19cdaeSstevel 	    &real_size, &cmd_acc_handle) != DDI_SUCCESS) {
30473d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30483d19cdaeSstevel 		    "Could not allocate DMA memory for ELS\n"));
30493d19cdaeSstevel 		goto fail;
30503d19cdaeSstevel 	}
30513d19cdaeSstevel 
30523d19cdaeSstevel 	if (real_size < cmd_size) {
30533d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30543d19cdaeSstevel 		    "DMA memory too small for ELS\n"));
30553d19cdaeSstevel 		goto fail;
30563d19cdaeSstevel 	}
30573d19cdaeSstevel 
30583d19cdaeSstevel 	if (ddi_dma_addr_bind_handle(cmd_dma_handle, NULL,
30593d19cdaeSstevel 	    cmd, real_size, DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
30603d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &pcookie, &ccount) != DDI_DMA_MAPPED) {
30613d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30623d19cdaeSstevel 		    "Could not bind DMA memory for ELS\n"));
30633d19cdaeSstevel 		goto fail;
30643d19cdaeSstevel 	}
30653d19cdaeSstevel 	cmd_bound = TRUE;
30663d19cdaeSstevel 
30673d19cdaeSstevel 	if (ccount != 1) {
30683d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30693d19cdaeSstevel 		    "Wrong cookie count for ELS\n"));
30703d19cdaeSstevel 		goto fail;
30713d19cdaeSstevel 	}
30723d19cdaeSstevel 
30733d19cdaeSstevel 	if (ddi_dma_alloc_handle(sf->sf_dip, sf->sf_sochandle->fcal_dmaattr,
30743d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &rsp_dma_handle) != DDI_SUCCESS) {
30753d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30763d19cdaeSstevel 		    "Could not allocate DMA handle for ELS rsp\n"));
30773d19cdaeSstevel 		goto fail;
30783d19cdaeSstevel 	}
30793d19cdaeSstevel 	if (ddi_dma_mem_alloc(rsp_dma_handle, rsp_size,
30803d19cdaeSstevel 	    sf->sf_sochandle->fcal_accattr, DDI_DMA_CONSISTENT,
30813d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &rsp,
30823d19cdaeSstevel 	    &real_size, &rsp_acc_handle) != DDI_SUCCESS) {
30833d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30843d19cdaeSstevel 		    "Could not allocate DMA memory for ELS rsp\n"));
30853d19cdaeSstevel 		goto fail;
30863d19cdaeSstevel 	}
30873d19cdaeSstevel 
30883d19cdaeSstevel 	if (real_size < rsp_size) {
30893d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30903d19cdaeSstevel 		    "DMA memory too small for ELS rsp\n"));
30913d19cdaeSstevel 		goto fail;
30923d19cdaeSstevel 	}
30933d19cdaeSstevel 
30943d19cdaeSstevel 	if (ddi_dma_addr_bind_handle(rsp_dma_handle, NULL,
30953d19cdaeSstevel 	    rsp, real_size, DDI_DMA_READ | DDI_DMA_CONSISTENT,
30963d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &rcookie, &ccount) != DDI_DMA_MAPPED) {
30973d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
30983d19cdaeSstevel 		    "Could not bind DMA memory for ELS rsp\n"));
30993d19cdaeSstevel 		goto fail;
31003d19cdaeSstevel 	}
31013d19cdaeSstevel 	rsp_bound = TRUE;
31023d19cdaeSstevel 
31033d19cdaeSstevel 	if (ccount != 1) {
31043d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_WARN,
31053d19cdaeSstevel 		    "Wrong cookie count for ELS rsp\n"));
31063d19cdaeSstevel 		goto fail;
31073d19cdaeSstevel 	}
31083d19cdaeSstevel 
31093d19cdaeSstevel 	privp->cmd = cmd;
31103d19cdaeSstevel 	privp->sf = sf;
31113d19cdaeSstevel 	privp->cmd_dma_handle = cmd_dma_handle;
31123d19cdaeSstevel 	privp->cmd_acc_handle = cmd_acc_handle;
31133d19cdaeSstevel 	privp->rsp = rsp;
31143d19cdaeSstevel 	privp->rsp_dma_handle = rsp_dma_handle;
31153d19cdaeSstevel 	privp->rsp_acc_handle = rsp_acc_handle;
31163d19cdaeSstevel 	privp->dest_nport_id = dest_id;
31173d19cdaeSstevel 	privp->fpkt = fpkt;
31183d19cdaeSstevel 
31193d19cdaeSstevel 	fpkt->fcal_pkt_cookie = sf->sf_socp;
31203d19cdaeSstevel 	fpkt->fcal_pkt_comp = sf_els_callback;
31213d19cdaeSstevel 	fpkt->fcal_magic = FCALP_MAGIC;
31223d19cdaeSstevel 	fpkt->fcal_pkt_flags = 0;
31233d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_flags =
31243d19cdaeSstevel 	    (ushort_t)(SOC_FC_HEADER | sf->sf_sochandle->fcal_portno);
31253d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_class = 3;
31263d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_seg_cnt = 2;
31273d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_byte_cnt = cmd_size;
31283d19cdaeSstevel 	fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_count = 1;
31293d19cdaeSstevel 	fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_flags = 0;
31303d19cdaeSstevel 	fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_seqno = 0;
31313d19cdaeSstevel 	fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_type = CQ_TYPE_SIMPLE;
31323d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[0].fc_base = (uint32_t)
31333d19cdaeSstevel 	    pcookie.dmac_address;
31343d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[0].fc_count = cmd_size;
31353d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[1].fc_base = (uint32_t)
31363d19cdaeSstevel 	    rcookie.dmac_address;
31373d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[1].fc_count = rsp_size;
31383d19cdaeSstevel 
31393d19cdaeSstevel 	/* Fill in the Fabric Channel Header */
31403d19cdaeSstevel 	hp = &fpkt->fcal_socal_request.sr_fc_frame_hdr;
31413d19cdaeSstevel 	hp->r_ctl = R_CTL_ELS_REQ;
31423d19cdaeSstevel 	hp->d_id = dest_id;
31433d19cdaeSstevel 	hp->s_id = sf->sf_al_pa;
31443d19cdaeSstevel 	hp->type = TYPE_EXTENDED_LS;
31453d19cdaeSstevel 	hp->reserved1 = 0;
31463d19cdaeSstevel 	hp->f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
31473d19cdaeSstevel 	hp->seq_id = 0;
31483d19cdaeSstevel 	hp->df_ctl  = 0;
31493d19cdaeSstevel 	hp->seq_cnt = 0;
31503d19cdaeSstevel 	hp->ox_id = 0xffff;
31513d19cdaeSstevel 	hp->rx_id = 0xffff;
31523d19cdaeSstevel 	hp->ro = 0;
31533d19cdaeSstevel 
31543d19cdaeSstevel 	*rprivp = (caddr_t)privp;
31553d19cdaeSstevel 	*cmd_buf = cmd;
31563d19cdaeSstevel 	return (fpkt);
31573d19cdaeSstevel 
31583d19cdaeSstevel fail:
31593d19cdaeSstevel 	if (cmd_dma_handle != NULL) {
31603d19cdaeSstevel 		if (cmd_bound) {
31613d19cdaeSstevel 			(void) ddi_dma_unbind_handle(cmd_dma_handle);
31623d19cdaeSstevel 		}
31633d19cdaeSstevel 		ddi_dma_free_handle(&cmd_dma_handle);
31643d19cdaeSstevel 		privp->cmd_dma_handle = NULL;
31653d19cdaeSstevel 	}
31663d19cdaeSstevel 	if (rsp_dma_handle != NULL) {
31673d19cdaeSstevel 		if (rsp_bound) {
31683d19cdaeSstevel 			(void) ddi_dma_unbind_handle(rsp_dma_handle);
31693d19cdaeSstevel 		}
31703d19cdaeSstevel 		ddi_dma_free_handle(&rsp_dma_handle);
31713d19cdaeSstevel 		privp->rsp_dma_handle = NULL;
31723d19cdaeSstevel 	}
31733d19cdaeSstevel 	sf_els_free(fpkt);
31743d19cdaeSstevel 	return (NULL);
31753d19cdaeSstevel }
31763d19cdaeSstevel 
31773d19cdaeSstevel 
31783d19cdaeSstevel static void
31793d19cdaeSstevel sf_els_free(struct fcal_packet *fpkt)
31803d19cdaeSstevel {
31813d19cdaeSstevel 	struct	sf_els_hdr	*privp = fpkt->fcal_pkt_private;
31823d19cdaeSstevel 
31833d19cdaeSstevel 	if (privp != NULL) {
31843d19cdaeSstevel 		if (privp->cmd_dma_handle != NULL) {
31853d19cdaeSstevel 			(void) ddi_dma_unbind_handle(privp->cmd_dma_handle);
31863d19cdaeSstevel 			ddi_dma_free_handle(&privp->cmd_dma_handle);
31873d19cdaeSstevel 		}
31883d19cdaeSstevel 		if (privp->cmd != NULL) {
31893d19cdaeSstevel 			ddi_dma_mem_free(&privp->cmd_acc_handle);
31903d19cdaeSstevel 		}
31913d19cdaeSstevel 
31923d19cdaeSstevel 		if (privp->rsp_dma_handle != NULL) {
31933d19cdaeSstevel 			(void) ddi_dma_unbind_handle(privp->rsp_dma_handle);
31943d19cdaeSstevel 			ddi_dma_free_handle(&privp->rsp_dma_handle);
31953d19cdaeSstevel 		}
31963d19cdaeSstevel 
31973d19cdaeSstevel 		if (privp->rsp != NULL) {
31983d19cdaeSstevel 			ddi_dma_mem_free(&privp->rsp_acc_handle);
31993d19cdaeSstevel 		}
32003d19cdaeSstevel 		if (privp->data_dma_handle) {
32013d19cdaeSstevel 			(void) ddi_dma_unbind_handle(privp->data_dma_handle);
32023d19cdaeSstevel 			ddi_dma_free_handle(&privp->data_dma_handle);
32033d19cdaeSstevel 		}
32043d19cdaeSstevel 		if (privp->data_buf) {
32053d19cdaeSstevel 			ddi_dma_mem_free(&privp->data_acc_handle);
32063d19cdaeSstevel 		}
32073d19cdaeSstevel 		kmem_free(privp, privp->size);
32083d19cdaeSstevel 	}
32093d19cdaeSstevel 	kmem_free(fpkt, sizeof (struct fcal_packet));
32103d19cdaeSstevel }
32113d19cdaeSstevel 
32123d19cdaeSstevel 
32133d19cdaeSstevel static struct sf_target *
32143d19cdaeSstevel sf_create_target(struct sf *sf, struct sf_els_hdr *privp, int tnum, int64_t lun)
32153d19cdaeSstevel {
32163d19cdaeSstevel 	struct sf_target *target, *ntarget, *otarget, *ptarget;
32173d19cdaeSstevel 	int hash;
32183d19cdaeSstevel #ifdef RAID_LUNS
32193d19cdaeSstevel 	int64_t orig_lun = lun;
32203d19cdaeSstevel 
32213d19cdaeSstevel 	/* XXXX Work around SCSA limitations. */
32223d19cdaeSstevel 	lun = *((short *)&lun);
32233d19cdaeSstevel #endif
32243d19cdaeSstevel 	ntarget = kmem_zalloc(sizeof (struct sf_target), KM_NOSLEEP);
32253d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
32263d19cdaeSstevel 	if (sf->sf_lip_cnt != privp->lip_cnt) {
32273d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
32283d19cdaeSstevel 		if (ntarget != NULL)
32293d19cdaeSstevel 			kmem_free(ntarget, sizeof (struct sf_target));
32303d19cdaeSstevel 		return (NULL);
32313d19cdaeSstevel 	}
32323d19cdaeSstevel 
32333d19cdaeSstevel 	target = sf_lookup_target(sf, privp->port_wwn, lun);
32343d19cdaeSstevel 	if (lun != 0) {
32353d19cdaeSstevel 		/*
32363d19cdaeSstevel 		 * Since LUNs != 0 are queued up after LUN == 0, find LUN == 0
32373d19cdaeSstevel 		 * and enqueue the new LUN.
32383d19cdaeSstevel 		 */
32393d19cdaeSstevel 		if ((ptarget = sf_lookup_target(sf, privp->port_wwn,
32403d19cdaeSstevel 		    (int64_t)0)) ==	NULL) {
32413d19cdaeSstevel 			/*
32423d19cdaeSstevel 			 * Yeep -- no LUN 0?
32433d19cdaeSstevel 			 */
32443d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
32453d19cdaeSstevel 			sf_log(sf, CE_WARN, "target 0x%x "
32463d19cdaeSstevel 			    "lun %" PRIx64 ": No LUN 0\n", tnum, lun);
32473d19cdaeSstevel 			if (ntarget != NULL)
32483d19cdaeSstevel 				kmem_free(ntarget, sizeof (struct sf_target));
32493d19cdaeSstevel 			return (NULL);
32503d19cdaeSstevel 		}
32513d19cdaeSstevel 		mutex_enter(&ptarget->sft_mutex);
32523d19cdaeSstevel 		if (target != NULL && ptarget->sft_lip_cnt == sf->sf_lip_cnt &&
32533d19cdaeSstevel 		    ptarget->sft_state&SF_TARGET_OFFLINE) {
32543d19cdaeSstevel 			/* LUN 0 already finished, duplicate its state */
32553d19cdaeSstevel 			mutex_exit(&ptarget->sft_mutex);
32563d19cdaeSstevel 			sf_offline_target(sf, target);
32573d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
32583d19cdaeSstevel 			if (ntarget != NULL)
32593d19cdaeSstevel 				kmem_free(ntarget, sizeof (struct sf_target));
32603d19cdaeSstevel 			return (target);
32613d19cdaeSstevel 		} else if (target != NULL) {
32623d19cdaeSstevel 			/*
32633d19cdaeSstevel 			 * LUN 0 online or not examined yet.
32643d19cdaeSstevel 			 * Try to bring the LUN back online
32653d19cdaeSstevel 			 */
32663d19cdaeSstevel 			mutex_exit(&ptarget->sft_mutex);
32673d19cdaeSstevel 			mutex_enter(&target->sft_mutex);
32683d19cdaeSstevel 			target->sft_lip_cnt = privp->lip_cnt;
32693d19cdaeSstevel 			target->sft_state |= SF_TARGET_BUSY;
32703d19cdaeSstevel 			target->sft_state &= ~(SF_TARGET_OFFLINE|
32713d19cdaeSstevel 			    SF_TARGET_MARK);
32723d19cdaeSstevel 			target->sft_al_pa = (uchar_t)privp->dest_nport_id;
32733d19cdaeSstevel 			target->sft_hard_address = sf_switch_to_alpa[tnum];
32743d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
32753d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
32763d19cdaeSstevel 			if (ntarget != NULL)
32773d19cdaeSstevel 				kmem_free(ntarget, sizeof (struct sf_target));
32783d19cdaeSstevel 			return (target);
32793d19cdaeSstevel 		}
32803d19cdaeSstevel 		mutex_exit(&ptarget->sft_mutex);
32813d19cdaeSstevel 		if (ntarget == NULL) {
32823d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
32833d19cdaeSstevel 			return (NULL);
32843d19cdaeSstevel 		}
32853d19cdaeSstevel 		/* Initialize new target structure */
32863d19cdaeSstevel 		bcopy((caddr_t)&privp->node_wwn,
32873d19cdaeSstevel 		    (caddr_t)&ntarget->sft_node_wwn, sizeof (privp->node_wwn));
32883d19cdaeSstevel 		bcopy((caddr_t)&privp->port_wwn,
32893d19cdaeSstevel 		    (caddr_t)&ntarget->sft_port_wwn, sizeof (privp->port_wwn));
32903d19cdaeSstevel 		ntarget->sft_lun.l = lun;
32913d19cdaeSstevel #ifdef RAID_LUNS
32923d19cdaeSstevel 		ntarget->sft_lun.l = orig_lun;
32933d19cdaeSstevel 		ntarget->sft_raid_lun = (uint_t)lun;
32943d19cdaeSstevel #endif
32953d19cdaeSstevel 		mutex_init(&ntarget->sft_mutex, NULL, MUTEX_DRIVER, NULL);
32963d19cdaeSstevel 		mutex_init(&ntarget->sft_pkt_mutex, NULL, MUTEX_DRIVER, NULL);
32973d19cdaeSstevel 		/* Don't let anyone use this till we finishup init. */
32983d19cdaeSstevel 		mutex_enter(&ntarget->sft_mutex);
32993d19cdaeSstevel 		mutex_enter(&ntarget->sft_pkt_mutex);
33003d19cdaeSstevel 
33013d19cdaeSstevel 		hash = SF_HASH(privp->port_wwn, lun);
33023d19cdaeSstevel 		ntarget->sft_next = sf->sf_wwn_lists[hash];
33033d19cdaeSstevel 		sf->sf_wwn_lists[hash] = ntarget;
33043d19cdaeSstevel 
33053d19cdaeSstevel 		ntarget->sft_lip_cnt = privp->lip_cnt;
33063d19cdaeSstevel 		ntarget->sft_al_pa = (uchar_t)privp->dest_nport_id;
33073d19cdaeSstevel 		ntarget->sft_hard_address = sf_switch_to_alpa[tnum];
33083d19cdaeSstevel 		ntarget->sft_device_type = DTYPE_UNKNOWN;
33093d19cdaeSstevel 		ntarget->sft_state = SF_TARGET_BUSY;
33103d19cdaeSstevel 		ntarget->sft_pkt_head = (struct sf_pkt *)&ntarget->
33113d19cdaeSstevel 		    sft_pkt_head;
33123d19cdaeSstevel 		ntarget->sft_pkt_tail = (struct sf_pkt *)&ntarget->
33133d19cdaeSstevel 		    sft_pkt_head;
33143d19cdaeSstevel 
33153d19cdaeSstevel 		mutex_enter(&ptarget->sft_mutex);
33163d19cdaeSstevel 		/* Traverse the list looking for this target */
33173d19cdaeSstevel 		for (target = ptarget; target->sft_next_lun;
33183d19cdaeSstevel 		    target = target->sft_next_lun) {
33193d19cdaeSstevel 			otarget = target->sft_next_lun;
33203d19cdaeSstevel 		}
33213d19cdaeSstevel 		ntarget->sft_next_lun = target->sft_next_lun;
33223d19cdaeSstevel 		target->sft_next_lun = ntarget;
33233d19cdaeSstevel 		mutex_exit(&ptarget->sft_mutex);
33243d19cdaeSstevel 		mutex_exit(&ntarget->sft_pkt_mutex);
33253d19cdaeSstevel 		mutex_exit(&ntarget->sft_mutex);
33263d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
33273d19cdaeSstevel 		return (ntarget);
33283d19cdaeSstevel 
33293d19cdaeSstevel 	}
33303d19cdaeSstevel 	if (target != NULL && target->sft_lip_cnt == sf->sf_lip_cnt) {
33313d19cdaeSstevel 		/* It's been touched this LIP -- duplicate WWNs */
33323d19cdaeSstevel 		sf_offline_target(sf, target); /* And all the baby targets */
33333d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
33343d19cdaeSstevel 		sf_log(sf, CE_WARN, "target 0x%x, duplicate port wwns\n",
33353d19cdaeSstevel 		    tnum);
33363d19cdaeSstevel 		if (ntarget != NULL) {
33373d19cdaeSstevel 			kmem_free(ntarget, sizeof (struct sf_target));
33383d19cdaeSstevel 		}
33393d19cdaeSstevel 		return (NULL);
33403d19cdaeSstevel 	}
33413d19cdaeSstevel 
33423d19cdaeSstevel 	if ((otarget = sf->sf_targets[tnum]) != NULL) {
33433d19cdaeSstevel 		/* Someone else is in our slot */
33443d19cdaeSstevel 		mutex_enter(&otarget->sft_mutex);
33453d19cdaeSstevel 		if (otarget->sft_lip_cnt == sf->sf_lip_cnt) {
33463d19cdaeSstevel 			mutex_exit(&otarget->sft_mutex);
33473d19cdaeSstevel 			sf_offline_target(sf, otarget);
33483d19cdaeSstevel 			if (target != NULL)
33493d19cdaeSstevel 				sf_offline_target(sf, target);
33503d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
33513d19cdaeSstevel 			sf_log(sf, CE_WARN,
33523d19cdaeSstevel 			    "target 0x%x, duplicate switch settings\n", tnum);
33533d19cdaeSstevel 			if (ntarget != NULL)
33543d19cdaeSstevel 				kmem_free(ntarget, sizeof (struct sf_target));
33553d19cdaeSstevel 			return (NULL);
33563d19cdaeSstevel 		}
33573d19cdaeSstevel 		mutex_exit(&otarget->sft_mutex);
33583d19cdaeSstevel 		if (bcmp((caddr_t)&privp->port_wwn, (caddr_t)&otarget->
33593d19cdaeSstevel 		    sft_port_wwn, sizeof (privp->port_wwn))) {
33603d19cdaeSstevel 			sf_offline_target(sf, otarget);
33613d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
33623d19cdaeSstevel 			sf_log(sf, CE_WARN, "wwn changed on target 0x%x\n",
33633d19cdaeSstevel 			    tnum);
33643d19cdaeSstevel 			bzero((caddr_t)&sf->sf_stats.tstats[tnum],
33653d19cdaeSstevel 			    sizeof (struct sf_target_stats));
33663d19cdaeSstevel 			mutex_enter(&sf->sf_mutex);
33673d19cdaeSstevel 		}
33683d19cdaeSstevel 	}
33693d19cdaeSstevel 
33703d19cdaeSstevel 	sf->sf_targets[tnum] = target;
33713d19cdaeSstevel 	if ((target = sf->sf_targets[tnum]) == NULL) {
33723d19cdaeSstevel 		if (ntarget == NULL) {
33733d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
33743d19cdaeSstevel 			return (NULL);
33753d19cdaeSstevel 		}
33763d19cdaeSstevel 		bcopy((caddr_t)&privp->node_wwn,
33773d19cdaeSstevel 		    (caddr_t)&ntarget->sft_node_wwn, sizeof (privp->node_wwn));
33783d19cdaeSstevel 		bcopy((caddr_t)&privp->port_wwn,
33793d19cdaeSstevel 		    (caddr_t)&ntarget->sft_port_wwn, sizeof (privp->port_wwn));
33803d19cdaeSstevel 		ntarget->sft_lun.l = lun;
33813d19cdaeSstevel #ifdef RAID_LUNS
33823d19cdaeSstevel 		ntarget->sft_lun.l = orig_lun;
33833d19cdaeSstevel 		ntarget->sft_raid_lun = (uint_t)lun;
33843d19cdaeSstevel #endif
33853d19cdaeSstevel 		mutex_init(&ntarget->sft_mutex, NULL, MUTEX_DRIVER, NULL);
33863d19cdaeSstevel 		mutex_init(&ntarget->sft_pkt_mutex, NULL, MUTEX_DRIVER, NULL);
33873d19cdaeSstevel 		mutex_enter(&ntarget->sft_mutex);
33883d19cdaeSstevel 		mutex_enter(&ntarget->sft_pkt_mutex);
33893d19cdaeSstevel 		hash = SF_HASH(privp->port_wwn, lun); /* lun 0 */
33903d19cdaeSstevel 		ntarget->sft_next = sf->sf_wwn_lists[hash];
33913d19cdaeSstevel 		sf->sf_wwn_lists[hash] = ntarget;
33923d19cdaeSstevel 
33933d19cdaeSstevel 		target = ntarget;
33943d19cdaeSstevel 		target->sft_lip_cnt = privp->lip_cnt;
33953d19cdaeSstevel 		target->sft_al_pa = (uchar_t)privp->dest_nport_id;
33963d19cdaeSstevel 		target->sft_hard_address = sf_switch_to_alpa[tnum];
33973d19cdaeSstevel 		target->sft_device_type = DTYPE_UNKNOWN;
33983d19cdaeSstevel 		target->sft_state = SF_TARGET_BUSY;
33993d19cdaeSstevel 		target->sft_pkt_head = (struct sf_pkt *)&target->
34003d19cdaeSstevel 		    sft_pkt_head;
34013d19cdaeSstevel 		target->sft_pkt_tail = (struct sf_pkt *)&target->
34023d19cdaeSstevel 		    sft_pkt_head;
34033d19cdaeSstevel 		sf->sf_targets[tnum] = target;
34043d19cdaeSstevel 		mutex_exit(&ntarget->sft_mutex);
34053d19cdaeSstevel 		mutex_exit(&ntarget->sft_pkt_mutex);
34063d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
34073d19cdaeSstevel 	} else {
34083d19cdaeSstevel 		mutex_enter(&target->sft_mutex);
34093d19cdaeSstevel 		target->sft_lip_cnt = privp->lip_cnt;
34103d19cdaeSstevel 		target->sft_state |= SF_TARGET_BUSY;
34113d19cdaeSstevel 		target->sft_state &= ~(SF_TARGET_OFFLINE|SF_TARGET_MARK);
34123d19cdaeSstevel 		target->sft_al_pa = (uchar_t)privp->dest_nport_id;
34133d19cdaeSstevel 		target->sft_hard_address = sf_switch_to_alpa[tnum];
34143d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
34153d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
34163d19cdaeSstevel 		if (ntarget != NULL)
34173d19cdaeSstevel 			kmem_free(ntarget, sizeof (struct sf_target));
34183d19cdaeSstevel 	}
34193d19cdaeSstevel 	return (target);
34203d19cdaeSstevel }
34213d19cdaeSstevel 
34223d19cdaeSstevel 
34233d19cdaeSstevel /*
34243d19cdaeSstevel  * find the target for a given sf instance
34253d19cdaeSstevel  */
34263d19cdaeSstevel /* ARGSUSED */
34273d19cdaeSstevel static struct sf_target *
34283d19cdaeSstevel #ifdef RAID_LUNS
34293d19cdaeSstevel sf_lookup_target(struct sf *sf, uchar_t *wwn, int lun)
34303d19cdaeSstevel #else
34313d19cdaeSstevel sf_lookup_target(struct sf *sf, uchar_t *wwn, int64_t lun)
34323d19cdaeSstevel #endif
34333d19cdaeSstevel {
34343d19cdaeSstevel 	int hash;
34353d19cdaeSstevel 	struct sf_target *target;
34363d19cdaeSstevel 
34373d19cdaeSstevel 	ASSERT(mutex_owned(&sf->sf_mutex));
34383d19cdaeSstevel 	hash = SF_HASH(wwn, lun);
34393d19cdaeSstevel 
34403d19cdaeSstevel 	target = sf->sf_wwn_lists[hash];
34413d19cdaeSstevel 	while (target != NULL) {
34423d19cdaeSstevel 
34433d19cdaeSstevel #ifndef	RAID_LUNS
34443d19cdaeSstevel 		if (bcmp((caddr_t)wwn, (caddr_t)&target->sft_port_wwn,
34453d19cdaeSstevel 		    sizeof (target->sft_port_wwn)) == 0 &&
34463d19cdaeSstevel 			target->sft_lun.l == lun)
34473d19cdaeSstevel 			break;
34483d19cdaeSstevel #else
34493d19cdaeSstevel 		if (bcmp((caddr_t)wwn, (caddr_t)&target->sft_port_wwn,
34503d19cdaeSstevel 		    sizeof (target->sft_port_wwn)) == 0 &&
34513d19cdaeSstevel 			target->sft_raid_lun == lun)
34523d19cdaeSstevel 			break;
34533d19cdaeSstevel #endif
34543d19cdaeSstevel 		target = target->sft_next;
34553d19cdaeSstevel 	}
34563d19cdaeSstevel 
34573d19cdaeSstevel 	return (target);
34583d19cdaeSstevel }
34593d19cdaeSstevel 
34603d19cdaeSstevel 
34613d19cdaeSstevel /*
34623d19cdaeSstevel  * Send out a REPORT_LUNS command.
34633d19cdaeSstevel  */
34643d19cdaeSstevel static int
34653d19cdaeSstevel sf_do_reportlun(struct sf *sf, struct sf_els_hdr *privp,
34663d19cdaeSstevel     struct sf_target *target)
34673d19cdaeSstevel {
34683d19cdaeSstevel 	struct	fcal_packet	*fpkt = privp->fpkt;
34693d19cdaeSstevel 	ddi_dma_cookie_t	pcookie;
34703d19cdaeSstevel 	ddi_dma_handle_t	lun_dma_handle = NULL;
34713d19cdaeSstevel 	ddi_acc_handle_t	lun_acc_handle;
34723d19cdaeSstevel 	uint_t			ccount;
34733d19cdaeSstevel 	size_t			real_size;
34743d19cdaeSstevel 	caddr_t			lun_buf = NULL;
34753d19cdaeSstevel 	int			handle_bound = 0;
34763d19cdaeSstevel 	fc_frame_header_t	*hp = &fpkt->fcal_socal_request.sr_fc_frame_hdr;
34773d19cdaeSstevel 	struct fcp_cmd		*reportlun = (struct fcp_cmd *)privp->cmd;
34783d19cdaeSstevel 	char			*msg = "Transport";
34793d19cdaeSstevel 
34803d19cdaeSstevel 	if (ddi_dma_alloc_handle(sf->sf_dip, sf->sf_sochandle->fcal_dmaattr,
34813d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &lun_dma_handle) != DDI_SUCCESS) {
34823d19cdaeSstevel 		msg = "ddi_dma_alloc_handle()";
34833d19cdaeSstevel 		goto fail;
34843d19cdaeSstevel 	}
34853d19cdaeSstevel 
34863d19cdaeSstevel 	if (ddi_dma_mem_alloc(lun_dma_handle, REPORT_LUNS_SIZE,
34873d19cdaeSstevel 	    sf->sf_sochandle->fcal_accattr, DDI_DMA_CONSISTENT,
34883d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &lun_buf,
34893d19cdaeSstevel 	    &real_size, &lun_acc_handle) != DDI_SUCCESS) {
34903d19cdaeSstevel 		msg = "ddi_dma_mem_alloc()";
34913d19cdaeSstevel 		goto fail;
34923d19cdaeSstevel 	}
34933d19cdaeSstevel 
34943d19cdaeSstevel 	if (real_size < REPORT_LUNS_SIZE) {
34953d19cdaeSstevel 		msg = "DMA mem < REPORT_LUNS_SIZE";
34963d19cdaeSstevel 		goto fail;
34973d19cdaeSstevel 	}
34983d19cdaeSstevel 
34993d19cdaeSstevel 	if (ddi_dma_addr_bind_handle(lun_dma_handle, NULL,
35003d19cdaeSstevel 	    lun_buf, real_size, DDI_DMA_READ |
35013d19cdaeSstevel 	    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
35023d19cdaeSstevel 	    NULL, &pcookie, &ccount) != DDI_DMA_MAPPED) {
35033d19cdaeSstevel 		msg = "ddi_dma_addr_bind_handle()";
35043d19cdaeSstevel 		goto fail;
35053d19cdaeSstevel 	}
35063d19cdaeSstevel 	handle_bound = 1;
35073d19cdaeSstevel 
35083d19cdaeSstevel 	if (ccount != 1) {
35093d19cdaeSstevel 		msg = "ccount != 1";
35103d19cdaeSstevel 		goto fail;
35113d19cdaeSstevel 	}
35123d19cdaeSstevel 	privp->els_code = 0;
35133d19cdaeSstevel 	privp->target = target;
35143d19cdaeSstevel 	privp->data_dma_handle = lun_dma_handle;
35153d19cdaeSstevel 	privp->data_acc_handle = lun_acc_handle;
35163d19cdaeSstevel 	privp->data_buf = lun_buf;
35173d19cdaeSstevel 
35183d19cdaeSstevel 	fpkt->fcal_pkt_comp = sf_reportlun_callback;
35193d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_seg_cnt = 3;
35203d19cdaeSstevel 	fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_type = CQ_TYPE_IO_READ;
35213d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[0].fc_count =
35223d19cdaeSstevel 	    sizeof (struct fcp_cmd);
35233d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[2].fc_base =
35243d19cdaeSstevel 	    (uint32_t)pcookie.dmac_address;
35253d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[2].fc_count = pcookie.dmac_size;
35263d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_byte_cnt = pcookie.dmac_size;
35273d19cdaeSstevel 	hp->r_ctl = R_CTL_COMMAND;
35283d19cdaeSstevel 	hp->type = TYPE_SCSI_FCP;
35293d19cdaeSstevel 	bzero((caddr_t)reportlun, sizeof (struct fcp_cmd));
35303d19cdaeSstevel 	((union scsi_cdb *)reportlun->fcp_cdb)->scc_cmd = SCMD_REPORT_LUNS;
35313d19cdaeSstevel 	/* Now set the buffer size.  If DDI gave us extra, that's O.K. */
35323d19cdaeSstevel 	((union scsi_cdb *)reportlun->fcp_cdb)->scc5_count0 =
35333d19cdaeSstevel 	    (real_size&0x0ff);
35343d19cdaeSstevel 	((union scsi_cdb *)reportlun->fcp_cdb)->scc5_count1 =
35353d19cdaeSstevel 	    (real_size>>8)&0x0ff;
35363d19cdaeSstevel 	((union scsi_cdb *)reportlun->fcp_cdb)->scc5_count2 =
35373d19cdaeSstevel 	    (real_size>>16)&0x0ff;
35383d19cdaeSstevel 	((union scsi_cdb *)reportlun->fcp_cdb)->scc5_count3 =
35393d19cdaeSstevel 	    (real_size>>24)&0x0ff;
35403d19cdaeSstevel 	reportlun->fcp_cntl.cntl_read_data = 1;
35413d19cdaeSstevel 	reportlun->fcp_cntl.cntl_write_data = 0;
35423d19cdaeSstevel 	reportlun->fcp_data_len = pcookie.dmac_size;
35433d19cdaeSstevel 	reportlun->fcp_cntl.cntl_qtype = FCP_QTYPE_SIMPLE;
35443d19cdaeSstevel 
35453d19cdaeSstevel 	(void) ddi_dma_sync(lun_dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV);
35463d19cdaeSstevel 	/* We know he's there, so this should be fast */
35473d19cdaeSstevel 	privp->timeout = sf_watchdog_time + SF_FCP_TIMEOUT;
35483d19cdaeSstevel 	if (sf_els_transport(sf, privp) == 1)
35493d19cdaeSstevel 		return (1);
35503d19cdaeSstevel 
35513d19cdaeSstevel fail:
35523d19cdaeSstevel 	sf_log(sf, CE_WARN,
35533d19cdaeSstevel 	    "%s failure for REPORTLUN to target 0x%x\n",
35543d19cdaeSstevel 	    msg, sf_alpa_to_switch[privp->dest_nport_id]);
35553d19cdaeSstevel 	sf_els_free(fpkt);
35563d19cdaeSstevel 	if (lun_dma_handle != NULL) {
35573d19cdaeSstevel 		if (handle_bound)
35583d19cdaeSstevel 			(void) ddi_dma_unbind_handle(lun_dma_handle);
35593d19cdaeSstevel 		ddi_dma_free_handle(&lun_dma_handle);
35603d19cdaeSstevel 	}
35613d19cdaeSstevel 	if (lun_buf != NULL) {
35623d19cdaeSstevel 		ddi_dma_mem_free(&lun_acc_handle);
35633d19cdaeSstevel 	}
35643d19cdaeSstevel 	return (0);
35653d19cdaeSstevel }
35663d19cdaeSstevel 
35673d19cdaeSstevel /*
35683d19cdaeSstevel  * Handle the results of a REPORT_LUNS command:
35693d19cdaeSstevel  *	Create additional targets if necessary
35703d19cdaeSstevel  *	Initiate INQUIRYs on all LUNs.
35713d19cdaeSstevel  */
35723d19cdaeSstevel static void
35733d19cdaeSstevel sf_reportlun_callback(struct fcal_packet *fpkt)
35743d19cdaeSstevel {
35753d19cdaeSstevel 	struct sf_els_hdr *privp = (struct sf_els_hdr *)fpkt->
35763d19cdaeSstevel 	    fcal_pkt_private;
35773d19cdaeSstevel 	struct scsi_report_luns *ptr =
35783d19cdaeSstevel 	    (struct scsi_report_luns *)privp->data_buf;
35793d19cdaeSstevel 	struct sf *sf = privp->sf;
35803d19cdaeSstevel 	struct sf_target *target = privp->target;
35813d19cdaeSstevel 	struct fcp_rsp *rsp = NULL;
35823d19cdaeSstevel 	int delayed_retry = 0;
35833d19cdaeSstevel 	int tid = sf_alpa_to_switch[target->sft_hard_address];
35843d19cdaeSstevel 	int i, free_pkt = 1;
35853d19cdaeSstevel 	short	ncmds;
35863d19cdaeSstevel 
35873d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
35883d19cdaeSstevel 	/* use as temporary state variable */
35893d19cdaeSstevel 	if (privp->timeout == SF_INVALID_TIMEOUT) {
35903d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
35913d19cdaeSstevel 		return;
35923d19cdaeSstevel 	}
35933d19cdaeSstevel 	if (privp->prev)
35943d19cdaeSstevel 		privp->prev->next = privp->next;
35953d19cdaeSstevel 	if (privp->next)
35963d19cdaeSstevel 		privp->next->prev = privp->prev;
35973d19cdaeSstevel 	if (sf->sf_els_list == privp)
35983d19cdaeSstevel 		sf->sf_els_list = privp->next;
35993d19cdaeSstevel 	privp->prev = privp->next = NULL;
36003d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
36013d19cdaeSstevel 	ncmds = fpkt->fcal_ncmds;
36023d19cdaeSstevel 	ASSERT(ncmds >= 0);
36033d19cdaeSstevel 	mutex_enter(&sf->sf_cmd_mutex);
36043d19cdaeSstevel 	sf->sf_ncmds = ncmds;
36053d19cdaeSstevel 	mutex_exit(&sf->sf_cmd_mutex);
36063d19cdaeSstevel 
36073d19cdaeSstevel 	if (fpkt->fcal_pkt_status == FCAL_STATUS_OK) {
36083d19cdaeSstevel 		(void) ddi_dma_sync(privp->rsp_dma_handle, 0,
36093d19cdaeSstevel 		    0, DDI_DMA_SYNC_FORKERNEL);
36103d19cdaeSstevel 
36113d19cdaeSstevel 		rsp = (struct fcp_rsp *)privp->rsp;
36123d19cdaeSstevel 	}
36133d19cdaeSstevel 	SF_DEBUG(1, (sf, CE_CONT,
36143d19cdaeSstevel 	    "!REPORTLUN to al_pa %x pkt status %x scsi status %x\n",
36153d19cdaeSstevel 	    privp->dest_nport_id,
36163d19cdaeSstevel 	    fpkt->fcal_pkt_status,
36173d19cdaeSstevel 	    rsp?rsp->fcp_u.fcp_status.scsi_status:0));
36183d19cdaeSstevel 
36193d19cdaeSstevel 		/* See if target simply does not support REPORT_LUNS. */
36203d19cdaeSstevel 	if (rsp && rsp->fcp_u.fcp_status.scsi_status == STATUS_CHECK &&
36213d19cdaeSstevel 	    rsp->fcp_u.fcp_status.sense_len_set &&
36223d19cdaeSstevel 	    rsp->fcp_sense_len >=
36233d19cdaeSstevel 		offsetof(struct scsi_extended_sense, es_qual_code)) {
36243d19cdaeSstevel 			struct scsi_extended_sense *sense;
36253d19cdaeSstevel 			sense = (struct scsi_extended_sense *)
36263d19cdaeSstevel 			((caddr_t)rsp + sizeof (struct fcp_rsp)
36273d19cdaeSstevel 				+ rsp->fcp_response_len);
36283d19cdaeSstevel 			if (sense->es_key == KEY_ILLEGAL_REQUEST) {
36293d19cdaeSstevel 				if (sense->es_add_code == 0x20) {
36303d19cdaeSstevel 					/* Fake LUN 0 */
36313d19cdaeSstevel 				SF_DEBUG(1, (sf, CE_CONT,
36323d19cdaeSstevel 					"!REPORTLUN Faking good "
36333d19cdaeSstevel 					"completion for alpa %x\n",
36343d19cdaeSstevel 					privp->dest_nport_id));
36353d19cdaeSstevel 					ptr->lun_list_len = FCP_LUN_SIZE;
36363d19cdaeSstevel 					ptr->lun[0] = 0;
36373d19cdaeSstevel 					rsp->fcp_u.fcp_status.scsi_status =
36383d19cdaeSstevel 						STATUS_GOOD;
36393d19cdaeSstevel 				} else if (sense->es_add_code == 0x25) {
36403d19cdaeSstevel 					SF_DEBUG(1, (sf, CE_CONT,
36413d19cdaeSstevel 					    "!REPORTLUN device alpa %x "
36423d19cdaeSstevel 					    "key %x code %x\n",
36433d19cdaeSstevel 					    privp->dest_nport_id,
36443d19cdaeSstevel 					    sense->es_key, sense->es_add_code));
36453d19cdaeSstevel 					    goto fail;
36463d19cdaeSstevel 				}
36473d19cdaeSstevel 			} else if (sense->es_key ==
36483d19cdaeSstevel 				KEY_UNIT_ATTENTION &&
36493d19cdaeSstevel 				sense->es_add_code == 0x29) {
36503d19cdaeSstevel 				SF_DEBUG(1, (sf, CE_CONT,
36513d19cdaeSstevel 					"!REPORTLUN device alpa %x was reset\n",
36523d19cdaeSstevel 					privp->dest_nport_id));
36533d19cdaeSstevel 			} else {
36543d19cdaeSstevel 				SF_DEBUG(1, (sf, CE_CONT,
36553d19cdaeSstevel 					"!REPORTLUN device alpa %x "
36563d19cdaeSstevel 					"key %x code %x\n",
36573d19cdaeSstevel 					privp->dest_nport_id,
36583d19cdaeSstevel 					sense->es_key, sense->es_add_code));
36593d19cdaeSstevel /* XXXXXX The following is here to handle broken targets -- remove it later */
36603d19cdaeSstevel 				if (sf_reportlun_forever &&
36613d19cdaeSstevel 					sense->es_key == KEY_UNIT_ATTENTION)
36623d19cdaeSstevel 					goto retry;
36633d19cdaeSstevel /* XXXXXX */
36643d19cdaeSstevel 				if (sense->es_key == KEY_NOT_READY)
36653d19cdaeSstevel 					delayed_retry = 1;
36663d19cdaeSstevel 				}
36673d19cdaeSstevel 		}
36683d19cdaeSstevel 
36693d19cdaeSstevel 	if (rsp && rsp->fcp_u.fcp_status.scsi_status == STATUS_GOOD) {
36703d19cdaeSstevel 		struct fcp_rsp_info *bep;
36713d19cdaeSstevel 
36723d19cdaeSstevel 		bep = (struct fcp_rsp_info *)(&rsp->
36733d19cdaeSstevel 		    fcp_response_len + 1);
36743d19cdaeSstevel 		if (!rsp->fcp_u.fcp_status.rsp_len_set ||
36753d19cdaeSstevel 		    bep->rsp_code == FCP_NO_FAILURE) {
36763d19cdaeSstevel 			(void) ddi_dma_sync(privp->data_dma_handle,
36773d19cdaeSstevel 			    0, 0, DDI_DMA_SYNC_FORKERNEL);
36783d19cdaeSstevel 
36793d19cdaeSstevel 			/* Convert from #bytes to #ints */
36803d19cdaeSstevel 			ptr->lun_list_len = ptr->lun_list_len >> 3;
36813d19cdaeSstevel 			SF_DEBUG(2, (sf, CE_CONT,
36823d19cdaeSstevel 			    "!REPORTLUN to al_pa %x succeeded: %d LUNs\n",
36833d19cdaeSstevel 			    privp->dest_nport_id, ptr->lun_list_len));
36843d19cdaeSstevel 			if (!ptr->lun_list_len) {
36853d19cdaeSstevel 				/* No LUNs? Ya gotta be kidding... */
36863d19cdaeSstevel 				sf_log(sf, CE_WARN,
36873d19cdaeSstevel 				    "SCSI violation -- "
36883d19cdaeSstevel 				    "target 0x%x reports no LUNs\n",
36893d19cdaeSstevel 				    sf_alpa_to_switch[
36903d19cdaeSstevel 				    privp->dest_nport_id]);
36913d19cdaeSstevel 				ptr->lun_list_len = 1;
36923d19cdaeSstevel 				ptr->lun[0] = 0;
36933d19cdaeSstevel 			}
36943d19cdaeSstevel 
36953d19cdaeSstevel 			mutex_enter(&sf->sf_mutex);
36963d19cdaeSstevel 			if (sf->sf_lip_cnt == privp->lip_cnt) {
36973d19cdaeSstevel 				sf->sf_device_count += ptr->lun_list_len - 1;
36983d19cdaeSstevel 			}
36993d19cdaeSstevel 
37003d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
37013d19cdaeSstevel 			for (i = 0; i < ptr->lun_list_len && privp->lip_cnt ==
37023d19cdaeSstevel 			    sf->sf_lip_cnt; i++) {
37033d19cdaeSstevel 				struct sf_els_hdr *nprivp;
37043d19cdaeSstevel 				struct fcal_packet *nfpkt;
37053d19cdaeSstevel 
37063d19cdaeSstevel 				/* LUN 0 is already in `target' */
37073d19cdaeSstevel 				if (ptr->lun[i] != 0) {
37083d19cdaeSstevel 					target = sf_create_target(sf,
37093d19cdaeSstevel 					    privp, tid, ptr->lun[i]);
37103d19cdaeSstevel 				}
37113d19cdaeSstevel 				nprivp = NULL;
37123d19cdaeSstevel 				nfpkt = NULL;
37133d19cdaeSstevel 				if (target) {
37143d19cdaeSstevel 					nfpkt = sf_els_alloc(sf,
37153d19cdaeSstevel 					    target->sft_al_pa,
37163d19cdaeSstevel 					    sizeof (struct sf_els_hdr),
37173d19cdaeSstevel 					    sizeof (union sf_els_cmd),
37183d19cdaeSstevel 					    sizeof (union sf_els_rsp),
37193d19cdaeSstevel 					    (caddr_t *)&nprivp,
37203d19cdaeSstevel 					    (caddr_t *)&rsp);
37213d19cdaeSstevel 					if (nprivp)
37223d19cdaeSstevel 						nprivp->lip_cnt =
37233d19cdaeSstevel 						    privp->lip_cnt;
37243d19cdaeSstevel 				}
37253d19cdaeSstevel 				if (nfpkt && nprivp &&
37263d19cdaeSstevel 				    (sf_do_inquiry(sf, nprivp, target) ==
37273d19cdaeSstevel 				    0)) {
37283d19cdaeSstevel 					mutex_enter(&sf->sf_mutex);
37293d19cdaeSstevel 					if (sf->sf_lip_cnt == privp->
37303d19cdaeSstevel 					    lip_cnt) {
37313d19cdaeSstevel 						sf->sf_device_count --;
37323d19cdaeSstevel 					}
37333d19cdaeSstevel 					sf_offline_target(sf, target);
37343d19cdaeSstevel 					mutex_exit(&sf->sf_mutex);
37353d19cdaeSstevel 				}
37363d19cdaeSstevel 			}
37373d19cdaeSstevel 			sf_els_free(fpkt);
37383d19cdaeSstevel 			return;
37393d19cdaeSstevel 		} else {
37403d19cdaeSstevel 			SF_DEBUG(1, (sf, CE_CONT,
37413d19cdaeSstevel 			    "!REPORTLUN al_pa %x fcp failure, "
37423d19cdaeSstevel 			    "fcp_rsp_code %x scsi status %x\n",
37433d19cdaeSstevel 			    privp->dest_nport_id, bep->rsp_code,
37443d19cdaeSstevel 			    rsp ? rsp->fcp_u.fcp_status.scsi_status:0));
37453d19cdaeSstevel 			goto fail;
37463d19cdaeSstevel 		}
37473d19cdaeSstevel 	}
37483d19cdaeSstevel 	if (rsp && ((rsp->fcp_u.fcp_status.scsi_status == STATUS_BUSY) ||
37493d19cdaeSstevel 	    (rsp->fcp_u.fcp_status.scsi_status == STATUS_QFULL))) {
37503d19cdaeSstevel 		delayed_retry = 1;
37513d19cdaeSstevel 	}
37523d19cdaeSstevel 
37533d19cdaeSstevel 	if (++(privp->retries) < sf_els_retries ||
37543d19cdaeSstevel 	    (delayed_retry && privp->retries < SF_BSY_RETRIES)) {
37553d19cdaeSstevel /* XXXXXX The following is here to handle broken targets -- remove it later */
37563d19cdaeSstevel retry:
37573d19cdaeSstevel /* XXXXXX */
37583d19cdaeSstevel 		if (delayed_retry) {
37593d19cdaeSstevel 			privp->retries--;
37603d19cdaeSstevel 			privp->timeout = sf_watchdog_time + SF_BSY_TIMEOUT;
37613d19cdaeSstevel 			privp->delayed_retry = 1;
37623d19cdaeSstevel 		} else {
37633d19cdaeSstevel 			privp->timeout = sf_watchdog_time + SF_FCP_TIMEOUT;
37643d19cdaeSstevel 		}
37653d19cdaeSstevel 
37663d19cdaeSstevel 		privp->prev = NULL;
37673d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
37683d19cdaeSstevel 		if (privp->lip_cnt == sf->sf_lip_cnt) {
37693d19cdaeSstevel 			if (!delayed_retry)
37703d19cdaeSstevel 				SF_DEBUG(1, (sf, CE_WARN,
37713d19cdaeSstevel 				    "!REPORTLUN to al_pa %x failed, retrying\n",
37723d19cdaeSstevel 				    privp->dest_nport_id));
37733d19cdaeSstevel 			privp->next = sf->sf_els_list;
37743d19cdaeSstevel 			if (sf->sf_els_list != NULL)
37753d19cdaeSstevel 				sf->sf_els_list->prev = privp;
37763d19cdaeSstevel 			sf->sf_els_list = privp;
37773d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
37783d19cdaeSstevel 			if (!delayed_retry && soc_transport(sf->sf_sochandle,
37793d19cdaeSstevel 			    fpkt, FCAL_NOSLEEP, CQ_REQUEST_1) !=
37803d19cdaeSstevel 			    FCAL_TRANSPORT_SUCCESS) {
37813d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
37823d19cdaeSstevel 				if (privp->prev)
37833d19cdaeSstevel 					privp->prev->next = privp->next;
37843d19cdaeSstevel 				if (privp->next)
37853d19cdaeSstevel 					privp->next->prev = privp->prev;
37863d19cdaeSstevel 				if (sf->sf_els_list == privp)
37873d19cdaeSstevel 					sf->sf_els_list = privp->next;
37883d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
37893d19cdaeSstevel 				goto fail;
37903d19cdaeSstevel 			} else
37913d19cdaeSstevel 				return;
37923d19cdaeSstevel 		} else {
37933d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
37943d19cdaeSstevel 		}
37953d19cdaeSstevel 	} else {
37963d19cdaeSstevel fail:
37973d19cdaeSstevel 
37983d19cdaeSstevel 		/* REPORT_LUN failed -- try inquiry */
37993d19cdaeSstevel 		if (sf_do_inquiry(sf, privp, target) != 0) {
38003d19cdaeSstevel 			return;
38013d19cdaeSstevel 		} else {
38023d19cdaeSstevel 			free_pkt = 0;
38033d19cdaeSstevel 		}
38043d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
38053d19cdaeSstevel 		if (sf->sf_lip_cnt == privp->lip_cnt) {
380619397407SSherry Moore 			sf_log(sf, CE_WARN,
380719397407SSherry Moore 			    "!REPORTLUN to target 0x%x failed\n",
38083d19cdaeSstevel 			    sf_alpa_to_switch[privp->dest_nport_id]);
38093d19cdaeSstevel 			sf_offline_target(sf, target);
38103d19cdaeSstevel 			sf->sf_device_count--;
38113d19cdaeSstevel 			ASSERT(sf->sf_device_count >= 0);
38123d19cdaeSstevel 			if (sf->sf_device_count == 0)
38133d19cdaeSstevel 			sf_finish_init(sf, privp->lip_cnt);
38143d19cdaeSstevel 		}
38153d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
38163d19cdaeSstevel 	}
38173d19cdaeSstevel 	if (free_pkt) {
38183d19cdaeSstevel 		sf_els_free(fpkt);
38193d19cdaeSstevel 	}
38203d19cdaeSstevel }
38213d19cdaeSstevel 
38223d19cdaeSstevel static int
38233d19cdaeSstevel sf_do_inquiry(struct sf *sf, struct sf_els_hdr *privp,
38243d19cdaeSstevel     struct sf_target *target)
38253d19cdaeSstevel {
38263d19cdaeSstevel 	struct	fcal_packet	*fpkt = privp->fpkt;
38273d19cdaeSstevel 	ddi_dma_cookie_t	pcookie;
38283d19cdaeSstevel 	ddi_dma_handle_t	inq_dma_handle = NULL;
38293d19cdaeSstevel 	ddi_acc_handle_t	inq_acc_handle;
38303d19cdaeSstevel 	uint_t			ccount;
38313d19cdaeSstevel 	size_t			real_size;
38323d19cdaeSstevel 	caddr_t			inq_buf = NULL;
38333d19cdaeSstevel 	int			handle_bound = FALSE;
38343d19cdaeSstevel 	fc_frame_header_t *hp = &fpkt->fcal_socal_request.sr_fc_frame_hdr;
38353d19cdaeSstevel 	struct fcp_cmd		*inq = (struct fcp_cmd *)privp->cmd;
38363d19cdaeSstevel 	char			*msg = "Transport";
38373d19cdaeSstevel 
38383d19cdaeSstevel 
38393d19cdaeSstevel 	if (ddi_dma_alloc_handle(sf->sf_dip, sf->sf_sochandle->fcal_dmaattr,
38403d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &inq_dma_handle) != DDI_SUCCESS) {
38413d19cdaeSstevel 		msg = "ddi_dma_alloc_handle()";
38423d19cdaeSstevel 		goto fail;
38433d19cdaeSstevel 	}
38443d19cdaeSstevel 
38453d19cdaeSstevel 	if (ddi_dma_mem_alloc(inq_dma_handle, SUN_INQSIZE,
38463d19cdaeSstevel 	    sf->sf_sochandle->fcal_accattr, DDI_DMA_CONSISTENT,
38473d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &inq_buf,
38483d19cdaeSstevel 	    &real_size, &inq_acc_handle) != DDI_SUCCESS) {
38493d19cdaeSstevel 		msg = "ddi_dma_mem_alloc()";
38503d19cdaeSstevel 		goto fail;
38513d19cdaeSstevel 	}
38523d19cdaeSstevel 
38533d19cdaeSstevel 	if (real_size < SUN_INQSIZE) {
38543d19cdaeSstevel 		msg = "DMA mem < inquiry size";
38553d19cdaeSstevel 		goto fail;
38563d19cdaeSstevel 	}
38573d19cdaeSstevel 
38583d19cdaeSstevel 	if (ddi_dma_addr_bind_handle(inq_dma_handle, NULL,
38593d19cdaeSstevel 	    inq_buf, real_size, DDI_DMA_READ | DDI_DMA_CONSISTENT,
38603d19cdaeSstevel 	    DDI_DMA_DONTWAIT, NULL, &pcookie, &ccount) != DDI_DMA_MAPPED) {
38613d19cdaeSstevel 		msg = "ddi_dma_addr_bind_handle()";
38623d19cdaeSstevel 		goto fail;
38633d19cdaeSstevel 	}
38643d19cdaeSstevel 	handle_bound = TRUE;
38653d19cdaeSstevel 
38663d19cdaeSstevel 	if (ccount != 1) {
38673d19cdaeSstevel 		msg = "ccount != 1";
38683d19cdaeSstevel 		goto fail;
38693d19cdaeSstevel 	}
38703d19cdaeSstevel 	privp->els_code = 0;			/* not an ELS command */
38713d19cdaeSstevel 	privp->target = target;
38723d19cdaeSstevel 	privp->data_dma_handle = inq_dma_handle;
38733d19cdaeSstevel 	privp->data_acc_handle = inq_acc_handle;
38743d19cdaeSstevel 	privp->data_buf = inq_buf;
38753d19cdaeSstevel 	fpkt->fcal_pkt_comp = sf_inq_callback;
38763d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_seg_cnt = 3;
38773d19cdaeSstevel 	fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_type = CQ_TYPE_IO_READ;
38783d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[0].fc_count =
38793d19cdaeSstevel 	    sizeof (struct fcp_cmd);
38803d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[2].fc_base =
38813d19cdaeSstevel 	    (uint32_t)pcookie.dmac_address;
38823d19cdaeSstevel 	fpkt->fcal_socal_request.sr_dataseg[2].fc_count = pcookie.dmac_size;
38833d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_byte_cnt = pcookie.dmac_size;
38843d19cdaeSstevel 	hp->r_ctl = R_CTL_COMMAND;
38853d19cdaeSstevel 	hp->type = TYPE_SCSI_FCP;
38863d19cdaeSstevel 	bzero((caddr_t)inq, sizeof (struct fcp_cmd));
38873d19cdaeSstevel 	((union scsi_cdb *)inq->fcp_cdb)->scc_cmd = SCMD_INQUIRY;
38883d19cdaeSstevel 	((union scsi_cdb *)inq->fcp_cdb)->g0_count0 = SUN_INQSIZE;
38893d19cdaeSstevel 	bcopy((caddr_t)&target->sft_lun.b, (caddr_t)&inq->fcp_ent_addr,
38903d19cdaeSstevel 	    FCP_LUN_SIZE);
38913d19cdaeSstevel 	inq->fcp_cntl.cntl_read_data = 1;
38923d19cdaeSstevel 	inq->fcp_cntl.cntl_write_data = 0;
38933d19cdaeSstevel 	inq->fcp_data_len = pcookie.dmac_size;
38943d19cdaeSstevel 	inq->fcp_cntl.cntl_qtype = FCP_QTYPE_SIMPLE;
38953d19cdaeSstevel 
38963d19cdaeSstevel 	(void) ddi_dma_sync(inq_dma_handle, (off_t)0, (size_t)0,
38973d19cdaeSstevel 	    DDI_DMA_SYNC_FORDEV);
38983d19cdaeSstevel 	privp->timeout = sf_watchdog_time + SF_FCP_TIMEOUT;
38993d19cdaeSstevel 	SF_DEBUG(5, (sf, CE_WARN,
39003d19cdaeSstevel 	    "!Sending INQUIRY to al_pa %x lun %" PRIx64 "\n",
39013d19cdaeSstevel 	    privp->dest_nport_id,
39023d19cdaeSstevel 	    SCSA_LUN(target)));
39033d19cdaeSstevel 	return (sf_els_transport(sf, privp));
39043d19cdaeSstevel 
39053d19cdaeSstevel fail:
39063d19cdaeSstevel 	sf_log(sf, CE_WARN,
39073d19cdaeSstevel 	    "%s failure for INQUIRY to target 0x%x\n",
39083d19cdaeSstevel 	    msg, sf_alpa_to_switch[privp->dest_nport_id]);
39093d19cdaeSstevel 	sf_els_free(fpkt);
39103d19cdaeSstevel 	if (inq_dma_handle != NULL) {
39113d19cdaeSstevel 		if (handle_bound) {
39123d19cdaeSstevel 			(void) ddi_dma_unbind_handle(inq_dma_handle);
39133d19cdaeSstevel 		}
39143d19cdaeSstevel 		ddi_dma_free_handle(&inq_dma_handle);
39153d19cdaeSstevel 	}
39163d19cdaeSstevel 	if (inq_buf != NULL) {
39173d19cdaeSstevel 		ddi_dma_mem_free(&inq_acc_handle);
39183d19cdaeSstevel 	}
39193d19cdaeSstevel 	return (FALSE);
39203d19cdaeSstevel }
39213d19cdaeSstevel 
39223d19cdaeSstevel 
39233d19cdaeSstevel /*
39243d19cdaeSstevel  * called as the pkt_comp routine for INQ packets
39253d19cdaeSstevel  */
39263d19cdaeSstevel static void
39273d19cdaeSstevel sf_inq_callback(struct fcal_packet *fpkt)
39283d19cdaeSstevel {
39293d19cdaeSstevel 	struct sf_els_hdr *privp = (struct sf_els_hdr *)fpkt->
39303d19cdaeSstevel 	    fcal_pkt_private;
39313d19cdaeSstevel 	struct scsi_inquiry *prt = (struct scsi_inquiry *)privp->data_buf;
39323d19cdaeSstevel 	struct sf *sf = privp->sf;
39333d19cdaeSstevel 	struct sf *tsf;
39343d19cdaeSstevel 	struct sf_target *target = privp->target;
39353d19cdaeSstevel 	struct fcp_rsp *rsp;
39363d19cdaeSstevel 	int delayed_retry = FALSE;
39373d19cdaeSstevel 	short	ncmds;
39383d19cdaeSstevel 
39393d19cdaeSstevel 
39403d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
39413d19cdaeSstevel 	/* use as temporary state variable */
39423d19cdaeSstevel 	if (privp->timeout == SF_INVALID_TIMEOUT) {
39433d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
39443d19cdaeSstevel 		return;
39453d19cdaeSstevel 	}
39463d19cdaeSstevel 	if (privp->prev != NULL) {
39473d19cdaeSstevel 		privp->prev->next = privp->next;
39483d19cdaeSstevel 	}
39493d19cdaeSstevel 	if (privp->next != NULL) {
39503d19cdaeSstevel 		privp->next->prev = privp->prev;
39513d19cdaeSstevel 	}
39523d19cdaeSstevel 	if (sf->sf_els_list == privp) {
39533d19cdaeSstevel 		sf->sf_els_list = privp->next;
39543d19cdaeSstevel 	}
39553d19cdaeSstevel 	privp->prev = privp->next = NULL;
39563d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
39573d19cdaeSstevel 	ncmds = fpkt->fcal_ncmds;
39583d19cdaeSstevel 	ASSERT(ncmds >= 0);
39593d19cdaeSstevel 	mutex_enter(&sf->sf_cmd_mutex);
39603d19cdaeSstevel 	sf->sf_ncmds = ncmds;
39613d19cdaeSstevel 	mutex_exit(&sf->sf_cmd_mutex);
39623d19cdaeSstevel 
39633d19cdaeSstevel 	if (fpkt->fcal_pkt_status == FCAL_STATUS_OK) {
39643d19cdaeSstevel 
39653d19cdaeSstevel 		(void) ddi_dma_sync(privp->rsp_dma_handle, (off_t)0,
39663d19cdaeSstevel 		    (size_t)0, DDI_DMA_SYNC_FORKERNEL);
39673d19cdaeSstevel 
39683d19cdaeSstevel 		rsp = (struct fcp_rsp *)privp->rsp;
39693d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT,
39703d19cdaeSstevel 		    "!INQUIRY to al_pa %x scsi status %x",
39713d19cdaeSstevel 		    privp->dest_nport_id, rsp->fcp_u.fcp_status.scsi_status));
39723d19cdaeSstevel 
39733d19cdaeSstevel 		if ((rsp->fcp_u.fcp_status.scsi_status == STATUS_GOOD) &&
39743d19cdaeSstevel 		    !rsp->fcp_u.fcp_status.resid_over &&
39753d19cdaeSstevel 		    (!rsp->fcp_u.fcp_status.resid_under ||
39763d19cdaeSstevel 		    ((SUN_INQSIZE - rsp->fcp_resid) >= SUN_MIN_INQLEN))) {
39773d19cdaeSstevel 			struct fcp_rsp_info *bep;
39783d19cdaeSstevel 
39793d19cdaeSstevel 			bep = (struct fcp_rsp_info *)(&rsp->
39803d19cdaeSstevel 			    fcp_response_len + 1);
39813d19cdaeSstevel 
39823d19cdaeSstevel 			if (!rsp->fcp_u.fcp_status.rsp_len_set ||
39833d19cdaeSstevel 			    (bep->rsp_code == FCP_NO_FAILURE)) {
39843d19cdaeSstevel 
39853d19cdaeSstevel 				SF_DEBUG(2, (sf, CE_CONT,
39863d19cdaeSstevel 				    "!INQUIRY to al_pa %x lun %" PRIx64
39873d19cdaeSstevel 				    " succeeded\n",
39883d19cdaeSstevel 				    privp->dest_nport_id, SCSA_LUN(target)));
39893d19cdaeSstevel 
39903d19cdaeSstevel 				(void) ddi_dma_sync(privp->data_dma_handle,
39913d19cdaeSstevel 				    (off_t)0, (size_t)0,
39923d19cdaeSstevel 				    DDI_DMA_SYNC_FORKERNEL);
39933d19cdaeSstevel 
39943d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
39953d19cdaeSstevel 
39963d19cdaeSstevel 				if (sf->sf_lip_cnt == privp->lip_cnt) {
39973d19cdaeSstevel 					mutex_enter(&target->sft_mutex);
39983d19cdaeSstevel 					target->sft_device_type =
39993d19cdaeSstevel 					    prt->inq_dtype;
40003d19cdaeSstevel 					bcopy(prt, &target->sft_inq,
40013d19cdaeSstevel 					    sizeof (*prt));
40023d19cdaeSstevel 					mutex_exit(&target->sft_mutex);
40033d19cdaeSstevel 					sf->sf_device_count--;
40043d19cdaeSstevel 					ASSERT(sf->sf_device_count >= 0);
40053d19cdaeSstevel 					if (sf->sf_device_count == 0) {
40063d19cdaeSstevel 						sf_finish_init(sf,
40073d19cdaeSstevel 						    privp->lip_cnt);
40083d19cdaeSstevel 					}
40093d19cdaeSstevel 				}
40103d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
40113d19cdaeSstevel 				sf_els_free(fpkt);
40123d19cdaeSstevel 				return;
40133d19cdaeSstevel 			}
40143d19cdaeSstevel 		} else if ((rsp->fcp_u.fcp_status.scsi_status ==
40153d19cdaeSstevel 		    STATUS_BUSY) ||
40163d19cdaeSstevel 		    (rsp->fcp_u.fcp_status.scsi_status == STATUS_QFULL) ||
40173d19cdaeSstevel 		    (rsp->fcp_u.fcp_status.scsi_status == STATUS_CHECK)) {
40183d19cdaeSstevel 			delayed_retry = TRUE;
40193d19cdaeSstevel 		}
40203d19cdaeSstevel 	} else {
40213d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT, "!INQUIRY to al_pa %x fc status %x",
40223d19cdaeSstevel 		    privp->dest_nport_id, fpkt->fcal_pkt_status));
40233d19cdaeSstevel 	}
40243d19cdaeSstevel 
40253d19cdaeSstevel 	if (++(privp->retries) < sf_els_retries ||
40263d19cdaeSstevel 	    (delayed_retry && privp->retries < SF_BSY_RETRIES)) {
40273d19cdaeSstevel 		if (fpkt->fcal_pkt_status == FCAL_STATUS_MAX_XCHG_EXCEEDED)  {
40283d19cdaeSstevel 			tsf = sf->sf_sibling;
40293d19cdaeSstevel 			if (tsf != NULL) {
40303d19cdaeSstevel 				mutex_enter(&tsf->sf_cmd_mutex);
40313d19cdaeSstevel 				tsf->sf_flag = 1;
40323d19cdaeSstevel 				tsf->sf_throttle = SF_DECR_DELTA;
40333d19cdaeSstevel 				mutex_exit(&tsf->sf_cmd_mutex);
40343d19cdaeSstevel 			}
40353d19cdaeSstevel 			delayed_retry = 1;
40363d19cdaeSstevel 		}
40373d19cdaeSstevel 		if (delayed_retry) {
40383d19cdaeSstevel 			privp->retries--;
40393d19cdaeSstevel 			privp->timeout = sf_watchdog_time + SF_BSY_TIMEOUT;
40403d19cdaeSstevel 			privp->delayed_retry = TRUE;
40413d19cdaeSstevel 		} else {
40423d19cdaeSstevel 			privp->timeout = sf_watchdog_time + SF_FCP_TIMEOUT;
40433d19cdaeSstevel 		}
40443d19cdaeSstevel 
40453d19cdaeSstevel 		privp->prev = NULL;
40463d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
40473d19cdaeSstevel 		if (privp->lip_cnt == sf->sf_lip_cnt) {
40483d19cdaeSstevel 			if (!delayed_retry) {
40493d19cdaeSstevel 				SF_DEBUG(1, (sf, CE_WARN,
40503d19cdaeSstevel 				    "INQUIRY to al_pa %x failed, retrying",
40513d19cdaeSstevel 				    privp->dest_nport_id));
40523d19cdaeSstevel 			}
40533d19cdaeSstevel 			privp->next = sf->sf_els_list;
40543d19cdaeSstevel 			if (sf->sf_els_list != NULL) {
40553d19cdaeSstevel 				sf->sf_els_list->prev = privp;
40563d19cdaeSstevel 			}
40573d19cdaeSstevel 			sf->sf_els_list = privp;
40583d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
40593d19cdaeSstevel 			/* if not delayed call transport to send a pkt */
40603d19cdaeSstevel 			if (!delayed_retry &&
40613d19cdaeSstevel 			    (soc_transport(sf->sf_sochandle, fpkt,
40623d19cdaeSstevel 			    FCAL_NOSLEEP, CQ_REQUEST_1) !=
40633d19cdaeSstevel 			    FCAL_TRANSPORT_SUCCESS)) {
40643d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
40653d19cdaeSstevel 				if (privp->prev != NULL) {
40663d19cdaeSstevel 					privp->prev->next = privp->next;
40673d19cdaeSstevel 				}
40683d19cdaeSstevel 				if (privp->next != NULL) {
40693d19cdaeSstevel 					privp->next->prev = privp->prev;
40703d19cdaeSstevel 				}
40713d19cdaeSstevel 				if (sf->sf_els_list == privp) {
40723d19cdaeSstevel 					sf->sf_els_list = privp->next;
40733d19cdaeSstevel 				}
40743d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
40753d19cdaeSstevel 				goto fail;
40763d19cdaeSstevel 			}
40773d19cdaeSstevel 			return;
40783d19cdaeSstevel 		}
40793d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
40803d19cdaeSstevel 	} else {
40813d19cdaeSstevel fail:
40823d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
40833d19cdaeSstevel 		if (sf->sf_lip_cnt == privp->lip_cnt) {
40843d19cdaeSstevel 			sf_offline_target(sf, target);
40853d19cdaeSstevel 			sf_log(sf, CE_NOTE,
40863d19cdaeSstevel 			    "INQUIRY to target 0x%x lun %" PRIx64 " failed. "
40873d19cdaeSstevel 			    "Retry Count: %d\n",
40883d19cdaeSstevel 			    sf_alpa_to_switch[privp->dest_nport_id],
40893d19cdaeSstevel 			    SCSA_LUN(target),
40903d19cdaeSstevel 			    privp->retries);
40913d19cdaeSstevel 			sf->sf_device_count--;
40923d19cdaeSstevel 			ASSERT(sf->sf_device_count >= 0);
40933d19cdaeSstevel 			if (sf->sf_device_count == 0) {
40943d19cdaeSstevel 				sf_finish_init(sf, privp->lip_cnt);
40953d19cdaeSstevel 			}
40963d19cdaeSstevel 		}
40973d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
40983d19cdaeSstevel 	}
40993d19cdaeSstevel 	sf_els_free(fpkt);
41003d19cdaeSstevel }
41013d19cdaeSstevel 
41023d19cdaeSstevel 
41033d19cdaeSstevel static void
41043d19cdaeSstevel sf_finish_init(struct sf *sf, int lip_cnt)
41053d19cdaeSstevel {
41063d19cdaeSstevel 	int			i;		/* loop index */
41073d19cdaeSstevel 	int			cflag;
41083d19cdaeSstevel 	struct sf_target	*target;	/* current target */
41093d19cdaeSstevel 	dev_info_t		*dip;
41103d19cdaeSstevel 	struct sf_hp_elem	*elem;		/* hotplug element created */
41113d19cdaeSstevel 
41123d19cdaeSstevel 	SF_DEBUG(1, (sf, CE_WARN, "!sf_finish_init\n"));
41133d19cdaeSstevel 	ASSERT(mutex_owned(&sf->sf_mutex));
41143d19cdaeSstevel 
41153d19cdaeSstevel 	/* scan all hash queues */
41163d19cdaeSstevel 	for (i = 0; i < SF_NUM_HASH_QUEUES; i++) {
41173d19cdaeSstevel 		target = sf->sf_wwn_lists[i];
41183d19cdaeSstevel 		while (target != NULL) {
41193d19cdaeSstevel 			mutex_enter(&target->sft_mutex);
41203d19cdaeSstevel 
41213d19cdaeSstevel 			/* see if target is not offline */
41223d19cdaeSstevel 			if ((target->sft_state & SF_TARGET_OFFLINE)) {
41233d19cdaeSstevel 				/*
41243d19cdaeSstevel 				 * target already offline
41253d19cdaeSstevel 				 */
41263d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
41273d19cdaeSstevel 				goto next_entry;
41283d19cdaeSstevel 			}
41293d19cdaeSstevel 
41303d19cdaeSstevel 			/*
41313d19cdaeSstevel 			 * target is not already offline -- see if it has
41323d19cdaeSstevel 			 * already been marked as ready to go offline
41333d19cdaeSstevel 			 */
41343d19cdaeSstevel 			if (target->sft_state & SF_TARGET_MARK) {
41353d19cdaeSstevel 				/*
41363d19cdaeSstevel 				 * target already marked, so take it offline
41373d19cdaeSstevel 				 */
41383d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
41393d19cdaeSstevel 				sf_offline_target(sf, target);
41403d19cdaeSstevel 				goto next_entry;
41413d19cdaeSstevel 			}
41423d19cdaeSstevel 
41433d19cdaeSstevel 			/* clear target busy flag */
41443d19cdaeSstevel 			target->sft_state &= ~SF_TARGET_BUSY;
41453d19cdaeSstevel 
41463d19cdaeSstevel 			/* is target init not yet done ?? */
41473d19cdaeSstevel 			cflag = !(target->sft_state & SF_TARGET_INIT_DONE);
41483d19cdaeSstevel 
41493d19cdaeSstevel 			/* get pointer to target dip */
41503d19cdaeSstevel 			dip = target->sft_dip;
41513d19cdaeSstevel 
41523d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
41533d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
41543d19cdaeSstevel 
41553d19cdaeSstevel 			if (cflag && (dip == NULL)) {
41563d19cdaeSstevel 				/*
41573d19cdaeSstevel 				 * target init not yet done &&
41583d19cdaeSstevel 				 * devinfo not yet created
41593d19cdaeSstevel 				 */
41603d19cdaeSstevel 				sf_create_devinfo(sf, target, lip_cnt);
41613d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
41623d19cdaeSstevel 				goto next_entry;
41633d19cdaeSstevel 			}
41643d19cdaeSstevel 
41653d19cdaeSstevel 			/*
41663d19cdaeSstevel 			 * target init already done || devinfo already created
41673d19cdaeSstevel 			 */
41683d19cdaeSstevel 			ASSERT(dip != NULL);
41693d19cdaeSstevel 			if (!sf_create_props(dip, target, lip_cnt)) {
41703d19cdaeSstevel 				/* a problem creating properties */
41713d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
41723d19cdaeSstevel 				goto next_entry;
41733d19cdaeSstevel 			}
41743d19cdaeSstevel 
41753d19cdaeSstevel 			/* create a new element for the hotplug list */
41763d19cdaeSstevel 			if ((elem = kmem_zalloc(sizeof (struct sf_hp_elem),
41773d19cdaeSstevel 			    KM_NOSLEEP)) != NULL) {
41783d19cdaeSstevel 
41793d19cdaeSstevel 				/* fill in the new element */
41803d19cdaeSstevel 				elem->dip = dip;
41813d19cdaeSstevel 				elem->target = target;
41823d19cdaeSstevel 				elem->what = SF_ONLINE;
41833d19cdaeSstevel 
41843d19cdaeSstevel 				/* add the new element into the hotplug list */
41853d19cdaeSstevel 				mutex_enter(&sf->sf_hp_daemon_mutex);
41863d19cdaeSstevel 				if (sf->sf_hp_elem_tail != NULL) {
41873d19cdaeSstevel 					sf->sf_hp_elem_tail->next = elem;
41883d19cdaeSstevel 					sf->sf_hp_elem_tail = elem;
41893d19cdaeSstevel 				} else {
41903d19cdaeSstevel 					/* this is the first element in list */
41913d19cdaeSstevel 					sf->sf_hp_elem_head =
41923d19cdaeSstevel 					    sf->sf_hp_elem_tail =
41933d19cdaeSstevel 					    elem;
41943d19cdaeSstevel 				}
41953d19cdaeSstevel 				cv_signal(&sf->sf_hp_daemon_cv);
41963d19cdaeSstevel 				mutex_exit(&sf->sf_hp_daemon_mutex);
41973d19cdaeSstevel 			} else {
41983d19cdaeSstevel 				/* could not allocate memory for element ?? */
41993d19cdaeSstevel 				(void) ndi_devi_online_async(dip, 0);
42003d19cdaeSstevel 			}
42013d19cdaeSstevel 
42023d19cdaeSstevel 			mutex_enter(&sf->sf_mutex);
42033d19cdaeSstevel 
42043d19cdaeSstevel next_entry:
42053d19cdaeSstevel 			/* ensure no new LIPs have occurred */
42063d19cdaeSstevel 			if (sf->sf_lip_cnt != lip_cnt) {
42073d19cdaeSstevel 				return;
42083d19cdaeSstevel 			}
42093d19cdaeSstevel 			target = target->sft_next;
42103d19cdaeSstevel 		}
42113d19cdaeSstevel 
42123d19cdaeSstevel 		/* done scanning all targets in this queue */
42133d19cdaeSstevel 	}
42143d19cdaeSstevel 
42153d19cdaeSstevel 	/* done with all hash queues */
42163d19cdaeSstevel 
42173d19cdaeSstevel 	sf->sf_state = SF_STATE_ONLINE;
42183d19cdaeSstevel 	sf->sf_online_timer = 0;
42193d19cdaeSstevel }
42203d19cdaeSstevel 
42213d19cdaeSstevel 
42223d19cdaeSstevel /*
42233d19cdaeSstevel  * create devinfo node
42243d19cdaeSstevel  */
42253d19cdaeSstevel static void
42263d19cdaeSstevel sf_create_devinfo(struct sf *sf, struct sf_target *target, int lip_cnt)
42273d19cdaeSstevel {
42283d19cdaeSstevel 	dev_info_t		*cdip = NULL;
42293d19cdaeSstevel 	char			*nname = NULL;
42303d19cdaeSstevel 	char			**compatible = NULL;
42313d19cdaeSstevel 	int			ncompatible;
42323d19cdaeSstevel 	struct scsi_inquiry	*inq = &target->sft_inq;
42333d19cdaeSstevel 	char			*scsi_binding_set;
42343d19cdaeSstevel 
42353d19cdaeSstevel 	/* get the 'scsi-binding-set' property */
42363d19cdaeSstevel 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, sf->sf_dip,
42373d19cdaeSstevel 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set",
42383d19cdaeSstevel 	    &scsi_binding_set) != DDI_PROP_SUCCESS)
42393d19cdaeSstevel 		scsi_binding_set = NULL;
42403d19cdaeSstevel 
42413d19cdaeSstevel 	/* determine the node name and compatible */
42423d19cdaeSstevel 	scsi_hba_nodename_compatible_get(inq, scsi_binding_set,
42433d19cdaeSstevel 	    inq->inq_dtype, NULL, &nname, &compatible, &ncompatible);
42443d19cdaeSstevel 	if (scsi_binding_set)
42453d19cdaeSstevel 		ddi_prop_free(scsi_binding_set);
42463d19cdaeSstevel 
42473d19cdaeSstevel 	/* if nodename can't be determined then print a message and skip it */
42483d19cdaeSstevel 	if (nname == NULL) {
42493d19cdaeSstevel #ifndef	RAID_LUNS
42503d19cdaeSstevel 		sf_log(sf, CE_WARN, "%s%d: no driver for device "
42513d19cdaeSstevel 		    "@w%02x%02x%02x%02x%02x%02x%02x%02x,%x\n"
42523d19cdaeSstevel 		    "    compatible: %s",
42533d19cdaeSstevel 		    ddi_driver_name(sf->sf_dip), ddi_get_instance(sf->sf_dip),
42543d19cdaeSstevel 		    target->sft_port_wwn[0], target->sft_port_wwn[1],
42553d19cdaeSstevel 		    target->sft_port_wwn[2], target->sft_port_wwn[3],
42563d19cdaeSstevel 		    target->sft_port_wwn[4], target->sft_port_wwn[5],
42573d19cdaeSstevel 		    target->sft_port_wwn[6], target->sft_port_wwn[7],
42583d19cdaeSstevel 		    target->sft_lun.l, *compatible);
42593d19cdaeSstevel #else
42603d19cdaeSstevel 		sf_log(sf, CE_WARN, "%s%d: no driver for device "
42613d19cdaeSstevel 		    "@w%02x%02x%02x%02x%02x%02x%02x%02x,%x\n"
42623d19cdaeSstevel 		    "    compatible: %s",
42633d19cdaeSstevel 		    ddi_driver_name(sf->sf_dip), ddi_get_instance(sf->sf_dip),
42643d19cdaeSstevel 		    target->sft_port_wwn[0], target->sft_port_wwn[1],
42653d19cdaeSstevel 		    target->sft_port_wwn[2], target->sft_port_wwn[3],
42663d19cdaeSstevel 		    target->sft_port_wwn[4], target->sft_port_wwn[5],
42673d19cdaeSstevel 		    target->sft_port_wwn[6], target->sft_port_wwn[7],
42683d19cdaeSstevel 		    target->sft_raid_lun, *compatible);
42693d19cdaeSstevel #endif
42703d19cdaeSstevel 		goto fail;
42713d19cdaeSstevel 	}
42723d19cdaeSstevel 
42733d19cdaeSstevel 	/* allocate the node */
42743d19cdaeSstevel 	if (ndi_devi_alloc(sf->sf_dip, nname,
42753d19cdaeSstevel 	    DEVI_SID_NODEID, &cdip) != NDI_SUCCESS) {
42763d19cdaeSstevel 		goto fail;
42773d19cdaeSstevel 	}
42783d19cdaeSstevel 
42793d19cdaeSstevel 	/* decorate the node with compatible */
42803d19cdaeSstevel 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, cdip,
42813d19cdaeSstevel 	    "compatible", compatible, ncompatible) != DDI_PROP_SUCCESS) {
42823d19cdaeSstevel 		goto fail;
42833d19cdaeSstevel 	}
42843d19cdaeSstevel 
42853d19cdaeSstevel 	/* add addressing properties to the node */
42863d19cdaeSstevel 	if (sf_create_props(cdip, target, lip_cnt) != 1) {
42873d19cdaeSstevel 		goto fail;
42883d19cdaeSstevel 	}
42893d19cdaeSstevel 
42903d19cdaeSstevel 	mutex_enter(&target->sft_mutex);
42913d19cdaeSstevel 	if (target->sft_dip != NULL) {
42923d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
42933d19cdaeSstevel 		goto fail;
42943d19cdaeSstevel 	}
42953d19cdaeSstevel 	target->sft_dip = cdip;
42963d19cdaeSstevel 	mutex_exit(&target->sft_mutex);
42973d19cdaeSstevel 
42983d19cdaeSstevel 	if (ndi_devi_online_async(cdip, 0) != DDI_SUCCESS) {
42993d19cdaeSstevel 		goto fail;
43003d19cdaeSstevel 	}
43013d19cdaeSstevel 
43023d19cdaeSstevel 	scsi_hba_nodename_compatible_free(nname, compatible);
43033d19cdaeSstevel 	return;
43043d19cdaeSstevel 
43053d19cdaeSstevel fail:
43063d19cdaeSstevel 	scsi_hba_nodename_compatible_free(nname, compatible);
43073d19cdaeSstevel 	if (cdip != NULL) {
43083d19cdaeSstevel 		(void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, NODE_WWN_PROP);
43093d19cdaeSstevel 		(void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, PORT_WWN_PROP);
43103d19cdaeSstevel 		(void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, LIP_CNT_PROP);
43113d19cdaeSstevel 		(void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, TARGET_PROP);
43123d19cdaeSstevel 		(void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, LUN_PROP);
43133d19cdaeSstevel 		if (ndi_devi_free(cdip) != NDI_SUCCESS) {
43143d19cdaeSstevel 			sf_log(sf, CE_WARN, "ndi_devi_free failed\n");
43153d19cdaeSstevel 		} else {
43163d19cdaeSstevel 			mutex_enter(&target->sft_mutex);
43173d19cdaeSstevel 			if (cdip == target->sft_dip) {
43183d19cdaeSstevel 				target->sft_dip = NULL;
43193d19cdaeSstevel 			}
43203d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
43213d19cdaeSstevel 		}
43223d19cdaeSstevel 	}
43233d19cdaeSstevel }
43243d19cdaeSstevel 
43253d19cdaeSstevel /*
43263d19cdaeSstevel  * create required properties, returning TRUE iff we succeed, else
43273d19cdaeSstevel  * returning FALSE
43283d19cdaeSstevel  */
43293d19cdaeSstevel static int
43303d19cdaeSstevel sf_create_props(dev_info_t *cdip, struct sf_target *target, int lip_cnt)
43313d19cdaeSstevel {
43323d19cdaeSstevel 	int tgt_id = sf_alpa_to_switch[target->sft_al_pa];
43333d19cdaeSstevel 
43343d19cdaeSstevel 
43353d19cdaeSstevel 	if (ndi_prop_update_byte_array(DDI_DEV_T_NONE,
43363d19cdaeSstevel 	    cdip, NODE_WWN_PROP, target->sft_node_wwn, FC_WWN_SIZE) !=
43373d19cdaeSstevel 	    DDI_PROP_SUCCESS) {
43383d19cdaeSstevel 		return (FALSE);
43393d19cdaeSstevel 	}
43403d19cdaeSstevel 
43413d19cdaeSstevel 	if (ndi_prop_update_byte_array(DDI_DEV_T_NONE,
43423d19cdaeSstevel 	    cdip, PORT_WWN_PROP, target->sft_port_wwn, FC_WWN_SIZE) !=
43433d19cdaeSstevel 	    DDI_PROP_SUCCESS) {
43443d19cdaeSstevel 		return (FALSE);
43453d19cdaeSstevel 	}
43463d19cdaeSstevel 
43473d19cdaeSstevel 	if (ndi_prop_update_int(DDI_DEV_T_NONE,
43483d19cdaeSstevel 	    cdip, LIP_CNT_PROP, lip_cnt) != DDI_PROP_SUCCESS) {
43493d19cdaeSstevel 		return (FALSE);
43503d19cdaeSstevel 	}
43513d19cdaeSstevel 
43523d19cdaeSstevel 	if (ndi_prop_update_int(DDI_DEV_T_NONE,
43533d19cdaeSstevel 	    cdip, TARGET_PROP, tgt_id) != DDI_PROP_SUCCESS) {
43543d19cdaeSstevel 		return (FALSE);
43553d19cdaeSstevel 	}
43563d19cdaeSstevel 
43573d19cdaeSstevel #ifndef	RAID_LUNS
43583d19cdaeSstevel 	if (ndi_prop_update_int(DDI_DEV_T_NONE,
43593d19cdaeSstevel 	    cdip, LUN_PROP, target->sft_lun.l) != DDI_PROP_SUCCESS) {
43603d19cdaeSstevel 		return (0);
43613d19cdaeSstevel 	}
43623d19cdaeSstevel #else
43633d19cdaeSstevel 	if (ndi_prop_update_int(DDI_DEV_T_NONE,
43643d19cdaeSstevel 	    cdip, LUN_PROP, target->sft_raid_lun) != DDI_PROP_SUCCESS) {
43653d19cdaeSstevel 		return (0);
43663d19cdaeSstevel 	}
43673d19cdaeSstevel #endif
43683d19cdaeSstevel 
43693d19cdaeSstevel 	return (TRUE);
43703d19cdaeSstevel }
43713d19cdaeSstevel 
43723d19cdaeSstevel 
43733d19cdaeSstevel /*
43743d19cdaeSstevel  * called by the transport to offline a target
43753d19cdaeSstevel  */
43763d19cdaeSstevel /* ARGSUSED */
43773d19cdaeSstevel static void
43783d19cdaeSstevel sf_offline_target(struct sf *sf, struct sf_target *target)
43793d19cdaeSstevel {
43803d19cdaeSstevel 	dev_info_t *dip;
43813d19cdaeSstevel 	struct sf_target *next_target = NULL;
43823d19cdaeSstevel 	struct sf_hp_elem	*elem;
43833d19cdaeSstevel 
43843d19cdaeSstevel 	ASSERT(mutex_owned(&sf->sf_mutex));
43853d19cdaeSstevel 
43863d19cdaeSstevel 	if (sf_core && (sf_core & SF_CORE_OFFLINE_TARGET)) {
43873d19cdaeSstevel 		(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
43883d19cdaeSstevel 		sf_core = 0;
43893d19cdaeSstevel 	}
43903d19cdaeSstevel 
43913d19cdaeSstevel 	while (target != NULL) {
43923d19cdaeSstevel 		sf_log(sf, CE_NOTE,
43933d19cdaeSstevel 		    "!target 0x%x al_pa 0x%x lun %" PRIx64 " offlined\n",
43943d19cdaeSstevel 		    sf_alpa_to_switch[target->sft_al_pa],
43953d19cdaeSstevel 		    target->sft_al_pa, SCSA_LUN(target));
43963d19cdaeSstevel 		mutex_enter(&target->sft_mutex);
43973d19cdaeSstevel 		target->sft_state &= ~(SF_TARGET_BUSY|SF_TARGET_MARK);
43983d19cdaeSstevel 		target->sft_state |= SF_TARGET_OFFLINE;
43993d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
44003d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
44013d19cdaeSstevel 
44023d19cdaeSstevel 		/* XXXX if this is LUN 0, offline all other LUNs */
44033d19cdaeSstevel 		if (next_target || target->sft_lun.l == 0)
44043d19cdaeSstevel 			next_target = target->sft_next_lun;
44053d19cdaeSstevel 
44063d19cdaeSstevel 		/* abort all cmds for this target */
44073d19cdaeSstevel 		sf_abort_all(sf, target, FALSE, sf->sf_lip_cnt, FALSE);
44083d19cdaeSstevel 
44093d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
44103d19cdaeSstevel 		mutex_enter(&target->sft_mutex);
44113d19cdaeSstevel 		if (target->sft_state & SF_TARGET_INIT_DONE) {
44123d19cdaeSstevel 			dip = target->sft_dip;
44133d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
44143d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
44153d19cdaeSstevel 			(void) ndi_prop_remove(DDI_DEV_T_NONE, dip,
44163d19cdaeSstevel 			    TARGET_PROP);
44173d19cdaeSstevel 			(void) ndi_event_retrieve_cookie(sf->sf_event_hdl,
44183d19cdaeSstevel 			    dip, FCAL_REMOVE_EVENT, &sf_remove_eid,
44193d19cdaeSstevel 			    NDI_EVENT_NOPASS);
44203d19cdaeSstevel 			(void) ndi_event_run_callbacks(sf->sf_event_hdl,
44213d19cdaeSstevel 			    target->sft_dip, sf_remove_eid, NULL);
44223d19cdaeSstevel 
44233d19cdaeSstevel 			elem = kmem_zalloc(sizeof (struct sf_hp_elem),
44243d19cdaeSstevel 			    KM_NOSLEEP);
44253d19cdaeSstevel 			if (elem != NULL) {
44263d19cdaeSstevel 				elem->dip = dip;
44273d19cdaeSstevel 				elem->target = target;
44283d19cdaeSstevel 				elem->what = SF_OFFLINE;
44293d19cdaeSstevel 				mutex_enter(&sf->sf_hp_daemon_mutex);
44303d19cdaeSstevel 				if (sf->sf_hp_elem_tail != NULL) {
44313d19cdaeSstevel 					sf->sf_hp_elem_tail->next = elem;
44323d19cdaeSstevel 					sf->sf_hp_elem_tail = elem;
44333d19cdaeSstevel 				} else {
44343d19cdaeSstevel 					sf->sf_hp_elem_head =
44353d19cdaeSstevel 					    sf->sf_hp_elem_tail =
44363d19cdaeSstevel 					    elem;
44373d19cdaeSstevel 				}
44383d19cdaeSstevel 				cv_signal(&sf->sf_hp_daemon_cv);
44393d19cdaeSstevel 				mutex_exit(&sf->sf_hp_daemon_mutex);
44403d19cdaeSstevel 			} else {
44413d19cdaeSstevel 				/* don't do NDI_DEVI_REMOVE for now */
44423d19cdaeSstevel 				if (ndi_devi_offline(dip, 0) != NDI_SUCCESS) {
44433d19cdaeSstevel 					SF_DEBUG(1, (sf, CE_WARN,
44443d19cdaeSstevel 					    "target %x lun %" PRIx64 ", "
44453d19cdaeSstevel 					    "device offline failed",
44463d19cdaeSstevel 					    sf_alpa_to_switch[target->
44473d19cdaeSstevel 					    sft_al_pa],
44483d19cdaeSstevel 					    SCSA_LUN(target)));
44493d19cdaeSstevel 				} else {
44503d19cdaeSstevel 					SF_DEBUG(1, (sf, CE_NOTE,
44513d19cdaeSstevel 					    "target %x, lun %" PRIx64 ", "
44523d19cdaeSstevel 					    "device offline succeeded\n",
44533d19cdaeSstevel 					    sf_alpa_to_switch[target->
44543d19cdaeSstevel 					    sft_al_pa],
44553d19cdaeSstevel 					    SCSA_LUN(target)));
44563d19cdaeSstevel 				}
44573d19cdaeSstevel 			}
44583d19cdaeSstevel 			mutex_enter(&sf->sf_mutex);
44593d19cdaeSstevel 		} else {
44603d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
44613d19cdaeSstevel 		}
44623d19cdaeSstevel 		target = next_target;
44633d19cdaeSstevel 	}
44643d19cdaeSstevel }
44653d19cdaeSstevel 
44663d19cdaeSstevel 
44673d19cdaeSstevel /*
44683d19cdaeSstevel  * routine to get/set a capability
44693d19cdaeSstevel  *
44703d19cdaeSstevel  * returning:
44713d19cdaeSstevel  *	1 (TRUE)	boolean capability is true (on get)
44723d19cdaeSstevel  *	0 (FALSE)	invalid capability, can't set capability (on set),
44733d19cdaeSstevel  *			or boolean capability is false (on get)
44743d19cdaeSstevel  *	-1 (UNDEFINED)	can't find capability (SCSA) or unsupported capability
44753d19cdaeSstevel  *	3		when getting SCSI version number
44763d19cdaeSstevel  *	AL_PA		when getting port initiator ID
44773d19cdaeSstevel  */
44783d19cdaeSstevel static int
44793d19cdaeSstevel sf_commoncap(struct scsi_address *ap, char *cap,
44803d19cdaeSstevel     int val, int tgtonly, int doset)
44813d19cdaeSstevel {
44823d19cdaeSstevel 	struct sf *sf = ADDR2SF(ap);
44833d19cdaeSstevel 	int cidx;
44843d19cdaeSstevel 	int rval = FALSE;
44853d19cdaeSstevel 
44863d19cdaeSstevel 
44873d19cdaeSstevel 	if (cap == NULL) {
44883d19cdaeSstevel 		SF_DEBUG(3, (sf, CE_WARN, "sf_commoncap: invalid arg"));
44893d19cdaeSstevel 		return (rval);
44903d19cdaeSstevel 	}
44913d19cdaeSstevel 
44923d19cdaeSstevel 	/* get index of capability string */
44933d19cdaeSstevel 	if ((cidx = scsi_hba_lookup_capstr(cap)) == -1) {
44943d19cdaeSstevel 		/* can't find capability */
44953d19cdaeSstevel 		return (UNDEFINED);
44963d19cdaeSstevel 	}
44973d19cdaeSstevel 
44983d19cdaeSstevel 	if (doset) {
44993d19cdaeSstevel 		/*
45003d19cdaeSstevel 		 * Process setcap request.
45013d19cdaeSstevel 		 */
45023d19cdaeSstevel 
45033d19cdaeSstevel 		/*
45043d19cdaeSstevel 		 * At present, we can only set binary (0/1) values
45053d19cdaeSstevel 		 */
45063d19cdaeSstevel 		switch (cidx) {
45073d19cdaeSstevel 		case SCSI_CAP_ARQ:	/* can't set this capability */
45083d19cdaeSstevel 			break;
45093d19cdaeSstevel 		default:
45103d19cdaeSstevel 			SF_DEBUG(3, (sf, CE_WARN,
45113d19cdaeSstevel 			    "sf_setcap: unsupported %d", cidx));
45123d19cdaeSstevel 			rval = UNDEFINED;
45133d19cdaeSstevel 			break;
45143d19cdaeSstevel 		}
45153d19cdaeSstevel 
45163d19cdaeSstevel 		SF_DEBUG(4, (sf, CE_NOTE,
45173d19cdaeSstevel 		    "set cap: cap=%s,val=0x%x,tgtonly=0x%x"
45183d19cdaeSstevel 		    ",doset=0x%x,rval=%d\n",
45193d19cdaeSstevel 		    cap, val, tgtonly, doset, rval));
45203d19cdaeSstevel 
45213d19cdaeSstevel 	} else {
45223d19cdaeSstevel 		/*
45233d19cdaeSstevel 		 * Process getcap request.
45243d19cdaeSstevel 		 */
45253d19cdaeSstevel 		switch (cidx) {
45263d19cdaeSstevel 		case SCSI_CAP_DMA_MAX:
45273d19cdaeSstevel 			break;		/* don't' have this capability */
45283d19cdaeSstevel 		case SCSI_CAP_INITIATOR_ID:
45293d19cdaeSstevel 			rval = sf->sf_al_pa;
45303d19cdaeSstevel 			break;
45313d19cdaeSstevel 		case SCSI_CAP_ARQ:
45323d19cdaeSstevel 			rval = TRUE;	/* do have this capability */
45333d19cdaeSstevel 			break;
45343d19cdaeSstevel 		case SCSI_CAP_RESET_NOTIFICATION:
45353d19cdaeSstevel 		case SCSI_CAP_TAGGED_QING:
45363d19cdaeSstevel 			rval = TRUE;	/* do have this capability */
45373d19cdaeSstevel 			break;
45383d19cdaeSstevel 		case SCSI_CAP_SCSI_VERSION:
45393d19cdaeSstevel 			rval = 3;
45403d19cdaeSstevel 			break;
45413d19cdaeSstevel 		case SCSI_CAP_INTERCONNECT_TYPE:
45423d19cdaeSstevel 			rval = INTERCONNECT_FIBRE;
45433d19cdaeSstevel 			break;
45443d19cdaeSstevel 		default:
45453d19cdaeSstevel 			SF_DEBUG(4, (sf, CE_WARN,
45463d19cdaeSstevel 			    "sf_scsi_getcap: unsupported"));
45473d19cdaeSstevel 			rval = UNDEFINED;
45483d19cdaeSstevel 			break;
45493d19cdaeSstevel 		}
45503d19cdaeSstevel 		SF_DEBUG(4, (sf, CE_NOTE,
45513d19cdaeSstevel 		    "get cap: cap=%s,val=0x%x,tgtonly=0x%x,"
45523d19cdaeSstevel 		    "doset=0x%x,rval=%d\n",
45533d19cdaeSstevel 		    cap, val, tgtonly, doset, rval));
45543d19cdaeSstevel 	}
45553d19cdaeSstevel 
45563d19cdaeSstevel 	return (rval);
45573d19cdaeSstevel }
45583d19cdaeSstevel 
45593d19cdaeSstevel 
45603d19cdaeSstevel /*
45613d19cdaeSstevel  * called by the transport to get a capability
45623d19cdaeSstevel  */
45633d19cdaeSstevel static int
45643d19cdaeSstevel sf_getcap(struct scsi_address *ap, char *cap, int whom)
45653d19cdaeSstevel {
45663d19cdaeSstevel 	return (sf_commoncap(ap, cap, 0, whom, FALSE));
45673d19cdaeSstevel }
45683d19cdaeSstevel 
45693d19cdaeSstevel 
45703d19cdaeSstevel /*
45713d19cdaeSstevel  * called by the transport to set a capability
45723d19cdaeSstevel  */
45733d19cdaeSstevel static int
45743d19cdaeSstevel sf_setcap(struct scsi_address *ap, char *cap, int value, int whom)
45753d19cdaeSstevel {
45763d19cdaeSstevel 	return (sf_commoncap(ap, cap, value, whom, TRUE));
45773d19cdaeSstevel }
45783d19cdaeSstevel 
45793d19cdaeSstevel 
45803d19cdaeSstevel /*
45813d19cdaeSstevel  * called by the transport to abort a target
45823d19cdaeSstevel  */
45833d19cdaeSstevel static int
45843d19cdaeSstevel sf_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
45853d19cdaeSstevel {
45863d19cdaeSstevel 	struct sf *sf = ADDR2SF(ap);
45873d19cdaeSstevel 	struct sf_target *target = ADDR2TARGET(ap);
45883d19cdaeSstevel 	struct sf_pkt *cmd, *ncmd, *pcmd;
45893d19cdaeSstevel 	struct fcal_packet *fpkt;
45903d19cdaeSstevel 	int	rval = 0, t, my_rval = FALSE;
45913d19cdaeSstevel 	int	old_target_state;
45923d19cdaeSstevel 	int	lip_cnt;
45933d19cdaeSstevel 	int	tgt_id;
45943d19cdaeSstevel 	fc_frame_header_t	*hp;
45953d19cdaeSstevel 	int	deferred_destroy;
45963d19cdaeSstevel 
45973d19cdaeSstevel 	deferred_destroy = 0;
45983d19cdaeSstevel 
45993d19cdaeSstevel 	if (pkt != NULL) {
4600602ca9eaScth 		cmd = PKT2CMD(pkt);
4601602ca9eaScth 		fpkt = cmd->cmd_fp_pkt;
46023d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_NOTE, "sf_abort packet %p\n",
46033d19cdaeSstevel 		    (void *)fpkt));
46043d19cdaeSstevel 		pcmd = NULL;
46053d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
46063d19cdaeSstevel 		ncmd = sf->sf_pkt_head;
46073d19cdaeSstevel 		while (ncmd != NULL) {
46083d19cdaeSstevel 			if (ncmd == cmd) {
46093d19cdaeSstevel 				if (pcmd != NULL) {
46103d19cdaeSstevel 					pcmd->cmd_next = cmd->cmd_next;
46113d19cdaeSstevel 				} else {
46123d19cdaeSstevel 					sf->sf_pkt_head = cmd->cmd_next;
46133d19cdaeSstevel 				}
46143d19cdaeSstevel 				cmd->cmd_flags &= ~CFLAG_IN_QUEUE;
46153d19cdaeSstevel 				cmd->cmd_state = SF_STATE_IDLE;
46163d19cdaeSstevel 				pkt->pkt_reason = CMD_ABORTED;
46173d19cdaeSstevel 				pkt->pkt_statistics |= STAT_ABORTED;
46183d19cdaeSstevel 				my_rval = TRUE;
46193d19cdaeSstevel 				break;
46203d19cdaeSstevel 			} else {
46213d19cdaeSstevel 				pcmd = ncmd;
46223d19cdaeSstevel 				ncmd = ncmd->cmd_next;
46233d19cdaeSstevel 			}
46243d19cdaeSstevel 		}
46253d19cdaeSstevel 		mutex_exit(&sf->sf_cmd_mutex);
46263d19cdaeSstevel 		if (ncmd == NULL) {
46273d19cdaeSstevel 			mutex_enter(&cmd->cmd_abort_mutex);
46283d19cdaeSstevel 			if (cmd->cmd_state == SF_STATE_ISSUED) {
46293d19cdaeSstevel 				cmd->cmd_state = SF_STATE_ABORTING;
46303d19cdaeSstevel 				cmd->cmd_timeout = sf_watchdog_time + 20;
46313d19cdaeSstevel 				mutex_exit(&cmd->cmd_abort_mutex);
46323d19cdaeSstevel 				/* call transport to abort command */
46333d19cdaeSstevel 				if (((rval = soc_abort(sf->sf_sochandle,
46343d19cdaeSstevel 				    sf->sf_socp, sf->sf_sochandle->fcal_portno,
46353d19cdaeSstevel 				    fpkt, 1)) == FCAL_ABORTED) ||
46363d19cdaeSstevel 				    (rval == FCAL_ABORT_FAILED)) {
46373d19cdaeSstevel 					my_rval = TRUE;
46383d19cdaeSstevel 					pkt->pkt_reason = CMD_ABORTED;
46393d19cdaeSstevel 					pkt->pkt_statistics |= STAT_ABORTED;
46403d19cdaeSstevel 					cmd->cmd_state = SF_STATE_IDLE;
46413d19cdaeSstevel 				} else if (rval == FCAL_BAD_ABORT) {
46423d19cdaeSstevel 					cmd->cmd_timeout = sf_watchdog_time
46433d19cdaeSstevel 					    + 20;
46443d19cdaeSstevel 					my_rval = FALSE;
46453d19cdaeSstevel 				} else {
46463d19cdaeSstevel 					SF_DEBUG(1, (sf, CE_NOTE,
46473d19cdaeSstevel 					    "Command Abort failed\n"));
46483d19cdaeSstevel 				}
46493d19cdaeSstevel 			} else {
46503d19cdaeSstevel 				mutex_exit(&cmd->cmd_abort_mutex);
46513d19cdaeSstevel 			}
46523d19cdaeSstevel 		}
46533d19cdaeSstevel 	} else {
46543d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_NOTE, "sf_abort target\n"));
46553d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
46563d19cdaeSstevel 		lip_cnt = sf->sf_lip_cnt;
46573d19cdaeSstevel 		mutex_enter(&target->sft_mutex);
46583d19cdaeSstevel 		if (target->sft_state & (SF_TARGET_BUSY |
46593d19cdaeSstevel 		    SF_TARGET_OFFLINE)) {
46603d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
46613d19cdaeSstevel 			return (rval);
46623d19cdaeSstevel 		}
46633d19cdaeSstevel 		old_target_state = target->sft_state;
46643d19cdaeSstevel 		target->sft_state |= SF_TARGET_BUSY;
46653d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
46663d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
46673d19cdaeSstevel 
46683d19cdaeSstevel 		if ((pkt = sf_scsi_init_pkt(ap, NULL, NULL, 0,
46693d19cdaeSstevel 		    0, 0, 0, NULL, 0)) != NULL) {
46703d19cdaeSstevel 
46713d19cdaeSstevel 			cmd = PKT2CMD(pkt);
46723d19cdaeSstevel 			cmd->cmd_block->fcp_cntl.cntl_abort_tsk = 1;
46733d19cdaeSstevel 			cmd->cmd_fp_pkt->fcal_pkt_comp = NULL;
46743d19cdaeSstevel 			cmd->cmd_pkt->pkt_flags |= FLAG_NOINTR;
46753d19cdaeSstevel 
46763d19cdaeSstevel 			/* prepare the packet for transport */
46773d19cdaeSstevel 			if (sf_prepare_pkt(sf, cmd, target) == TRAN_ACCEPT) {
46783d19cdaeSstevel 
46793d19cdaeSstevel 				cmd->cmd_state = SF_STATE_ISSUED;
46803d19cdaeSstevel 				/*
46813d19cdaeSstevel 				 * call transport to send a pkt polled
46823d19cdaeSstevel 				 *
46833d19cdaeSstevel 				 * if that fails call the transport to abort it
46843d19cdaeSstevel 				 */
46853d19cdaeSstevel 				if (soc_transport_poll(sf->sf_sochandle,
46863d19cdaeSstevel 				    cmd->cmd_fp_pkt, SF_ABORT_TIMEOUT,
46873d19cdaeSstevel 				    CQ_REQUEST_1) == FCAL_TRANSPORT_SUCCESS) {
46883d19cdaeSstevel 					(void) ddi_dma_sync(
46893d19cdaeSstevel 					    cmd->cmd_cr_pool->rsp_dma_handle,
46903d19cdaeSstevel 					    (off_t)
46913d19cdaeSstevel 					    ((caddr_t)cmd->cmd_rsp_block -
46923d19cdaeSstevel 					    cmd->cmd_cr_pool->rsp_base),
46933d19cdaeSstevel 					    FCP_MAX_RSP_IU_SIZE,
46943d19cdaeSstevel 					    DDI_DMA_SYNC_FORKERNEL);
46953d19cdaeSstevel 					if (((struct fcp_rsp_info *)
46963d19cdaeSstevel 					    (&cmd->cmd_rsp_block->
46973d19cdaeSstevel 					    fcp_response_len + 1))->
46983d19cdaeSstevel 					    rsp_code == FCP_NO_FAILURE) {
46993d19cdaeSstevel 						/* abort cmds for this targ */
47003d19cdaeSstevel 						sf_abort_all(sf, target, TRUE,
47013d19cdaeSstevel 						    lip_cnt, TRUE);
47023d19cdaeSstevel 					} else {
47033d19cdaeSstevel 						hp = &cmd->cmd_fp_pkt->
47043d19cdaeSstevel 						    fcal_socal_request.
47053d19cdaeSstevel 						    sr_fc_frame_hdr;
47063d19cdaeSstevel 						tgt_id = sf_alpa_to_switch[
47073d19cdaeSstevel 						    (uchar_t)hp->d_id];
47083d19cdaeSstevel 						sf->sf_stats.tstats[tgt_id].
47093d19cdaeSstevel 						    task_mgmt_failures++;
47103d19cdaeSstevel 						SF_DEBUG(1, (sf, CE_NOTE,
47113d19cdaeSstevel 						    "Target %d Abort Task "
47123d19cdaeSstevel 						    "Set failed\n", hp->d_id));
47133d19cdaeSstevel 					}
47143d19cdaeSstevel 				} else {
47153d19cdaeSstevel 					mutex_enter(&cmd->cmd_abort_mutex);
47163d19cdaeSstevel 					if (cmd->cmd_state == SF_STATE_ISSUED) {
47173d19cdaeSstevel 					cmd->cmd_state = SF_STATE_ABORTING;
47183d19cdaeSstevel 					cmd->cmd_timeout = sf_watchdog_time
47193d19cdaeSstevel 					    + 20;
47203d19cdaeSstevel 					mutex_exit(&cmd->cmd_abort_mutex);
47213d19cdaeSstevel 					if ((t = soc_abort(sf->sf_sochandle,
47223d19cdaeSstevel 					    sf->sf_socp, sf->sf_sochandle->
47233d19cdaeSstevel 					    fcal_portno, cmd->cmd_fp_pkt, 1)) !=
47243d19cdaeSstevel 					    FCAL_ABORTED &&
47253d19cdaeSstevel 					    (t != FCAL_ABORT_FAILED)) {
47263d19cdaeSstevel 						sf_log(sf, CE_NOTE,
47273d19cdaeSstevel 						    "sf_abort failed, "
47283d19cdaeSstevel 						    "initiating LIP\n");
47293d19cdaeSstevel 						sf_force_lip(sf);
47303d19cdaeSstevel 						deferred_destroy = 1;
47313d19cdaeSstevel 					}
47323d19cdaeSstevel 					} else {
47333d19cdaeSstevel 					mutex_exit(&cmd->cmd_abort_mutex);
47343d19cdaeSstevel 					}
47353d19cdaeSstevel 				}
47363d19cdaeSstevel 			}
47373d19cdaeSstevel 			if (!deferred_destroy) {
47383d19cdaeSstevel 				cmd->cmd_fp_pkt->fcal_pkt_comp =
47393d19cdaeSstevel 				    sf_cmd_callback;
47403d19cdaeSstevel 				cmd->cmd_block->fcp_cntl.cntl_abort_tsk = 0;
47413d19cdaeSstevel 				sf_scsi_destroy_pkt(ap, pkt);
47423d19cdaeSstevel 				my_rval = TRUE;
47433d19cdaeSstevel 			}
47443d19cdaeSstevel 		}
47453d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
47463d19cdaeSstevel 		if (lip_cnt == sf->sf_lip_cnt) {
47473d19cdaeSstevel 			mutex_enter(&target->sft_mutex);
47483d19cdaeSstevel 			target->sft_state = old_target_state;
47493d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
47503d19cdaeSstevel 		}
47513d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
47523d19cdaeSstevel 	}
47533d19cdaeSstevel 	return (my_rval);
47543d19cdaeSstevel }
47553d19cdaeSstevel 
47563d19cdaeSstevel 
47573d19cdaeSstevel /*
47583d19cdaeSstevel  * called by the transport and internally to reset a target
47593d19cdaeSstevel  */
47603d19cdaeSstevel static int
47613d19cdaeSstevel sf_reset(struct scsi_address *ap, int level)
47623d19cdaeSstevel {
47633d19cdaeSstevel 	struct scsi_pkt *pkt;
47643d19cdaeSstevel 	struct fcal_packet *fpkt;
47653d19cdaeSstevel 	struct sf *sf = ADDR2SF(ap);
47663d19cdaeSstevel 	struct sf_target *target = ADDR2TARGET(ap), *ntarget;
47673d19cdaeSstevel 	struct sf_pkt *cmd;
47683d19cdaeSstevel 	int	rval = FALSE, t;
47693d19cdaeSstevel 	int	lip_cnt;
47703d19cdaeSstevel 	int	tgt_id, ret;
47713d19cdaeSstevel 	fc_frame_header_t	*hp;
47723d19cdaeSstevel 	int	deferred_destroy;
47733d19cdaeSstevel 
47743d19cdaeSstevel 	/* We don't support RESET_LUN yet. */
47753d19cdaeSstevel 	if (level == RESET_TARGET) {
47763d19cdaeSstevel 		struct sf_reset_list *p;
47773d19cdaeSstevel 
47783d19cdaeSstevel 		if ((p = kmem_alloc(sizeof (struct sf_reset_list), KM_NOSLEEP))
47793d19cdaeSstevel 		    == NULL)
47803d19cdaeSstevel 			return (rval);
47813d19cdaeSstevel 
47823d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_NOTE, "sf_reset target\n"));
47833d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
47843d19cdaeSstevel 		/* All target resets go to LUN 0 */
47853d19cdaeSstevel 		if (target->sft_lun.l) {
47863d19cdaeSstevel 			target = sf_lookup_target(sf, target->sft_port_wwn, 0);
47873d19cdaeSstevel 		}
47883d19cdaeSstevel 		mutex_enter(&target->sft_mutex);
47893d19cdaeSstevel 		if (target->sft_state & (SF_TARGET_BUSY |
47903d19cdaeSstevel 		    SF_TARGET_OFFLINE)) {
47913d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
47923d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
47933d19cdaeSstevel 			kmem_free(p, sizeof (struct sf_reset_list));
47943d19cdaeSstevel 			return (rval);
47953d19cdaeSstevel 		}
47963d19cdaeSstevel 		lip_cnt = sf->sf_lip_cnt;
47973d19cdaeSstevel 		target->sft_state |= SF_TARGET_BUSY;
47983d19cdaeSstevel 		for (ntarget = target->sft_next_lun;
47993d19cdaeSstevel 		    ntarget;
48003d19cdaeSstevel 		    ntarget = ntarget->sft_next_lun) {
48013d19cdaeSstevel 			mutex_enter(&ntarget->sft_mutex);
48023d19cdaeSstevel 			/*
48033d19cdaeSstevel 			 * XXXX If we supported RESET_LUN we should check here
48043d19cdaeSstevel 			 * to see if any LUN were being reset and somehow fail
48053d19cdaeSstevel 			 * that operation.
48063d19cdaeSstevel 			 */
48073d19cdaeSstevel 			ntarget->sft_state |= SF_TARGET_BUSY;
48083d19cdaeSstevel 			mutex_exit(&ntarget->sft_mutex);
48093d19cdaeSstevel 		}
48103d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
48113d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
48123d19cdaeSstevel 
48133d19cdaeSstevel 		deferred_destroy = 0;
48143d19cdaeSstevel 		if ((pkt = sf_scsi_init_pkt(ap, NULL, NULL, 0,
48153d19cdaeSstevel 		    0, 0, 0, NULL, 0)) != NULL) {
48163d19cdaeSstevel 			cmd = PKT2CMD(pkt);
48173d19cdaeSstevel 			cmd->cmd_block->fcp_cntl.cntl_reset = 1;
48183d19cdaeSstevel 			cmd->cmd_fp_pkt->fcal_pkt_comp = NULL;
48193d19cdaeSstevel 			cmd->cmd_pkt->pkt_flags |= FLAG_NOINTR;
48203d19cdaeSstevel 
48213d19cdaeSstevel 			/* prepare the packet for transport */
48223d19cdaeSstevel 			if (sf_prepare_pkt(sf, cmd, target) == TRAN_ACCEPT) {
48233d19cdaeSstevel 				/* call transport to send a pkt polled */
48243d19cdaeSstevel 				cmd->cmd_state = SF_STATE_ISSUED;
48253d19cdaeSstevel 				if ((ret = soc_transport_poll(sf->sf_sochandle,
48263d19cdaeSstevel 				    cmd->cmd_fp_pkt, SF_ABORT_TIMEOUT,
48273d19cdaeSstevel 				    CQ_REQUEST_1)) == FCAL_TRANSPORT_SUCCESS) {
48283d19cdaeSstevel 					(void) ddi_dma_sync(cmd->cmd_cr_pool->
48293d19cdaeSstevel 					    rsp_dma_handle, (caddr_t)cmd->
48303d19cdaeSstevel 					    cmd_rsp_block - cmd->cmd_cr_pool->
48313d19cdaeSstevel 					    rsp_base, FCP_MAX_RSP_IU_SIZE,
48323d19cdaeSstevel 					    DDI_DMA_SYNC_FORKERNEL);
48333d19cdaeSstevel 					fpkt = cmd->cmd_fp_pkt;
48343d19cdaeSstevel 					if ((fpkt->fcal_pkt_status ==
48353d19cdaeSstevel 					    FCAL_STATUS_OK) &&
48363d19cdaeSstevel 					    (((struct fcp_rsp_info *)
48373d19cdaeSstevel 					    (&cmd->cmd_rsp_block->
48383d19cdaeSstevel 					    fcp_response_len + 1))->
48393d19cdaeSstevel 					    rsp_code == FCP_NO_FAILURE)) {
48403d19cdaeSstevel 						sf_log(sf, CE_NOTE,
48413d19cdaeSstevel 						    "!sf%d: Target 0x%x Reset "
48423d19cdaeSstevel 						    "successful\n",
48433d19cdaeSstevel 						    ddi_get_instance(\
48443d19cdaeSstevel 						    sf->sf_dip),
48453d19cdaeSstevel 						    sf_alpa_to_switch[
48463d19cdaeSstevel 						    target->sft_al_pa]);
48473d19cdaeSstevel 						rval = TRUE;
48483d19cdaeSstevel 					} else {
48493d19cdaeSstevel 						hp = &cmd->cmd_fp_pkt->
48503d19cdaeSstevel 						    fcal_socal_request.
48513d19cdaeSstevel 						    sr_fc_frame_hdr;
48523d19cdaeSstevel 						tgt_id = sf_alpa_to_switch[
48533d19cdaeSstevel 						    (uchar_t)hp->d_id];
48543d19cdaeSstevel 						sf->sf_stats.tstats[tgt_id].
48553d19cdaeSstevel 						    task_mgmt_failures++;
48563d19cdaeSstevel 						sf_log(sf, CE_NOTE,
48573d19cdaeSstevel 						    "!sf%d: Target 0x%x "
48583d19cdaeSstevel 						    "Reset failed."
48593d19cdaeSstevel 						    "Status code 0x%x "
48603d19cdaeSstevel 						    "Resp code 0x%x\n",
48613d19cdaeSstevel 						    ddi_get_instance(\
48623d19cdaeSstevel 						    sf->sf_dip),
48633d19cdaeSstevel 						    tgt_id,
48643d19cdaeSstevel 						    fpkt->fcal_pkt_status,
48653d19cdaeSstevel 						    ((struct fcp_rsp_info *)
48663d19cdaeSstevel 						    (&cmd->cmd_rsp_block->
48673d19cdaeSstevel 						    fcp_response_len + 1))->
48683d19cdaeSstevel 						    rsp_code);
48693d19cdaeSstevel 					}
48703d19cdaeSstevel 				} else {
48713d19cdaeSstevel 					sf_log(sf, CE_NOTE, "!sf%d: Target "
48723d19cdaeSstevel 					    "0x%x Reset Failed. Ret=%x\n",
48733d19cdaeSstevel 					    ddi_get_instance(sf->sf_dip),
48743d19cdaeSstevel 					    sf_alpa_to_switch[
48753d19cdaeSstevel 					    target->sft_al_pa], ret);
48763d19cdaeSstevel 					mutex_enter(&cmd->cmd_abort_mutex);
48773d19cdaeSstevel 					if (cmd->cmd_state == SF_STATE_ISSUED) {
48783d19cdaeSstevel 					/* call the transport to abort a cmd */
48793d19cdaeSstevel 					cmd->cmd_timeout = sf_watchdog_time
48803d19cdaeSstevel 					    + 20;
48813d19cdaeSstevel 					cmd->cmd_state = SF_STATE_ABORTING;
48823d19cdaeSstevel 					mutex_exit(&cmd->cmd_abort_mutex);
48833d19cdaeSstevel 					if (((t = soc_abort(sf->sf_sochandle,
48843d19cdaeSstevel 					    sf->sf_socp,
48853d19cdaeSstevel 					    sf->sf_sochandle->fcal_portno,
48863d19cdaeSstevel 					    cmd->cmd_fp_pkt, 1)) !=
48873d19cdaeSstevel 					    FCAL_ABORTED) &&
48883d19cdaeSstevel 					    (t != FCAL_ABORT_FAILED)) {
48893d19cdaeSstevel 						sf_log(sf, CE_NOTE,
48903d19cdaeSstevel 						    "!sf%d: Target 0x%x Reset "
48913d19cdaeSstevel 						    "failed. Abort Failed, "
48923d19cdaeSstevel 						    "forcing LIP\n",
48933d19cdaeSstevel 						    ddi_get_instance(
48943d19cdaeSstevel 						    sf->sf_dip),
48953d19cdaeSstevel 						    sf_alpa_to_switch[
48963d19cdaeSstevel 						    target->sft_al_pa]);
48973d19cdaeSstevel 						sf_force_lip(sf);
48983d19cdaeSstevel 						rval = TRUE;
48993d19cdaeSstevel 						deferred_destroy = 1;
49003d19cdaeSstevel 					}
49013d19cdaeSstevel 					} else {
490219397407SSherry Moore 						mutex_exit
490319397407SSherry Moore 						    (&cmd->cmd_abort_mutex);
49043d19cdaeSstevel 					}
49053d19cdaeSstevel 				}
49063d19cdaeSstevel 			}
49073d19cdaeSstevel 			/*
49083d19cdaeSstevel 			 * Defer releasing the packet if we abort returned with
49093d19cdaeSstevel 			 * a BAD_ABORT or timed out, because there is a
49103d19cdaeSstevel 			 * possibility that the ucode might return it.
49113d19cdaeSstevel 			 * We wait for at least 20s and let it be released
49123d19cdaeSstevel 			 * by the sf_watch thread
49133d19cdaeSstevel 			 */
49143d19cdaeSstevel 			if (!deferred_destroy) {
49153d19cdaeSstevel 				cmd->cmd_block->fcp_cntl.cntl_reset = 0;
49163d19cdaeSstevel 				cmd->cmd_fp_pkt->fcal_pkt_comp =
49173d19cdaeSstevel 				    sf_cmd_callback;
49183d19cdaeSstevel 				cmd->cmd_state = SF_STATE_IDLE;
49193d19cdaeSstevel 				/* for cache */
49203d19cdaeSstevel 				sf_scsi_destroy_pkt(ap, pkt);
49213d19cdaeSstevel 			}
49223d19cdaeSstevel 		} else {
49233d19cdaeSstevel 			cmn_err(CE_WARN, "!sf%d: Target 0x%x Reset Failed. "
49243d19cdaeSstevel 			    "Resource allocation error.\n",
49253d19cdaeSstevel 			    ddi_get_instance(sf->sf_dip),
49263d19cdaeSstevel 			    sf_alpa_to_switch[target->sft_al_pa]);
49273d19cdaeSstevel 		}
49283d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
49293d19cdaeSstevel 		if ((rval == TRUE) && (lip_cnt == sf->sf_lip_cnt)) {
49303d19cdaeSstevel 			p->target = target;
49313d19cdaeSstevel 			p->lip_cnt = lip_cnt;
49323d19cdaeSstevel 			p->timeout = ddi_get_lbolt() +
49333d19cdaeSstevel 			    drv_usectohz(SF_TARGET_RESET_DELAY);
49343d19cdaeSstevel 			p->next = sf->sf_reset_list;
49353d19cdaeSstevel 			sf->sf_reset_list = p;
49363d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
49373d19cdaeSstevel 			mutex_enter(&sf_global_mutex);
49383d19cdaeSstevel 			if (sf_reset_timeout_id == 0) {
49393d19cdaeSstevel 				sf_reset_timeout_id = timeout(
49403d19cdaeSstevel 				    sf_check_reset_delay, NULL,
49413d19cdaeSstevel 				    drv_usectohz(SF_TARGET_RESET_DELAY));
49423d19cdaeSstevel 			}
49433d19cdaeSstevel 			mutex_exit(&sf_global_mutex);
49443d19cdaeSstevel 		} else {
49453d19cdaeSstevel 			if (lip_cnt == sf->sf_lip_cnt) {
49463d19cdaeSstevel 				mutex_enter(&target->sft_mutex);
49473d19cdaeSstevel 				target->sft_state &= ~SF_TARGET_BUSY;
49483d19cdaeSstevel 				for (ntarget = target->sft_next_lun;
49493d19cdaeSstevel 				    ntarget;
49503d19cdaeSstevel 				    ntarget = ntarget->sft_next_lun) {
49513d19cdaeSstevel 					mutex_enter(&ntarget->sft_mutex);
49523d19cdaeSstevel 					ntarget->sft_state &= ~SF_TARGET_BUSY;
49533d19cdaeSstevel 					mutex_exit(&ntarget->sft_mutex);
49543d19cdaeSstevel 				}
49553d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
49563d19cdaeSstevel 			}
49573d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
49583d19cdaeSstevel 			kmem_free(p, sizeof (struct sf_reset_list));
49593d19cdaeSstevel 		}
49603d19cdaeSstevel 	} else {
49613d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
49623d19cdaeSstevel 		if ((sf->sf_state == SF_STATE_OFFLINE) &&
49633d19cdaeSstevel 		    (sf_watchdog_time < sf->sf_timer)) {
49643d19cdaeSstevel 			/*
49653d19cdaeSstevel 			 * We are currently in a lip, so let this one
49663d19cdaeSstevel 			 * finish before forcing another one.
49673d19cdaeSstevel 			 */
49683d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
49693d19cdaeSstevel 			return (TRUE);
49703d19cdaeSstevel 		}
49713d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
49723d19cdaeSstevel 		sf_log(sf, CE_NOTE, "!sf:Target driver initiated lip\n");
49733d19cdaeSstevel 		sf_force_lip(sf);
49743d19cdaeSstevel 		rval = TRUE;
49753d19cdaeSstevel 	}
49763d19cdaeSstevel 	return (rval);
49773d19cdaeSstevel }
49783d19cdaeSstevel 
49793d19cdaeSstevel 
49803d19cdaeSstevel /*
49813d19cdaeSstevel  * abort all commands for a target
49823d19cdaeSstevel  *
49833d19cdaeSstevel  * if try_abort is set then send an abort
49843d19cdaeSstevel  * if abort is set then this is abort, else this is a reset
49853d19cdaeSstevel  */
49863d19cdaeSstevel static void
49873d19cdaeSstevel sf_abort_all(struct sf *sf, struct sf_target *target, int abort, int
49883d19cdaeSstevel     lip_cnt, int try_abort)
49893d19cdaeSstevel {
49903d19cdaeSstevel 	struct sf_target *ntarget;
49913d19cdaeSstevel 	struct sf_pkt *cmd, *head = NULL, *tail = NULL, *pcmd = NULL, *tcmd;
49923d19cdaeSstevel 	struct fcal_packet *fpkt;
49933d19cdaeSstevel 	struct scsi_pkt *pkt;
49943d19cdaeSstevel 	int rval = FCAL_ABORTED;
49953d19cdaeSstevel 
49963d19cdaeSstevel 	/*
49973d19cdaeSstevel 	 * First pull all commands for all LUNs on this target out of the
49983d19cdaeSstevel 	 * overflow list.  We can tell it's the same target by comparing
49993d19cdaeSstevel 	 * the node WWN.
50003d19cdaeSstevel 	 */
50013d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
50023d19cdaeSstevel 	if (lip_cnt == sf->sf_lip_cnt) {
50033d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
50043d19cdaeSstevel 		cmd = sf->sf_pkt_head;
50053d19cdaeSstevel 		while (cmd != NULL) {
50063d19cdaeSstevel 			ntarget = ADDR2TARGET(&cmd->cmd_pkt->
50073d19cdaeSstevel 			    pkt_address);
50083d19cdaeSstevel 			if (ntarget == target) {
50093d19cdaeSstevel 				if (pcmd != NULL)
50103d19cdaeSstevel 					pcmd->cmd_next = cmd->cmd_next;
50113d19cdaeSstevel 				else
50123d19cdaeSstevel 					sf->sf_pkt_head = cmd->cmd_next;
50133d19cdaeSstevel 				if (sf->sf_pkt_tail == cmd) {
50143d19cdaeSstevel 					sf->sf_pkt_tail = pcmd;
50153d19cdaeSstevel 					if (pcmd != NULL)
50163d19cdaeSstevel 						pcmd->cmd_next = NULL;
50173d19cdaeSstevel 				}
50183d19cdaeSstevel 				tcmd = cmd->cmd_next;
50193d19cdaeSstevel 				if (head == NULL) {
50203d19cdaeSstevel 					head = cmd;
50213d19cdaeSstevel 					tail = cmd;
50223d19cdaeSstevel 				} else {
50233d19cdaeSstevel 					tail->cmd_next = cmd;
50243d19cdaeSstevel 					tail = cmd;
50253d19cdaeSstevel 				}
50263d19cdaeSstevel 				cmd->cmd_next = NULL;
50273d19cdaeSstevel 				cmd = tcmd;
50283d19cdaeSstevel 			} else {
50293d19cdaeSstevel 				pcmd = cmd;
50303d19cdaeSstevel 				cmd = cmd->cmd_next;
50313d19cdaeSstevel 			}
50323d19cdaeSstevel 		}
50333d19cdaeSstevel 		mutex_exit(&sf->sf_cmd_mutex);
50343d19cdaeSstevel 	}
50353d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
50363d19cdaeSstevel 
50373d19cdaeSstevel 	/*
50383d19cdaeSstevel 	 * Now complete all the commands on our list.  In the process,
50393d19cdaeSstevel 	 * the completion routine may take the commands off the target
50403d19cdaeSstevel 	 * lists.
50413d19cdaeSstevel 	 */
50423d19cdaeSstevel 	cmd = head;
50433d19cdaeSstevel 	while (cmd != NULL) {
50443d19cdaeSstevel 		pkt = cmd->cmd_pkt;
50453d19cdaeSstevel 		if (abort) {
50463d19cdaeSstevel 			pkt->pkt_reason = CMD_ABORTED;
50473d19cdaeSstevel 			pkt->pkt_statistics |= STAT_ABORTED;
50483d19cdaeSstevel 		} else {
50493d19cdaeSstevel 			pkt->pkt_reason = CMD_RESET;
50503d19cdaeSstevel 			pkt->pkt_statistics |= STAT_DEV_RESET;
50513d19cdaeSstevel 		}
50523d19cdaeSstevel 		cmd->cmd_flags &= ~CFLAG_IN_QUEUE;
50533d19cdaeSstevel 		cmd->cmd_state = SF_STATE_IDLE;
50543d19cdaeSstevel 		cmd = cmd->cmd_next;
50553d19cdaeSstevel 		/*
50563d19cdaeSstevel 		 * call the packet completion routine only for
50573d19cdaeSstevel 		 * non-polled commands. Ignore the polled commands as
50583d19cdaeSstevel 		 * they timeout and will be handled differently
50593d19cdaeSstevel 		 */
50603d19cdaeSstevel 		if ((pkt->pkt_comp) && !(pkt->pkt_flags & FLAG_NOINTR))
50613d19cdaeSstevel 			(*pkt->pkt_comp)(pkt);
50623d19cdaeSstevel 
50633d19cdaeSstevel 	}
50643d19cdaeSstevel 
50653d19cdaeSstevel 	/*
50663d19cdaeSstevel 	 * Finally get all outstanding commands for each LUN, and abort them if
50673d19cdaeSstevel 	 * they've been issued, and call the completion routine.
50683d19cdaeSstevel 	 * For the case where sf_offline_target is called from sf_watch
50693d19cdaeSstevel 	 * due to a Offline Timeout, it is quite possible that the soc+
50703d19cdaeSstevel 	 * ucode is hosed and therefore  cannot return the commands.
50713d19cdaeSstevel 	 * Clear up all the issued commands as well.
50723d19cdaeSstevel 	 * Try_abort will be false only if sf_abort_all is coming from
50733d19cdaeSstevel 	 * sf_target_offline.
50743d19cdaeSstevel 	 */
50753d19cdaeSstevel 
50763d19cdaeSstevel 	if (try_abort || sf->sf_state == SF_STATE_OFFLINE) {
50773d19cdaeSstevel 		mutex_enter(&target->sft_pkt_mutex);
50783d19cdaeSstevel 		cmd = tcmd = target->sft_pkt_head;
50793d19cdaeSstevel 		while (cmd != (struct sf_pkt *)&target->sft_pkt_head) {
50803d19cdaeSstevel 			fpkt = cmd->cmd_fp_pkt;
50813d19cdaeSstevel 			pkt = cmd->cmd_pkt;
50823d19cdaeSstevel 			mutex_enter(&cmd->cmd_abort_mutex);
50833d19cdaeSstevel 			if ((cmd->cmd_state == SF_STATE_ISSUED) &&
50843d19cdaeSstevel 			    (fpkt->fcal_cmd_state &
50853d19cdaeSstevel 			    FCAL_CMD_IN_TRANSPORT) &&
50863d19cdaeSstevel 			    ((fpkt->fcal_cmd_state & FCAL_CMD_COMPLETE) ==
50873d19cdaeSstevel 			    0) && !(pkt->pkt_flags & FLAG_NOINTR)) {
50883d19cdaeSstevel 				cmd->cmd_state = SF_STATE_ABORTING;
50893d19cdaeSstevel 				cmd->cmd_timeout = sf_watchdog_time +
50903d19cdaeSstevel 				    cmd->cmd_pkt->pkt_time + 20;
50913d19cdaeSstevel 				mutex_exit(&cmd->cmd_abort_mutex);
50923d19cdaeSstevel 				mutex_exit(&target->sft_pkt_mutex);
50933d19cdaeSstevel 				if (try_abort) {
50943d19cdaeSstevel 					/* call the transport to abort a pkt */
50953d19cdaeSstevel 					rval = soc_abort(sf->sf_sochandle,
50963d19cdaeSstevel 					    sf->sf_socp,
50973d19cdaeSstevel 					    sf->sf_sochandle->fcal_portno,
50983d19cdaeSstevel 					    fpkt, 1);
50993d19cdaeSstevel 				}
51003d19cdaeSstevel 				if ((rval == FCAL_ABORTED) ||
51013d19cdaeSstevel 				    (rval == FCAL_ABORT_FAILED)) {
51023d19cdaeSstevel 					if (abort) {
51033d19cdaeSstevel 						pkt->pkt_reason = CMD_ABORTED;
51043d19cdaeSstevel 						pkt->pkt_statistics |=
51053d19cdaeSstevel 						    STAT_ABORTED;
51063d19cdaeSstevel 					} else {
51073d19cdaeSstevel 						pkt->pkt_reason = CMD_RESET;
51083d19cdaeSstevel 						pkt->pkt_statistics |=
51093d19cdaeSstevel 						    STAT_DEV_RESET;
51103d19cdaeSstevel 					}
51113d19cdaeSstevel 					cmd->cmd_state = SF_STATE_IDLE;
51123d19cdaeSstevel 					if (pkt->pkt_comp)
51133d19cdaeSstevel 						(*pkt->pkt_comp)(pkt);
51143d19cdaeSstevel 				}
51153d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
51163d19cdaeSstevel 				if (lip_cnt != sf->sf_lip_cnt) {
51173d19cdaeSstevel 					mutex_exit(&sf->sf_mutex);
51183d19cdaeSstevel 					return;
51193d19cdaeSstevel 				}
51203d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
51213d19cdaeSstevel 				mutex_enter(&target->sft_pkt_mutex);
51223d19cdaeSstevel 				cmd = target->sft_pkt_head;
51233d19cdaeSstevel 			} else {
51243d19cdaeSstevel 				mutex_exit(&cmd->cmd_abort_mutex);
51253d19cdaeSstevel 				cmd = cmd->cmd_forw;
51263d19cdaeSstevel 			}
51273d19cdaeSstevel 		}
51283d19cdaeSstevel 		mutex_exit(&target->sft_pkt_mutex);
51293d19cdaeSstevel 	}
51303d19cdaeSstevel }
51313d19cdaeSstevel 
51323d19cdaeSstevel 
51333d19cdaeSstevel /*
51343d19cdaeSstevel  * called by the transport to start a packet
51353d19cdaeSstevel  */
51363d19cdaeSstevel static int
51373d19cdaeSstevel sf_start(struct scsi_address *ap, struct scsi_pkt *pkt)
51383d19cdaeSstevel {
51393d19cdaeSstevel 	struct sf *sf = ADDR2SF(ap);
51403d19cdaeSstevel 	struct sf_target *target = ADDR2TARGET(ap);
51413d19cdaeSstevel 	struct sf_pkt *cmd = PKT2CMD(pkt);
51423d19cdaeSstevel 	int rval;
51433d19cdaeSstevel 
51443d19cdaeSstevel 
51453d19cdaeSstevel 	SF_DEBUG(6, (sf, CE_NOTE, "sf_start\n"));
51463d19cdaeSstevel 
51473d19cdaeSstevel 	if (cmd->cmd_state == SF_STATE_ISSUED) {
51483d19cdaeSstevel 		cmn_err(CE_PANIC, "sf: issuing packet twice 0x%p\n",
51493d19cdaeSstevel 		    (void *)cmd);
51503d19cdaeSstevel 	}
51513d19cdaeSstevel 
51523d19cdaeSstevel 	/* prepare the packet for transport */
51533d19cdaeSstevel 	if ((rval = sf_prepare_pkt(sf, cmd, target)) != TRAN_ACCEPT) {
51543d19cdaeSstevel 		return (rval);
51553d19cdaeSstevel 	}
51563d19cdaeSstevel 
51573d19cdaeSstevel 	if (target->sft_state & (SF_TARGET_BUSY|SF_TARGET_OFFLINE)) {
51583d19cdaeSstevel 		if (target->sft_state & SF_TARGET_OFFLINE) {
51593d19cdaeSstevel 			return (TRAN_FATAL_ERROR);
51603d19cdaeSstevel 		}
51613d19cdaeSstevel 		if (pkt->pkt_flags & FLAG_NOINTR) {
51623d19cdaeSstevel 			return (TRAN_BUSY);
51633d19cdaeSstevel 		}
51643d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
51653d19cdaeSstevel 		sf->sf_use_lock = TRUE;
51663d19cdaeSstevel 		goto enque;
51673d19cdaeSstevel 	}
51683d19cdaeSstevel 
51693d19cdaeSstevel 
51703d19cdaeSstevel 	/* if no interrupts then do polled I/O */
51713d19cdaeSstevel 	if (pkt->pkt_flags & FLAG_NOINTR) {
51723d19cdaeSstevel 		return (sf_dopoll(sf, cmd));
51733d19cdaeSstevel 	}
51743d19cdaeSstevel 
51753d19cdaeSstevel 	/* regular interrupt-driven I/O */
51763d19cdaeSstevel 
51773d19cdaeSstevel 	if (!sf->sf_use_lock) {
51783d19cdaeSstevel 
51793d19cdaeSstevel 		/* locking no needed */
51803d19cdaeSstevel 
51813d19cdaeSstevel 		cmd->cmd_timeout = cmd->cmd_pkt->pkt_time ?
51823d19cdaeSstevel 		    sf_watchdog_time + cmd->cmd_pkt->pkt_time : 0;
51833d19cdaeSstevel 		cmd->cmd_state = SF_STATE_ISSUED;
51843d19cdaeSstevel 
51853d19cdaeSstevel 		/* call the transport to send a pkt */
51863d19cdaeSstevel 		if (soc_transport(sf->sf_sochandle, cmd->cmd_fp_pkt,
51873d19cdaeSstevel 		    FCAL_NOSLEEP, CQ_REQUEST_1) != FCAL_TRANSPORT_SUCCESS) {
51883d19cdaeSstevel 			cmd->cmd_state = SF_STATE_IDLE;
51893d19cdaeSstevel 			return (TRAN_BADPKT);
51903d19cdaeSstevel 		}
51913d19cdaeSstevel 		return (TRAN_ACCEPT);
51923d19cdaeSstevel 	}
51933d19cdaeSstevel 
51943d19cdaeSstevel 	/* regular I/O using locking */
51953d19cdaeSstevel 
51963d19cdaeSstevel 	mutex_enter(&sf->sf_cmd_mutex);
51973d19cdaeSstevel 	if ((sf->sf_ncmds >= sf->sf_throttle) ||
51983d19cdaeSstevel 	    (sf->sf_pkt_head != NULL)) {
51993d19cdaeSstevel enque:
52003d19cdaeSstevel 		/*
52013d19cdaeSstevel 		 * either we're throttling back or there are already commands
52023d19cdaeSstevel 		 * on the queue, so enqueue this one for later
52033d19cdaeSstevel 		 */
52043d19cdaeSstevel 		cmd->cmd_flags |= CFLAG_IN_QUEUE;
52053d19cdaeSstevel 		if (sf->sf_pkt_head != NULL) {
52063d19cdaeSstevel 			/* add to the queue */
52073d19cdaeSstevel 			sf->sf_pkt_tail->cmd_next = cmd;
52083d19cdaeSstevel 			cmd->cmd_next = NULL;
52093d19cdaeSstevel 			sf->sf_pkt_tail = cmd;
52103d19cdaeSstevel 		} else {
52113d19cdaeSstevel 			/* this is the first entry in the queue */
52123d19cdaeSstevel 			sf->sf_pkt_head = sf->sf_pkt_tail = cmd;
52133d19cdaeSstevel 			cmd->cmd_next = NULL;
52143d19cdaeSstevel 		}
52153d19cdaeSstevel 		mutex_exit(&sf->sf_cmd_mutex);
52163d19cdaeSstevel 		return (TRAN_ACCEPT);
52173d19cdaeSstevel 	}
52183d19cdaeSstevel 
52193d19cdaeSstevel 	/*
52203d19cdaeSstevel 	 * start this packet now
52213d19cdaeSstevel 	 */
52223d19cdaeSstevel 
52233d19cdaeSstevel 	/* still have cmd mutex */
52243d19cdaeSstevel 	return (sf_start_internal(sf, cmd));
52253d19cdaeSstevel }
52263d19cdaeSstevel 
52273d19cdaeSstevel 
52283d19cdaeSstevel /*
52293d19cdaeSstevel  * internal routine to start a packet from the queue now
52303d19cdaeSstevel  *
52313d19cdaeSstevel  * enter with cmd mutex held and leave with it released
52323d19cdaeSstevel  */
52333d19cdaeSstevel static int
52343d19cdaeSstevel sf_start_internal(struct sf *sf, struct sf_pkt *cmd)
52353d19cdaeSstevel {
52363d19cdaeSstevel 	/* we have the cmd mutex */
52373d19cdaeSstevel 	sf->sf_ncmds++;
52383d19cdaeSstevel 	mutex_exit(&sf->sf_cmd_mutex);
52393d19cdaeSstevel 
52403d19cdaeSstevel 	ASSERT(cmd->cmd_state != SF_STATE_ISSUED);
52413d19cdaeSstevel 	SF_DEBUG(6, (sf, CE_NOTE, "sf_start_internal\n"));
52423d19cdaeSstevel 
52433d19cdaeSstevel 	cmd->cmd_timeout = cmd->cmd_pkt->pkt_time ? sf_watchdog_time +
52443d19cdaeSstevel 	    cmd->cmd_pkt->pkt_time : 0;
52453d19cdaeSstevel 	cmd->cmd_state = SF_STATE_ISSUED;
52463d19cdaeSstevel 
52473d19cdaeSstevel 	/* call transport to send the pkt */
52483d19cdaeSstevel 	if (soc_transport(sf->sf_sochandle, cmd->cmd_fp_pkt, FCAL_NOSLEEP,
52493d19cdaeSstevel 	    CQ_REQUEST_1) != FCAL_TRANSPORT_SUCCESS) {
52503d19cdaeSstevel 		cmd->cmd_state = SF_STATE_IDLE;
52513d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
52523d19cdaeSstevel 		sf->sf_ncmds--;
52533d19cdaeSstevel 		mutex_exit(&sf->sf_cmd_mutex);
52543d19cdaeSstevel 		return (TRAN_BADPKT);
52553d19cdaeSstevel 	}
52563d19cdaeSstevel 	return (TRAN_ACCEPT);
52573d19cdaeSstevel }
52583d19cdaeSstevel 
52593d19cdaeSstevel 
52603d19cdaeSstevel /*
52613d19cdaeSstevel  * prepare a packet for transport
52623d19cdaeSstevel  */
52633d19cdaeSstevel static int
52643d19cdaeSstevel sf_prepare_pkt(struct sf *sf, struct sf_pkt *cmd, struct sf_target *target)
52653d19cdaeSstevel {
52663d19cdaeSstevel 	struct fcp_cmd *fcmd = cmd->cmd_block;
52673d19cdaeSstevel 
52683d19cdaeSstevel /* XXXX Need to set the LUN ? */
52693d19cdaeSstevel 	bcopy((caddr_t)&target->sft_lun.b,
52703d19cdaeSstevel 	    (caddr_t)&fcmd->fcp_ent_addr,
52713d19cdaeSstevel 	    FCP_LUN_SIZE);
52723d19cdaeSstevel 	cmd->cmd_pkt->pkt_reason = CMD_CMPLT;
52733d19cdaeSstevel 	cmd->cmd_pkt->pkt_state = 0;
52743d19cdaeSstevel 	cmd->cmd_pkt->pkt_statistics = 0;
52753d19cdaeSstevel 
52763d19cdaeSstevel 
52773d19cdaeSstevel 	if ((cmd->cmd_pkt->pkt_comp == NULL) &&
52783d19cdaeSstevel 	    ((cmd->cmd_pkt->pkt_flags & FLAG_NOINTR) == 0)) {
52793d19cdaeSstevel 		return (TRAN_BADPKT);
52803d19cdaeSstevel 	}
52813d19cdaeSstevel 
52823d19cdaeSstevel 	/* invalidate imp field(s) of rsp block */
52833d19cdaeSstevel 	cmd->cmd_rsp_block->fcp_u.i_fcp_status = SF_BAD_DMA_MAGIC;
52843d19cdaeSstevel 
52853d19cdaeSstevel 	/* set up amt of I/O to do */
52863d19cdaeSstevel 	if (cmd->cmd_flags & CFLAG_DMAVALID) {
52873d19cdaeSstevel 		cmd->cmd_pkt->pkt_resid = cmd->cmd_dmacount;
52883d19cdaeSstevel 		if (cmd->cmd_flags & CFLAG_CMDIOPB) {
52893d19cdaeSstevel 			(void) ddi_dma_sync(cmd->cmd_dmahandle, 0, 0,
52903d19cdaeSstevel 			    DDI_DMA_SYNC_FORDEV);
52913d19cdaeSstevel 		}
52923d19cdaeSstevel 	} else {
52933d19cdaeSstevel 		cmd->cmd_pkt->pkt_resid = 0;
52943d19cdaeSstevel 	}
52953d19cdaeSstevel 
52963d19cdaeSstevel 	/* set up the Tagged Queuing type */
52973d19cdaeSstevel 	if (cmd->cmd_pkt->pkt_flags & FLAG_HTAG) {
52983d19cdaeSstevel 		fcmd->fcp_cntl.cntl_qtype = FCP_QTYPE_HEAD_OF_Q;
52993d19cdaeSstevel 	} else if (cmd->cmd_pkt->pkt_flags & FLAG_OTAG) {
53003d19cdaeSstevel 		fcmd->fcp_cntl.cntl_qtype = FCP_QTYPE_ORDERED;
53013d19cdaeSstevel 	}
53023d19cdaeSstevel 
53033d19cdaeSstevel 	/*
53043d19cdaeSstevel 	 * Sync the cmd segment
53053d19cdaeSstevel 	 */
53063d19cdaeSstevel 	(void) ddi_dma_sync(cmd->cmd_cr_pool->cmd_dma_handle,
53073d19cdaeSstevel 	    (caddr_t)fcmd - cmd->cmd_cr_pool->cmd_base,
53083d19cdaeSstevel 	    sizeof (struct fcp_cmd), DDI_DMA_SYNC_FORDEV);
53093d19cdaeSstevel 
53103d19cdaeSstevel 	sf_fill_ids(sf, cmd, target);
53113d19cdaeSstevel 	return (TRAN_ACCEPT);
53123d19cdaeSstevel }
53133d19cdaeSstevel 
53143d19cdaeSstevel 
53153d19cdaeSstevel /*
53163d19cdaeSstevel  * fill in packet hdr source and destination IDs and hdr byte count
53173d19cdaeSstevel  */
53183d19cdaeSstevel static void
53193d19cdaeSstevel sf_fill_ids(struct sf *sf, struct sf_pkt *cmd, struct sf_target *target)
53203d19cdaeSstevel {
53213d19cdaeSstevel 	struct fcal_packet *fpkt = cmd->cmd_fp_pkt;
53223d19cdaeSstevel 	fc_frame_header_t	*hp;
53233d19cdaeSstevel 
53243d19cdaeSstevel 
53253d19cdaeSstevel 	hp = &fpkt->fcal_socal_request.sr_fc_frame_hdr;
53263d19cdaeSstevel 	hp->d_id = target->sft_al_pa;
53273d19cdaeSstevel 	hp->s_id = sf->sf_al_pa;
53283d19cdaeSstevel 	fpkt->fcal_socal_request.sr_soc_hdr.sh_byte_cnt =
53293d19cdaeSstevel 	    cmd->cmd_dmacookie.dmac_size;
53303d19cdaeSstevel }
53313d19cdaeSstevel 
53323d19cdaeSstevel 
53333d19cdaeSstevel /*
53343d19cdaeSstevel  * do polled I/O using transport
53353d19cdaeSstevel  */
53363d19cdaeSstevel static int
53373d19cdaeSstevel sf_dopoll(struct sf *sf, struct sf_pkt *cmd)
53383d19cdaeSstevel {
53393d19cdaeSstevel 	int timeout;
53403d19cdaeSstevel 	int rval;
53413d19cdaeSstevel 
53423d19cdaeSstevel 
53433d19cdaeSstevel 	mutex_enter(&sf->sf_cmd_mutex);
53443d19cdaeSstevel 	sf->sf_ncmds++;
53453d19cdaeSstevel 	mutex_exit(&sf->sf_cmd_mutex);
53463d19cdaeSstevel 
53473d19cdaeSstevel 	timeout = cmd->cmd_pkt->pkt_time ? cmd->cmd_pkt->pkt_time
53483d19cdaeSstevel 	    : SF_POLL_TIMEOUT;
53493d19cdaeSstevel 	cmd->cmd_timeout = 0;
53503d19cdaeSstevel 	cmd->cmd_fp_pkt->fcal_pkt_comp = NULL;
53513d19cdaeSstevel 	cmd->cmd_state = SF_STATE_ISSUED;
53523d19cdaeSstevel 
53533d19cdaeSstevel 	/* call transport to send a pkt polled */
53543d19cdaeSstevel 	rval = soc_transport_poll(sf->sf_sochandle, cmd->cmd_fp_pkt,
53553d19cdaeSstevel 	    timeout*1000000, CQ_REQUEST_1);
53563d19cdaeSstevel 	mutex_enter(&cmd->cmd_abort_mutex);
53573d19cdaeSstevel 	cmd->cmd_fp_pkt->fcal_pkt_comp = sf_cmd_callback;
53583d19cdaeSstevel 	if (rval != FCAL_TRANSPORT_SUCCESS) {
53593d19cdaeSstevel 		if (rval == FCAL_TRANSPORT_TIMEOUT) {
53603d19cdaeSstevel 			cmd->cmd_state = SF_STATE_ABORTING;
53613d19cdaeSstevel 			mutex_exit(&cmd->cmd_abort_mutex);
53623d19cdaeSstevel 			(void) sf_target_timeout(sf, cmd);
53633d19cdaeSstevel 		} else {
53643d19cdaeSstevel 			mutex_exit(&cmd->cmd_abort_mutex);
53653d19cdaeSstevel 		}
53663d19cdaeSstevel 		cmd->cmd_state = SF_STATE_IDLE;
53673d19cdaeSstevel 		cmd->cmd_fp_pkt->fcal_pkt_comp = sf_cmd_callback;
53683d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
53693d19cdaeSstevel 		sf->sf_ncmds--;
53703d19cdaeSstevel 		mutex_exit(&sf->sf_cmd_mutex);
53713d19cdaeSstevel 		return (TRAN_BADPKT);
53723d19cdaeSstevel 	}
53733d19cdaeSstevel 	mutex_exit(&cmd->cmd_abort_mutex);
53743d19cdaeSstevel 	cmd->cmd_fp_pkt->fcal_pkt_comp = sf_cmd_callback;
53753d19cdaeSstevel 	sf_cmd_callback(cmd->cmd_fp_pkt);
53763d19cdaeSstevel 	return (TRAN_ACCEPT);
53773d19cdaeSstevel }
53783d19cdaeSstevel 
53793d19cdaeSstevel 
53803d19cdaeSstevel /* a shortcut for defining debug messages below */
53813d19cdaeSstevel #ifdef	DEBUG
53823d19cdaeSstevel #define	SF_DMSG1(s)		msg1 = s
53833d19cdaeSstevel #else
53843d19cdaeSstevel #define	SF_DMSG1(s)		/* do nothing */
53853d19cdaeSstevel #endif
53863d19cdaeSstevel 
53873d19cdaeSstevel 
53883d19cdaeSstevel /*
53893d19cdaeSstevel  * the pkt_comp callback for command packets
53903d19cdaeSstevel  */
53913d19cdaeSstevel static void
53923d19cdaeSstevel sf_cmd_callback(struct fcal_packet *fpkt)
53933d19cdaeSstevel {
53943d19cdaeSstevel 	struct sf_pkt *cmd = (struct sf_pkt *)fpkt->fcal_pkt_private;
53953d19cdaeSstevel 	struct scsi_pkt *pkt = cmd->cmd_pkt;
53963d19cdaeSstevel 	struct sf *sf = ADDR2SF(&pkt->pkt_address);
53973d19cdaeSstevel 	struct sf_target *target = ADDR2TARGET(&pkt->pkt_address);
53983d19cdaeSstevel 	struct fcp_rsp *rsp;
53993d19cdaeSstevel 	char *msg1 = NULL;
54003d19cdaeSstevel 	char *msg2 = NULL;
54013d19cdaeSstevel 	short ncmds;
54023d19cdaeSstevel 	int tgt_id;
54033d19cdaeSstevel 	int good_scsi_status = TRUE;
54043d19cdaeSstevel 
54053d19cdaeSstevel 
54063d19cdaeSstevel 
54073d19cdaeSstevel 	if (cmd->cmd_state == SF_STATE_IDLE) {
54083d19cdaeSstevel 		cmn_err(CE_PANIC, "sf: completing idle packet 0x%p\n",
54093d19cdaeSstevel 		    (void *)cmd);
54103d19cdaeSstevel 	}
54113d19cdaeSstevel 
54123d19cdaeSstevel 	mutex_enter(&cmd->cmd_abort_mutex);
54133d19cdaeSstevel 	if (cmd->cmd_state == SF_STATE_ABORTING) {
54143d19cdaeSstevel 		/* cmd already being aborted -- nothing to do */
54153d19cdaeSstevel 		mutex_exit(&cmd->cmd_abort_mutex);
54163d19cdaeSstevel 		return;
54173d19cdaeSstevel 	}
54183d19cdaeSstevel 
54193d19cdaeSstevel 	cmd->cmd_state = SF_STATE_IDLE;
54203d19cdaeSstevel 	mutex_exit(&cmd->cmd_abort_mutex);
54213d19cdaeSstevel 
54223d19cdaeSstevel 	if (fpkt->fcal_pkt_status == FCAL_STATUS_OK) {
54233d19cdaeSstevel 
54243d19cdaeSstevel 		(void) ddi_dma_sync(cmd->cmd_cr_pool->rsp_dma_handle,
54253d19cdaeSstevel 		    (caddr_t)cmd->cmd_rsp_block - cmd->cmd_cr_pool->rsp_base,
54263d19cdaeSstevel 		    FCP_MAX_RSP_IU_SIZE, DDI_DMA_SYNC_FORKERNEL);
54273d19cdaeSstevel 
54283d19cdaeSstevel 		rsp = (struct fcp_rsp *)cmd->cmd_rsp_block;
54293d19cdaeSstevel 
54303d19cdaeSstevel 		if (rsp->fcp_u.i_fcp_status == SF_BAD_DMA_MAGIC) {
54313d19cdaeSstevel 
54323d19cdaeSstevel 			if (sf_core && (sf_core & SF_CORE_BAD_DMA)) {
54333d19cdaeSstevel 				sf_token = (int *)(uintptr_t)
54343d19cdaeSstevel 				    fpkt->fcal_socal_request.\
54353d19cdaeSstevel 				    sr_soc_hdr.sh_request_token;
54363d19cdaeSstevel 				(void) soc_take_core(sf->sf_sochandle,
54373d19cdaeSstevel 				    sf->sf_socp);
54383d19cdaeSstevel 			}
54393d19cdaeSstevel 
54403d19cdaeSstevel 			pkt->pkt_reason = CMD_INCOMPLETE;
54413d19cdaeSstevel 			pkt->pkt_state = STATE_GOT_BUS;
54423d19cdaeSstevel 			pkt->pkt_statistics |= STAT_ABORTED;
54433d19cdaeSstevel 
54443d19cdaeSstevel 		} else {
54453d19cdaeSstevel 
54463d19cdaeSstevel 			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
54473d19cdaeSstevel 			    STATE_SENT_CMD | STATE_GOT_STATUS;
54483d19cdaeSstevel 			pkt->pkt_resid = 0;
54493d19cdaeSstevel 			if (cmd->cmd_flags & CFLAG_DMAVALID) {
54503d19cdaeSstevel 				pkt->pkt_state |= STATE_XFERRED_DATA;
54513d19cdaeSstevel 			}
54523d19cdaeSstevel 
54533d19cdaeSstevel 			if ((pkt->pkt_scbp != NULL) &&
54543d19cdaeSstevel 			    ((*(pkt->pkt_scbp) =
54553d19cdaeSstevel 			    rsp->fcp_u.fcp_status.scsi_status)
54563d19cdaeSstevel 			    != STATUS_GOOD)) {
54573d19cdaeSstevel 				good_scsi_status = FALSE;
54583d19cdaeSstevel 			/*
54593d19cdaeSstevel 			 * The next two checks make sure that if there
54603d19cdaeSstevel 			 * is no sense data or a valid response and
54613d19cdaeSstevel 			 * the command came back with check condition,
54623d19cdaeSstevel 			 * the command should be retried
54633d19cdaeSstevel 			 */
54643d19cdaeSstevel 				if (!rsp->fcp_u.fcp_status.rsp_len_set &&
54653d19cdaeSstevel 				    !rsp->fcp_u.fcp_status.sense_len_set) {
54663d19cdaeSstevel 					pkt->pkt_state &= ~STATE_XFERRED_DATA;
54673d19cdaeSstevel 					pkt->pkt_resid = cmd->cmd_dmacount;
54683d19cdaeSstevel 				}
54693d19cdaeSstevel 			}
54703d19cdaeSstevel 
54713d19cdaeSstevel 			if ((cmd->cmd_flags & CFLAG_CMDIOPB) &&
54723d19cdaeSstevel 			    (pkt->pkt_state & STATE_XFERRED_DATA)) {
54733d19cdaeSstevel 				(void) ddi_dma_sync(cmd->cmd_dmahandle, 0,
54743d19cdaeSstevel 				    (uint_t)0, DDI_DMA_SYNC_FORCPU);
54753d19cdaeSstevel 			}
54763d19cdaeSstevel 			/*
54773d19cdaeSstevel 			 * Update the transfer resid, if appropriate
54783d19cdaeSstevel 			 */
54793d19cdaeSstevel 			if (rsp->fcp_u.fcp_status.resid_over ||
54803d19cdaeSstevel 			    rsp->fcp_u.fcp_status.resid_under)
54813d19cdaeSstevel 				pkt->pkt_resid = rsp->fcp_resid;
54823d19cdaeSstevel 
54833d19cdaeSstevel 			/*
54843d19cdaeSstevel 			 * Check to see if the SCSI command failed.
54853d19cdaeSstevel 			 *
54863d19cdaeSstevel 			 */
54873d19cdaeSstevel 
54883d19cdaeSstevel 			/*
54893d19cdaeSstevel 			 * First see if we got a FCP protocol error.
54903d19cdaeSstevel 			 */
54913d19cdaeSstevel 			if (rsp->fcp_u.fcp_status.rsp_len_set) {
54923d19cdaeSstevel 				struct fcp_rsp_info *bep;
54933d19cdaeSstevel 
549419397407SSherry Moore 				bep = (struct fcp_rsp_info *)
549519397407SSherry Moore 				    (&rsp->fcp_response_len + 1);
54963d19cdaeSstevel 				if (bep->rsp_code != FCP_NO_FAILURE) {
54973d19cdaeSstevel 						pkt->pkt_reason = CMD_TRAN_ERR;
54983d19cdaeSstevel 					tgt_id = pkt->pkt_address.a_target;
54993d19cdaeSstevel 					switch (bep->rsp_code) {
55003d19cdaeSstevel 					case FCP_CMND_INVALID:
55013d19cdaeSstevel 						SF_DMSG1("FCP_RSP FCP_CMND "
55023d19cdaeSstevel 						    "fields invalid");
55033d19cdaeSstevel 						break;
55043d19cdaeSstevel 					case FCP_TASK_MGMT_NOT_SUPPTD:
55053d19cdaeSstevel 						SF_DMSG1("FCP_RSP Task"
55063d19cdaeSstevel 						    "Management Function"
55073d19cdaeSstevel 						    "Not Supported");
55083d19cdaeSstevel 						break;
55093d19cdaeSstevel 					case FCP_TASK_MGMT_FAILED:
55103d19cdaeSstevel 						SF_DMSG1("FCP_RSP Task "
55113d19cdaeSstevel 						    "Management Function"
55123d19cdaeSstevel 						    "Failed");
55133d19cdaeSstevel 						sf->sf_stats.tstats[tgt_id].
55143d19cdaeSstevel 						    task_mgmt_failures++;
55153d19cdaeSstevel 						break;
55163d19cdaeSstevel 					case FCP_DATA_RO_MISMATCH:
55173d19cdaeSstevel 						SF_DMSG1("FCP_RSP FCP_DATA RO "
55183d19cdaeSstevel 						    "mismatch with "
55193d19cdaeSstevel 						    "FCP_XFER_RDY DATA_RO");
55203d19cdaeSstevel 						sf->sf_stats.tstats[tgt_id].
55213d19cdaeSstevel 						    data_ro_mismatches++;
55223d19cdaeSstevel 						break;
55233d19cdaeSstevel 					case FCP_DL_LEN_MISMATCH:
552419397407SSherry Moore 						SF_DMSG1("FCP_RSP FCP_DATA "
552519397407SSherry Moore 						    "length "
55263d19cdaeSstevel 						    "different than BURST_LEN");
55273d19cdaeSstevel 						sf->sf_stats.tstats[tgt_id].
55283d19cdaeSstevel 						    dl_len_mismatches++;
55293d19cdaeSstevel 						break;
55303d19cdaeSstevel 					default:
553119397407SSherry Moore 						SF_DMSG1("FCP_RSP invalid "
553219397407SSherry Moore 						    "RSP_CODE");
55333d19cdaeSstevel 						break;
55343d19cdaeSstevel 					}
55353d19cdaeSstevel 				}
55363d19cdaeSstevel 			}
55373d19cdaeSstevel 
55383d19cdaeSstevel 			/*
55393d19cdaeSstevel 			 * See if we got a SCSI error with sense data
55403d19cdaeSstevel 			 */
55413d19cdaeSstevel 			if (rsp->fcp_u.fcp_status.sense_len_set) {
55423d19cdaeSstevel 				uchar_t rqlen = min(rsp->fcp_sense_len,
55433d19cdaeSstevel 				    sizeof (struct scsi_extended_sense));
55443d19cdaeSstevel 				caddr_t sense = (caddr_t)rsp +
554519397407SSherry Moore 				    sizeof (struct fcp_rsp) +
554619397407SSherry Moore 				    rsp->fcp_response_len;
55473d19cdaeSstevel 				struct scsi_arq_status *arq;
55483d19cdaeSstevel 				struct scsi_extended_sense *sensep =
55493d19cdaeSstevel 				    (struct scsi_extended_sense *)sense;
55503d19cdaeSstevel 
55513d19cdaeSstevel 				if (rsp->fcp_u.fcp_status.scsi_status !=
55523d19cdaeSstevel 				    STATUS_GOOD) {
55533d19cdaeSstevel 				if (rsp->fcp_u.fcp_status.scsi_status
55543d19cdaeSstevel 				    == STATUS_CHECK) {
55553d19cdaeSstevel 					if (sensep->es_key ==
55563d19cdaeSstevel 					    KEY_RECOVERABLE_ERROR)
55573d19cdaeSstevel 						good_scsi_status = 1;
55583d19cdaeSstevel 					if (sensep->es_key ==
55593d19cdaeSstevel 					    KEY_UNIT_ATTENTION &&
55603d19cdaeSstevel 					    sensep->es_add_code == 0x3f &&
55613d19cdaeSstevel 					    sensep->es_qual_code == 0x0e) {
55623d19cdaeSstevel 						/* REPORT_LUNS_HAS_CHANGED */
55633d19cdaeSstevel 						sf_log(sf, CE_NOTE,
55643d19cdaeSstevel 						"!REPORT_LUNS_HAS_CHANGED\n");
55653d19cdaeSstevel 						sf_force_lip(sf);
55663d19cdaeSstevel 					}
55673d19cdaeSstevel 				}
55683d19cdaeSstevel 				}
55693d19cdaeSstevel 
55703d19cdaeSstevel 				if ((pkt->pkt_scbp != NULL) &&
55713d19cdaeSstevel 				    (cmd->cmd_scblen >=
55723d19cdaeSstevel 					sizeof (struct scsi_arq_status))) {
55733d19cdaeSstevel 
55743d19cdaeSstevel 				pkt->pkt_state |= STATE_ARQ_DONE;
55753d19cdaeSstevel 
55763d19cdaeSstevel 				arq = (struct scsi_arq_status *)pkt->pkt_scbp;
55773d19cdaeSstevel 				/*
55783d19cdaeSstevel 				 * copy out sense information
55793d19cdaeSstevel 				 */
55803d19cdaeSstevel 				bcopy(sense, (caddr_t)&arq->sts_sensedata,
55813d19cdaeSstevel 				    rqlen);
55823d19cdaeSstevel 				arq->sts_rqpkt_resid =
55833d19cdaeSstevel 				    sizeof (struct scsi_extended_sense) -
55843d19cdaeSstevel 					rqlen;
55853d19cdaeSstevel 				*((uchar_t *)&arq->sts_rqpkt_status) =
55863d19cdaeSstevel 				    STATUS_GOOD;
55873d19cdaeSstevel 				arq->sts_rqpkt_reason = 0;
55883d19cdaeSstevel 				arq->sts_rqpkt_statistics = 0;
55893d19cdaeSstevel 				arq->sts_rqpkt_state = STATE_GOT_BUS |
55903d19cdaeSstevel 				    STATE_GOT_TARGET | STATE_SENT_CMD |
55913d19cdaeSstevel 				    STATE_GOT_STATUS | STATE_ARQ_DONE |
55923d19cdaeSstevel 				    STATE_XFERRED_DATA;
55933d19cdaeSstevel 			    }
55943d19cdaeSstevel 				target->sft_alive = TRUE;
55953d19cdaeSstevel 			}
55963d19cdaeSstevel 
55973d19cdaeSstevel 			/*
55983d19cdaeSstevel 			 * The firmware returns the number of bytes actually
55993d19cdaeSstevel 			 * xfered into/out of host. Compare this with what
56003d19cdaeSstevel 			 * we asked and if it is different, we lost frames ?
56013d19cdaeSstevel 			 */
56023d19cdaeSstevel 			if ((pkt->pkt_reason == 0) && (pkt->pkt_resid == 0) &&
56033d19cdaeSstevel 			    (good_scsi_status) &&
56043d19cdaeSstevel 			    (pkt->pkt_state & STATE_XFERRED_DATA) &&
56053d19cdaeSstevel 			    (!(cmd->cmd_flags & CFLAG_CMDIOPB)) &&
56063d19cdaeSstevel 			    (target->sft_device_type != DTYPE_ESI)) {
56073d19cdaeSstevel 				int byte_cnt =
560819397407SSherry Moore 				    fpkt->fcal_socal_request.
560919397407SSherry Moore 				    sr_soc_hdr.sh_byte_cnt;
56103d19cdaeSstevel 				if (cmd->cmd_flags & CFLAG_DMASEND) {
56113d19cdaeSstevel 					if (byte_cnt != 0) {
56123d19cdaeSstevel 					sf_log(sf, CE_NOTE,
56133d19cdaeSstevel 					    "!sf_cmd_callback: Lost Frame: "
56143d19cdaeSstevel 					    "(write) received 0x%x expected"
56153d19cdaeSstevel 					    " 0x%x target 0x%x\n",
56163d19cdaeSstevel 					    byte_cnt, cmd->cmd_dmacount,
56173d19cdaeSstevel 					    sf_alpa_to_switch[
56183d19cdaeSstevel 					    target->sft_al_pa]);
56193d19cdaeSstevel 					pkt->pkt_reason = CMD_INCOMPLETE;
56203d19cdaeSstevel 					pkt->pkt_statistics |= STAT_ABORTED;
56213d19cdaeSstevel 					}
56223d19cdaeSstevel 				} else if (byte_cnt < cmd->cmd_dmacount) {
56233d19cdaeSstevel 					sf_log(sf, CE_NOTE,
562419397407SSherry Moore 					    "!sf_cmd_callback: "
562519397407SSherry Moore 					    "Lost Frame: (read) "
56263d19cdaeSstevel 					    "received 0x%x expected 0x%x "
56273d19cdaeSstevel 					    "target 0x%x\n", byte_cnt,
562819397407SSherry Moore 					    cmd->cmd_dmacount,
562919397407SSherry Moore 					    sf_alpa_to_switch[
56303d19cdaeSstevel 					    target->sft_al_pa]);
56313d19cdaeSstevel 					pkt->pkt_reason = CMD_INCOMPLETE;
56323d19cdaeSstevel 					pkt->pkt_statistics |= STAT_ABORTED;
56333d19cdaeSstevel 				}
56343d19cdaeSstevel 			}
56353d19cdaeSstevel 		}
56363d19cdaeSstevel 
56373d19cdaeSstevel 	} else {
56383d19cdaeSstevel 
56393d19cdaeSstevel 		/* pkt status was not ok */
56403d19cdaeSstevel 
56413d19cdaeSstevel 		switch (fpkt->fcal_pkt_status) {
56423d19cdaeSstevel 
56433d19cdaeSstevel 		case FCAL_STATUS_ERR_OFFLINE:
56443d19cdaeSstevel 			SF_DMSG1("Fibre Channel Offline");
56453d19cdaeSstevel 			mutex_enter(&target->sft_mutex);
56463d19cdaeSstevel 			if (!(target->sft_state & SF_TARGET_OFFLINE)) {
56473d19cdaeSstevel 				target->sft_state |= (SF_TARGET_BUSY
56483d19cdaeSstevel 				    | SF_TARGET_MARK);
56493d19cdaeSstevel 			}
56503d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
56513d19cdaeSstevel 			(void) ndi_event_retrieve_cookie(sf->sf_event_hdl,
56523d19cdaeSstevel 			    target->sft_dip, FCAL_REMOVE_EVENT,
56533d19cdaeSstevel 			    &sf_remove_eid, NDI_EVENT_NOPASS);
56543d19cdaeSstevel 			(void) ndi_event_run_callbacks(sf->sf_event_hdl,
56553d19cdaeSstevel 			    target->sft_dip, sf_remove_eid, NULL);
56563d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
56573d19cdaeSstevel 			pkt->pkt_statistics |= STAT_BUS_RESET;
56583d19cdaeSstevel 			break;
56593d19cdaeSstevel 
56603d19cdaeSstevel 		case FCAL_STATUS_MAX_XCHG_EXCEEDED:
56613d19cdaeSstevel 			sf_throttle(sf);
56623d19cdaeSstevel 			sf->sf_use_lock = TRUE;
56633d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
56643d19cdaeSstevel 			pkt->pkt_state = STATE_GOT_BUS;
56653d19cdaeSstevel 			pkt->pkt_statistics |= STAT_ABORTED;
56663d19cdaeSstevel 			break;
56673d19cdaeSstevel 
56683d19cdaeSstevel 		case FCAL_STATUS_TIMEOUT:
56693d19cdaeSstevel 			SF_DMSG1("Fibre Channel Timeout");
56703d19cdaeSstevel 			pkt->pkt_reason = CMD_TIMEOUT;
56713d19cdaeSstevel 			break;
56723d19cdaeSstevel 
56733d19cdaeSstevel 		case FCAL_STATUS_ERR_OVERRUN:
56743d19cdaeSstevel 			SF_DMSG1("CMD_DATA_OVR");
56753d19cdaeSstevel 			pkt->pkt_reason = CMD_DATA_OVR;
56763d19cdaeSstevel 			break;
56773d19cdaeSstevel 
56783d19cdaeSstevel 		case FCAL_STATUS_UNKNOWN_CQ_TYPE:
56793d19cdaeSstevel 			SF_DMSG1("Unknown CQ type");
56803d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
56813d19cdaeSstevel 			break;
56823d19cdaeSstevel 
56833d19cdaeSstevel 		case FCAL_STATUS_BAD_SEG_CNT:
56843d19cdaeSstevel 			SF_DMSG1("Bad SEG CNT");
56853d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
56863d19cdaeSstevel 			break;
56873d19cdaeSstevel 
56883d19cdaeSstevel 		case FCAL_STATUS_BAD_XID:
56893d19cdaeSstevel 			SF_DMSG1("Fibre Channel Invalid X_ID");
56903d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
56913d19cdaeSstevel 			break;
56923d19cdaeSstevel 
56933d19cdaeSstevel 		case FCAL_STATUS_XCHG_BUSY:
56943d19cdaeSstevel 			SF_DMSG1("Fibre Channel Exchange Busy");
56953d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
56963d19cdaeSstevel 			break;
56973d19cdaeSstevel 
56983d19cdaeSstevel 		case FCAL_STATUS_INSUFFICIENT_CQES:
56993d19cdaeSstevel 			SF_DMSG1("Insufficient CQEs");
57003d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
57013d19cdaeSstevel 			break;
57023d19cdaeSstevel 
57033d19cdaeSstevel 		case FCAL_STATUS_ALLOC_FAIL:
57043d19cdaeSstevel 			SF_DMSG1("ALLOC FAIL");
57053d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
57063d19cdaeSstevel 			break;
57073d19cdaeSstevel 
57083d19cdaeSstevel 		case FCAL_STATUS_BAD_SID:
57093d19cdaeSstevel 			SF_DMSG1("Fibre Channel Invalid S_ID");
57103d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
57113d19cdaeSstevel 			break;
57123d19cdaeSstevel 
57133d19cdaeSstevel 		case FCAL_STATUS_INCOMPLETE_DMA_ERR:
57143d19cdaeSstevel 			if (sf_core && (sf_core & SF_CORE_INCOMPLETE_DMA)) {
57153d19cdaeSstevel 				sf_token = (int *)(uintptr_t)
57163d19cdaeSstevel 				    fpkt->fcal_socal_request.\
57173d19cdaeSstevel 				    sr_soc_hdr.sh_request_token;
57183d19cdaeSstevel 				(void) soc_take_core(sf->sf_sochandle,
57193d19cdaeSstevel 				    sf->sf_socp);
57203d19cdaeSstevel 				sf_core = 0;
57213d19cdaeSstevel 			}
57223d19cdaeSstevel 			msg2 =
57233d19cdaeSstevel 			"INCOMPLETE DMA XFER due to bad SOC+ card, replace HBA";
57243d19cdaeSstevel 			pkt->pkt_reason = CMD_INCOMPLETE;
57253d19cdaeSstevel 			pkt->pkt_state = STATE_GOT_BUS;
57263d19cdaeSstevel 			pkt->pkt_statistics |= STAT_ABORTED;
57273d19cdaeSstevel 			break;
57283d19cdaeSstevel 
57293d19cdaeSstevel 		case FCAL_STATUS_CRC_ERR:
57303d19cdaeSstevel 			msg2 = "Fibre Channel CRC Error on frames";
57313d19cdaeSstevel 			pkt->pkt_reason = CMD_INCOMPLETE;
57323d19cdaeSstevel 			pkt->pkt_state = STATE_GOT_BUS;
57333d19cdaeSstevel 			pkt->pkt_statistics |= STAT_ABORTED;
57343d19cdaeSstevel 			break;
57353d19cdaeSstevel 
57363d19cdaeSstevel 		case FCAL_STATUS_NO_SEQ_INIT:
57373d19cdaeSstevel 			SF_DMSG1("Fibre Channel Seq Init Error");
57383d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
57393d19cdaeSstevel 			break;
57403d19cdaeSstevel 
57413d19cdaeSstevel 		case  FCAL_STATUS_OPEN_FAIL:
57423d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
57433d19cdaeSstevel 			SF_DMSG1("Fibre Channel Open Failure");
57443d19cdaeSstevel 			if ((target->sft_state & (SF_TARGET_BUSY |
57453d19cdaeSstevel 			    SF_TARGET_MARK | SF_TARGET_OFFLINE)) == 0) {
574619397407SSherry Moore 				sf_log(sf, CE_NOTE,
574719397407SSherry Moore 				    "!Open failure to target 0x%x "
57483d19cdaeSstevel 				    "forcing LIP\n",
57493d19cdaeSstevel 				    sf_alpa_to_switch[target->sft_al_pa]);
57503d19cdaeSstevel 				sf_force_lip(sf);
57513d19cdaeSstevel 			}
57523d19cdaeSstevel 			break;
57533d19cdaeSstevel 
57543d19cdaeSstevel 
57553d19cdaeSstevel 		case FCAL_STATUS_ONLINE_TIMEOUT:
57563d19cdaeSstevel 			SF_DMSG1("Fibre Channel Online Timeout");
57573d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
57583d19cdaeSstevel 			break;
57593d19cdaeSstevel 
57603d19cdaeSstevel 		default:
57613d19cdaeSstevel 			SF_DMSG1("Unknown FC Status");
57623d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
57633d19cdaeSstevel 			break;
57643d19cdaeSstevel 		}
57653d19cdaeSstevel 	}
57663d19cdaeSstevel 
57673d19cdaeSstevel #ifdef	DEBUG
57683d19cdaeSstevel 	/*
57693d19cdaeSstevel 	 * msg1 will be non-NULL if we've detected some sort of error
57703d19cdaeSstevel 	 */
57713d19cdaeSstevel 	if (msg1 != NULL && sfdebug >= 4) {
57723d19cdaeSstevel 		sf_log(sf, CE_WARN,
57733d19cdaeSstevel 		    "!Transport error on cmd=0x%p target=0x%x:  %s\n",
57743d19cdaeSstevel 		    (void *)fpkt, pkt->pkt_address.a_target, msg1);
57753d19cdaeSstevel 	}
57763d19cdaeSstevel #endif
57773d19cdaeSstevel 
57783d19cdaeSstevel 	if (msg2 != NULL) {
57793d19cdaeSstevel 		sf_log(sf, CE_WARN, "!Transport error on target=0x%x:  %s\n",
57803d19cdaeSstevel 		    pkt->pkt_address.a_target, msg2);
57813d19cdaeSstevel 	}
57823d19cdaeSstevel 
57833d19cdaeSstevel 	ncmds = fpkt->fcal_ncmds;
57843d19cdaeSstevel 	ASSERT(ncmds >= 0);
57853d19cdaeSstevel 	if (ncmds >= (sf->sf_throttle - SF_HI_CMD_DELTA)) {
57863d19cdaeSstevel #ifdef DEBUG
57873d19cdaeSstevel 		if (!sf->sf_use_lock) {
57883d19cdaeSstevel 			SF_DEBUG(4, (sf, CE_NOTE, "use lock flag on\n"));
57893d19cdaeSstevel 		}
57903d19cdaeSstevel #endif
57913d19cdaeSstevel 		sf->sf_use_lock = TRUE;
57923d19cdaeSstevel 	}
57933d19cdaeSstevel 
57943d19cdaeSstevel 	mutex_enter(&sf->sf_cmd_mutex);
57953d19cdaeSstevel 	sf->sf_ncmds = ncmds;
57963d19cdaeSstevel 	sf_throttle_start(sf);
57973d19cdaeSstevel 	mutex_exit(&sf->sf_cmd_mutex);
57983d19cdaeSstevel 
57993d19cdaeSstevel 	if (!msg1 && !msg2)
58003d19cdaeSstevel 		SF_DEBUG(6, (sf, CE_NOTE, "Completing pkt 0x%p\n",
58013d19cdaeSstevel 		    (void *)pkt));
58023d19cdaeSstevel 	if (pkt->pkt_comp != NULL) {
58033d19cdaeSstevel 		(*pkt->pkt_comp)(pkt);
58043d19cdaeSstevel 	}
58053d19cdaeSstevel }
58063d19cdaeSstevel 
58073d19cdaeSstevel #undef	SF_DMSG1
58083d19cdaeSstevel 
58093d19cdaeSstevel 
58103d19cdaeSstevel 
58113d19cdaeSstevel /*
58123d19cdaeSstevel  * start throttling for this instance
58133d19cdaeSstevel  */
58143d19cdaeSstevel static void
58153d19cdaeSstevel sf_throttle_start(struct sf *sf)
58163d19cdaeSstevel {
58173d19cdaeSstevel 	struct sf_pkt *cmd, *prev_cmd = NULL;
58183d19cdaeSstevel 	struct scsi_pkt *pkt;
58193d19cdaeSstevel 	struct sf_target *target;
58203d19cdaeSstevel 
58213d19cdaeSstevel 
58223d19cdaeSstevel 	ASSERT(mutex_owned(&sf->sf_cmd_mutex));
58233d19cdaeSstevel 
58243d19cdaeSstevel 	cmd = sf->sf_pkt_head;
58253d19cdaeSstevel 	while ((cmd != NULL) &&
58263d19cdaeSstevel 	    (sf->sf_state == SF_STATE_ONLINE) &&
58273d19cdaeSstevel 	    (sf->sf_ncmds < sf->sf_throttle)) {
58283d19cdaeSstevel 
58293d19cdaeSstevel 		pkt = CMD2PKT(cmd);
58303d19cdaeSstevel 
58313d19cdaeSstevel 		target = ADDR2TARGET(&pkt->pkt_address);
58323d19cdaeSstevel 		if (target->sft_state & SF_TARGET_BUSY) {
58333d19cdaeSstevel 			/* this command is busy -- go to next */
58343d19cdaeSstevel 			ASSERT(cmd->cmd_state != SF_STATE_ISSUED);
58353d19cdaeSstevel 			prev_cmd = cmd;
58363d19cdaeSstevel 			cmd = cmd->cmd_next;
58373d19cdaeSstevel 			continue;
58383d19cdaeSstevel 		}
58393d19cdaeSstevel 
58403d19cdaeSstevel 		ASSERT(cmd->cmd_state != SF_STATE_ISSUED);
58413d19cdaeSstevel 
58423d19cdaeSstevel 		/* this cmd not busy and not issued */
58433d19cdaeSstevel 
58443d19cdaeSstevel 		/* remove this packet from the queue */
58453d19cdaeSstevel 		if (sf->sf_pkt_head == cmd) {
58463d19cdaeSstevel 			/* this was the first packet */
58473d19cdaeSstevel 			sf->sf_pkt_head = cmd->cmd_next;
58483d19cdaeSstevel 		} else if (sf->sf_pkt_tail == cmd) {
58493d19cdaeSstevel 			/* this was the last packet */
58503d19cdaeSstevel 			sf->sf_pkt_tail = prev_cmd;
58513d19cdaeSstevel 			if (prev_cmd != NULL) {
58523d19cdaeSstevel 				prev_cmd->cmd_next = NULL;
58533d19cdaeSstevel 			}
58543d19cdaeSstevel 		} else {
58553d19cdaeSstevel 			/* some packet in the middle of the queue */
58563d19cdaeSstevel 			ASSERT(prev_cmd != NULL);
58573d19cdaeSstevel 			prev_cmd->cmd_next = cmd->cmd_next;
58583d19cdaeSstevel 		}
58593d19cdaeSstevel 		cmd->cmd_flags &= ~CFLAG_IN_QUEUE;
58603d19cdaeSstevel 
58613d19cdaeSstevel 		if (target->sft_state & SF_TARGET_OFFLINE) {
58623d19cdaeSstevel 			mutex_exit(&sf->sf_cmd_mutex);
58633d19cdaeSstevel 			pkt->pkt_reason = CMD_TRAN_ERR;
58643d19cdaeSstevel 			if (pkt->pkt_comp != NULL) {
58653d19cdaeSstevel 				(*pkt->pkt_comp)(cmd->cmd_pkt);
58663d19cdaeSstevel 			}
58673d19cdaeSstevel 		} else {
58683d19cdaeSstevel 			sf_fill_ids(sf, cmd, target);
58693d19cdaeSstevel 			if (sf_start_internal(sf, cmd) != TRAN_ACCEPT) {
58703d19cdaeSstevel 				pkt->pkt_reason = CMD_TRAN_ERR;
58713d19cdaeSstevel 				if (pkt->pkt_comp != NULL) {
58723d19cdaeSstevel 					(*pkt->pkt_comp)(cmd->cmd_pkt);
58733d19cdaeSstevel 				}
58743d19cdaeSstevel 			}
58753d19cdaeSstevel 		}
58763d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
58773d19cdaeSstevel 		cmd = sf->sf_pkt_head;
58783d19cdaeSstevel 		prev_cmd = NULL;
58793d19cdaeSstevel 	}
58803d19cdaeSstevel }
58813d19cdaeSstevel 
58823d19cdaeSstevel 
58833d19cdaeSstevel /*
58843d19cdaeSstevel  * called when the max exchange value is exceeded to throttle back commands
58853d19cdaeSstevel  */
58863d19cdaeSstevel static void
58873d19cdaeSstevel sf_throttle(struct sf *sf)
58883d19cdaeSstevel {
58893d19cdaeSstevel 	int cmdmax = sf->sf_sochandle->fcal_cmdmax;
58903d19cdaeSstevel 
58913d19cdaeSstevel 
58923d19cdaeSstevel 	mutex_enter(&sf->sf_cmd_mutex);
58933d19cdaeSstevel 
58943d19cdaeSstevel 	sf->sf_flag = TRUE;
58953d19cdaeSstevel 
58963d19cdaeSstevel 	if (sf->sf_ncmds > (cmdmax / 2)) {
58973d19cdaeSstevel 		sf->sf_throttle = cmdmax / 2;
58983d19cdaeSstevel 	} else {
58993d19cdaeSstevel 		if (sf->sf_ncmds > SF_DECR_DELTA) {
59003d19cdaeSstevel 			sf->sf_throttle = sf->sf_ncmds - SF_DECR_DELTA;
59013d19cdaeSstevel 		} else {
59023d19cdaeSstevel 			/*
59033d19cdaeSstevel 			 * This case is just a safeguard, should not really
59043d19cdaeSstevel 			 * happen(ncmds < SF_DECR_DELTA and MAX_EXCHG exceed
59053d19cdaeSstevel 			 */
59063d19cdaeSstevel 			sf->sf_throttle = SF_DECR_DELTA;
59073d19cdaeSstevel 		}
59083d19cdaeSstevel 	}
59093d19cdaeSstevel 	mutex_exit(&sf->sf_cmd_mutex);
59103d19cdaeSstevel 
59113d19cdaeSstevel 	sf = sf->sf_sibling;
59123d19cdaeSstevel 	if (sf != NULL) {
59133d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
59143d19cdaeSstevel 		sf->sf_flag = TRUE;
59153d19cdaeSstevel 		if (sf->sf_ncmds >= (cmdmax / 2)) {
59163d19cdaeSstevel 			sf->sf_throttle = cmdmax / 2;
59173d19cdaeSstevel 		} else {
59183d19cdaeSstevel 			if (sf->sf_ncmds > SF_DECR_DELTA) {
59193d19cdaeSstevel 				sf->sf_throttle = sf->sf_ncmds - SF_DECR_DELTA;
59203d19cdaeSstevel 			} else {
59213d19cdaeSstevel 				sf->sf_throttle = SF_DECR_DELTA;
59223d19cdaeSstevel 			}
59233d19cdaeSstevel 		}
59243d19cdaeSstevel 
59253d19cdaeSstevel 		mutex_exit(&sf->sf_cmd_mutex);
59263d19cdaeSstevel 	}
59273d19cdaeSstevel }
59283d19cdaeSstevel 
59293d19cdaeSstevel 
59303d19cdaeSstevel /*
59313d19cdaeSstevel  * sf watchdog routine, called for a timeout
59323d19cdaeSstevel  */
59333d19cdaeSstevel /*ARGSUSED*/
59343d19cdaeSstevel static void
59353d19cdaeSstevel sf_watch(void *arg)
59363d19cdaeSstevel {
59373d19cdaeSstevel 	struct sf *sf;
59383d19cdaeSstevel 	struct sf_els_hdr	*privp;
59393d19cdaeSstevel 	static int count = 0, pscan_count = 0;
59403d19cdaeSstevel 	int cmdmax, i, mescount = 0;
59413d19cdaeSstevel 	struct sf_target *target;
59423d19cdaeSstevel 
59433d19cdaeSstevel 
59443d19cdaeSstevel 	sf_watchdog_time += sf_watchdog_timeout;
59453d19cdaeSstevel 	count++;
59463d19cdaeSstevel 	pscan_count++;
59473d19cdaeSstevel 
59483d19cdaeSstevel 	mutex_enter(&sf_global_mutex);
59493d19cdaeSstevel 	sf_watch_running = 1;
59503d19cdaeSstevel 	for (sf = sf_head; sf != NULL; sf = sf->sf_next) {
59513d19cdaeSstevel 
59523d19cdaeSstevel 		mutex_exit(&sf_global_mutex);
59533d19cdaeSstevel 
59543d19cdaeSstevel 		/* disable throttling while we're suspended */
59553d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
59563d19cdaeSstevel 		if (sf->sf_state & SF_STATE_SUSPENDED) {
59573d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
59583d19cdaeSstevel 			SF_DEBUG(1, (sf, CE_CONT,
59593d19cdaeSstevel 			    "sf_watch, sf%d:throttle disabled "
59603d19cdaeSstevel 			    "due to DDI_SUSPEND\n",
59613d19cdaeSstevel 			    ddi_get_instance(sf->sf_dip)));
59623d19cdaeSstevel 			mutex_enter(&sf_global_mutex);
59633d19cdaeSstevel 			continue;
59643d19cdaeSstevel 		}
59653d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
59663d19cdaeSstevel 
59673d19cdaeSstevel 		cmdmax = sf->sf_sochandle->fcal_cmdmax;
59683d19cdaeSstevel 
59693d19cdaeSstevel 		if (sf->sf_take_core) {
59703d19cdaeSstevel 			(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
59713d19cdaeSstevel 		}
59723d19cdaeSstevel 
59733d19cdaeSstevel 		mutex_enter(&sf->sf_cmd_mutex);
59743d19cdaeSstevel 
59753d19cdaeSstevel 		if (!sf->sf_flag) {
59763d19cdaeSstevel 			if (sf->sf_throttle < (cmdmax / 2)) {
59773d19cdaeSstevel 				sf->sf_throttle = cmdmax / 2;
59783d19cdaeSstevel 			} else if ((sf->sf_throttle += SF_INCR_DELTA) >
59793d19cdaeSstevel 			    cmdmax) {
59803d19cdaeSstevel 				sf->sf_throttle = cmdmax;
59813d19cdaeSstevel 			}
59823d19cdaeSstevel 		} else {
59833d19cdaeSstevel 			sf->sf_flag = FALSE;
59843d19cdaeSstevel 		}
59853d19cdaeSstevel 
59863d19cdaeSstevel 		sf->sf_ncmds_exp_avg = (sf->sf_ncmds + sf->sf_ncmds_exp_avg)
59873d19cdaeSstevel 		    >> 2;
59883d19cdaeSstevel 		if ((sf->sf_ncmds <= (sf->sf_throttle - SF_LO_CMD_DELTA)) &&
59893d19cdaeSstevel 		    (sf->sf_pkt_head == NULL)) {
59903d19cdaeSstevel #ifdef DEBUG
59913d19cdaeSstevel 			if (sf->sf_use_lock) {
59923d19cdaeSstevel 				SF_DEBUG(4, (sf, CE_NOTE,
59933d19cdaeSstevel 				    "use lock flag off\n"));
59943d19cdaeSstevel 			}
59953d19cdaeSstevel #endif
59963d19cdaeSstevel 			sf->sf_use_lock = FALSE;
59973d19cdaeSstevel 		}
59983d19cdaeSstevel 
59993d19cdaeSstevel 		if (sf->sf_state == SF_STATE_ONLINE && sf->sf_pkt_head &&
60003d19cdaeSstevel 		    sf->sf_ncmds < sf->sf_throttle) {
60013d19cdaeSstevel 			sf_throttle_start(sf);
60023d19cdaeSstevel 		}
60033d19cdaeSstevel 
60043d19cdaeSstevel 		mutex_exit(&sf->sf_cmd_mutex);
60053d19cdaeSstevel 
60063d19cdaeSstevel 		if (pscan_count >= sf_pool_scan_cnt) {
60073d19cdaeSstevel 			if (sf->sf_ncmds_exp_avg < (sf->sf_cr_pool_cnt <<
60083d19cdaeSstevel 			    SF_LOG2_ELEMS_IN_POOL) - SF_FREE_CR_EPSILON) {
60093d19cdaeSstevel 				sf_crpool_free(sf);
60103d19cdaeSstevel 			}
60113d19cdaeSstevel 		}
60123d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
60133d19cdaeSstevel 
60143d19cdaeSstevel 		privp = sf->sf_els_list;
60153d19cdaeSstevel 		while (privp != NULL) {
60163d19cdaeSstevel 			if (privp->timeout < sf_watchdog_time) {
60173d19cdaeSstevel 				/* timeout this command */
60183d19cdaeSstevel 				privp = sf_els_timeout(sf, privp);
60193d19cdaeSstevel 			} else if ((privp->timeout == SF_INVALID_TIMEOUT) &&
60203d19cdaeSstevel 			    (privp->lip_cnt != sf->sf_lip_cnt)) {
60213d19cdaeSstevel 				if (privp->prev != NULL) {
60223d19cdaeSstevel 					privp->prev->next = privp->next;
60233d19cdaeSstevel 				}
60243d19cdaeSstevel 				if (sf->sf_els_list == privp) {
60253d19cdaeSstevel 					sf->sf_els_list = privp->next;
60263d19cdaeSstevel 				}
60273d19cdaeSstevel 				if (privp->next != NULL) {
60283d19cdaeSstevel 					privp->next->prev = privp->prev;
60293d19cdaeSstevel 				}
60303d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
60313d19cdaeSstevel 				sf_els_free(privp->fpkt);
60323d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
60333d19cdaeSstevel 				privp = sf->sf_els_list;
60343d19cdaeSstevel 			} else {
60353d19cdaeSstevel 				privp = privp->next;
60363d19cdaeSstevel 			}
60373d19cdaeSstevel 		}
60383d19cdaeSstevel 
60393d19cdaeSstevel 		if (sf->sf_online_timer && sf->sf_online_timer <
60403d19cdaeSstevel 		    sf_watchdog_time) {
60413d19cdaeSstevel 			for (i = 0; i < sf_max_targets; i++) {
60423d19cdaeSstevel 				target = sf->sf_targets[i];
60433d19cdaeSstevel 				if (target != NULL) {
60443d19cdaeSstevel 					if (!mescount && target->sft_state &
60453d19cdaeSstevel 					    SF_TARGET_BUSY) {
60463d19cdaeSstevel 						sf_log(sf, CE_WARN, "!Loop "
60473d19cdaeSstevel 						    "Unstable: Failed to bring "
60483d19cdaeSstevel 						    "Loop Online\n");
60493d19cdaeSstevel 						mescount = 1;
60503d19cdaeSstevel 					}
60513d19cdaeSstevel 					target->sft_state |= SF_TARGET_MARK;
60523d19cdaeSstevel 				}
60533d19cdaeSstevel 			}
60543d19cdaeSstevel 			sf_finish_init(sf, sf->sf_lip_cnt);
60553d19cdaeSstevel 			sf->sf_state = SF_STATE_INIT;
60563d19cdaeSstevel 			sf->sf_online_timer = 0;
60573d19cdaeSstevel 		}
60583d19cdaeSstevel 
60593d19cdaeSstevel 		if (sf->sf_state == SF_STATE_ONLINE) {
60603d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
60613d19cdaeSstevel 			if (count >= sf_pkt_scan_cnt) {
60623d19cdaeSstevel 				sf_check_targets(sf);
60633d19cdaeSstevel 			}
60643d19cdaeSstevel 		} else if ((sf->sf_state == SF_STATE_OFFLINE) &&
60653d19cdaeSstevel 		    (sf->sf_timer < sf_watchdog_time)) {
60663d19cdaeSstevel 			for (i = 0; i < sf_max_targets; i++) {
60673d19cdaeSstevel 				target = sf->sf_targets[i];
60683d19cdaeSstevel 				if ((target != NULL) &&
60693d19cdaeSstevel 				    (target->sft_state &
60703d19cdaeSstevel 				    SF_TARGET_BUSY)) {
60713d19cdaeSstevel 					sf_log(sf, CE_WARN,
60723d19cdaeSstevel 					    "!Offline Timeout\n");
60733d19cdaeSstevel 					if (sf_core && (sf_core &
60743d19cdaeSstevel 					    SF_CORE_OFFLINE_TIMEOUT)) {
60753d19cdaeSstevel 						(void) soc_take_core(
60763d19cdaeSstevel 						    sf->sf_sochandle,
60773d19cdaeSstevel 						    sf->sf_socp);
60783d19cdaeSstevel 						sf_core = 0;
60793d19cdaeSstevel 					}
60803d19cdaeSstevel 					break;
60813d19cdaeSstevel 				}
60823d19cdaeSstevel 			}
60833d19cdaeSstevel 			sf_finish_init(sf, sf->sf_lip_cnt);
60843d19cdaeSstevel 			sf->sf_state = SF_STATE_INIT;
60853d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
60863d19cdaeSstevel 		} else {
60873d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
60883d19cdaeSstevel 		}
60893d19cdaeSstevel 		mutex_enter(&sf_global_mutex);
60903d19cdaeSstevel 	}
60913d19cdaeSstevel 	mutex_exit(&sf_global_mutex);
60923d19cdaeSstevel 	if (count >= sf_pkt_scan_cnt) {
60933d19cdaeSstevel 		count = 0;
60943d19cdaeSstevel 	}
60953d19cdaeSstevel 	if (pscan_count >= sf_pool_scan_cnt) {
60963d19cdaeSstevel 		pscan_count = 0;
60973d19cdaeSstevel 	}
60983d19cdaeSstevel 
60993d19cdaeSstevel 	/* reset timeout */
61003d19cdaeSstevel 	sf_watchdog_id = timeout(sf_watch, (caddr_t)0, sf_watchdog_tick);
61013d19cdaeSstevel 
61023d19cdaeSstevel 	/* signal waiting thread */
61033d19cdaeSstevel 	mutex_enter(&sf_global_mutex);
61043d19cdaeSstevel 	sf_watch_running = 0;
61053d19cdaeSstevel 	cv_broadcast(&sf_watch_cv);
61063d19cdaeSstevel 	mutex_exit(&sf_global_mutex);
61073d19cdaeSstevel }
61083d19cdaeSstevel 
61093d19cdaeSstevel 
61103d19cdaeSstevel /*
61113d19cdaeSstevel  * called during a timeout to check targets
61123d19cdaeSstevel  */
61133d19cdaeSstevel static void
61143d19cdaeSstevel sf_check_targets(struct sf *sf)
61153d19cdaeSstevel {
61163d19cdaeSstevel 	struct sf_target *target;
61173d19cdaeSstevel 	int i;
61183d19cdaeSstevel 	struct sf_pkt *cmd;
61193d19cdaeSstevel 	struct scsi_pkt *pkt;
61203d19cdaeSstevel 	int lip_cnt;
61213d19cdaeSstevel 
61223d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
61233d19cdaeSstevel 	lip_cnt = sf->sf_lip_cnt;
61243d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
61253d19cdaeSstevel 
61263d19cdaeSstevel 	/* check scan all possible targets */
61273d19cdaeSstevel 	for (i = 0; i < sf_max_targets; i++) {
61283d19cdaeSstevel 		target = sf->sf_targets[i];
61293d19cdaeSstevel 		while (target != NULL) {
61303d19cdaeSstevel 			mutex_enter(&target->sft_pkt_mutex);
61313d19cdaeSstevel 			if (target->sft_alive && target->sft_scan_count !=
61323d19cdaeSstevel 			    sf_target_scan_cnt) {
61333d19cdaeSstevel 				target->sft_alive = 0;
61343d19cdaeSstevel 				target->sft_scan_count++;
61353d19cdaeSstevel 				mutex_exit(&target->sft_pkt_mutex);
61363d19cdaeSstevel 				return;
61373d19cdaeSstevel 			}
61383d19cdaeSstevel 			target->sft_alive = 0;
61393d19cdaeSstevel 			target->sft_scan_count = 0;
61403d19cdaeSstevel 			cmd = target->sft_pkt_head;
61413d19cdaeSstevel 			while (cmd != (struct sf_pkt *)&target->sft_pkt_head) {
61423d19cdaeSstevel 				mutex_enter(&cmd->cmd_abort_mutex);
61433d19cdaeSstevel 				if (cmd->cmd_state == SF_STATE_ISSUED &&
61443d19cdaeSstevel 				    ((cmd->cmd_timeout && sf_watchdog_time >
61453d19cdaeSstevel #ifdef	DEBUG
61463d19cdaeSstevel 				    cmd->cmd_timeout) || sf_abort_flag)) {
61473d19cdaeSstevel 					sf_abort_flag = 0;
61483d19cdaeSstevel #else
61493d19cdaeSstevel 					cmd->cmd_timeout))) {
61503d19cdaeSstevel #endif
61513d19cdaeSstevel 					cmd->cmd_timeout = 0;
61523d19cdaeSstevel 	/* prevent reset from getting at this packet */
61533d19cdaeSstevel 					cmd->cmd_state = SF_STATE_ABORTING;
61543d19cdaeSstevel 					mutex_exit(&cmd->cmd_abort_mutex);
61553d19cdaeSstevel 					mutex_exit(&target->sft_pkt_mutex);
61563d19cdaeSstevel 					sf->sf_stats.tstats[i].timeouts++;
61573d19cdaeSstevel 					if (sf_target_timeout(sf, cmd))
61583d19cdaeSstevel 						return;
61593d19cdaeSstevel 					else {
61603d19cdaeSstevel 						if (lip_cnt != sf->sf_lip_cnt) {
61613d19cdaeSstevel 							return;
61623d19cdaeSstevel 						} else {
61633d19cdaeSstevel 							mutex_enter(&target->
61643d19cdaeSstevel 							    sft_pkt_mutex);
61653d19cdaeSstevel 							cmd = target->
61663d19cdaeSstevel 							    sft_pkt_head;
61673d19cdaeSstevel 						}
61683d19cdaeSstevel 					}
61693d19cdaeSstevel 	/*
61703d19cdaeSstevel 	 * if the abort and lip fail, a reset will be carried out.
61713d19cdaeSstevel 	 * But the reset will ignore this packet. We have waited at least
61723d19cdaeSstevel 	 * 20 seconds after the initial timeout. Now, complete it here.
61733d19cdaeSstevel 	 * This also takes care of spurious bad aborts.
61743d19cdaeSstevel 	 */
61753d19cdaeSstevel 				} else if ((cmd->cmd_state ==
61763d19cdaeSstevel 				    SF_STATE_ABORTING) && (cmd->cmd_timeout
61773d19cdaeSstevel 				    <= sf_watchdog_time)) {
61783d19cdaeSstevel 					cmd->cmd_state = SF_STATE_IDLE;
61793d19cdaeSstevel 					mutex_exit(&cmd->cmd_abort_mutex);
61803d19cdaeSstevel 					mutex_exit(&target->sft_pkt_mutex);
618119397407SSherry Moore 					SF_DEBUG(1, (sf, CE_NOTE,
618219397407SSherry Moore 					    "Command 0x%p to sft 0x%p"
618319397407SSherry Moore 					    " delayed release\n",
61843d19cdaeSstevel 					    (void *)cmd, (void *)target));
61853d19cdaeSstevel 					pkt = cmd->cmd_pkt;
61863d19cdaeSstevel 					pkt->pkt_statistics |=
61873d19cdaeSstevel 					    (STAT_TIMEOUT|STAT_ABORTED);
61883d19cdaeSstevel 					pkt->pkt_reason = CMD_TIMEOUT;
61893d19cdaeSstevel 					if (pkt->pkt_comp) {
61909c57abc8Ssrivijitha dugganapalli 						scsi_hba_pkt_comp(pkt);
61913d19cdaeSstevel 					/* handle deferred_destroy case */
61923d19cdaeSstevel 					} else {
61933d19cdaeSstevel 						if ((cmd->cmd_block->fcp_cntl.
61943d19cdaeSstevel 						    cntl_reset == 1) ||
61953d19cdaeSstevel 						    (cmd->cmd_block->
619619397407SSherry Moore 						    fcp_cntl.cntl_abort_tsk ==
619719397407SSherry Moore 						    1)) {
619819397407SSherry Moore 							cmd->cmd_block->
619919397407SSherry Moore 							    fcp_cntl.
62003d19cdaeSstevel 							    cntl_reset = 0;
620119397407SSherry Moore 							cmd->cmd_block->
620219397407SSherry Moore 							    fcp_cntl.
62033d19cdaeSstevel 							    cntl_abort_tsk = 0;
620419397407SSherry Moore 							cmd->cmd_fp_pkt->
620519397407SSherry Moore 							    fcal_pkt_comp =
62063d19cdaeSstevel 							    sf_cmd_callback;
62073d19cdaeSstevel 							/* for cache */
62083d19cdaeSstevel 							sf_scsi_destroy_pkt
620919397407SSherry Moore 							    (&pkt->pkt_address,
621019397407SSherry Moore 							    pkt);
62113d19cdaeSstevel 						}
62123d19cdaeSstevel 					}
62133d19cdaeSstevel 					mutex_enter(&target->sft_pkt_mutex);
62143d19cdaeSstevel 					cmd = target->sft_pkt_head;
62153d19cdaeSstevel 				} else {
62163d19cdaeSstevel 					mutex_exit(&cmd->cmd_abort_mutex);
62173d19cdaeSstevel 					cmd = cmd->cmd_forw;
62183d19cdaeSstevel 				}
62193d19cdaeSstevel 			}
62203d19cdaeSstevel 			mutex_exit(&target->sft_pkt_mutex);
62213d19cdaeSstevel 			target = target->sft_next_lun;
62223d19cdaeSstevel 		}
62233d19cdaeSstevel 	}
62243d19cdaeSstevel }
62253d19cdaeSstevel 
62263d19cdaeSstevel 
62273d19cdaeSstevel /*
62283d19cdaeSstevel  * a command to a target has timed out
62293d19cdaeSstevel  * return TRUE iff cmd abort failed or timed out, else return FALSE
62303d19cdaeSstevel  */
62313d19cdaeSstevel static int
62323d19cdaeSstevel sf_target_timeout(struct sf *sf, struct sf_pkt *cmd)
62333d19cdaeSstevel {
62343d19cdaeSstevel 	int rval;
62353d19cdaeSstevel 	struct scsi_pkt *pkt;
62363d19cdaeSstevel 	struct fcal_packet *fpkt;
62373d19cdaeSstevel 	int tgt_id;
62383d19cdaeSstevel 	int retval = FALSE;
62393d19cdaeSstevel 
62403d19cdaeSstevel 
62413d19cdaeSstevel 	SF_DEBUG(1, (sf, CE_NOTE, "Command 0x%p to target %x timed out\n",
62423d19cdaeSstevel 	    (void *)cmd->cmd_fp_pkt, cmd->cmd_pkt->pkt_address.a_target));
62433d19cdaeSstevel 
6244602ca9eaScth 	fpkt = cmd->cmd_fp_pkt;
62453d19cdaeSstevel 
62463d19cdaeSstevel 	if (sf_core && (sf_core & SF_CORE_CMD_TIMEOUT)) {
62473d19cdaeSstevel 		sf_token = (int *)(uintptr_t)
62483d19cdaeSstevel 		    fpkt->fcal_socal_request.sr_soc_hdr.\
62493d19cdaeSstevel 		    sh_request_token;
62503d19cdaeSstevel 		(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
62513d19cdaeSstevel 		sf_core = 0;
62523d19cdaeSstevel 	}
62533d19cdaeSstevel 
62543d19cdaeSstevel 	/* call the transport to abort a command */
62553d19cdaeSstevel 	rval = soc_abort(sf->sf_sochandle, sf->sf_socp,
62563d19cdaeSstevel 	    sf->sf_sochandle->fcal_portno, fpkt, 1);
62573d19cdaeSstevel 
62583d19cdaeSstevel 	switch (rval) {
62593d19cdaeSstevel 	case FCAL_ABORTED:
62603d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_NOTE, "Command Abort succeeded\n"));
62613d19cdaeSstevel 		pkt = cmd->cmd_pkt;
62623d19cdaeSstevel 		cmd->cmd_state = SF_STATE_IDLE;
62633d19cdaeSstevel 		pkt->pkt_statistics |= (STAT_TIMEOUT|STAT_ABORTED);
62643d19cdaeSstevel 		pkt->pkt_reason = CMD_TIMEOUT;
62653d19cdaeSstevel 		if (pkt->pkt_comp != NULL) {
62663d19cdaeSstevel 			(*pkt->pkt_comp)(pkt);
62673d19cdaeSstevel 		}
62683d19cdaeSstevel 		break;				/* success */
62693d19cdaeSstevel 
62703d19cdaeSstevel 	case FCAL_ABORT_FAILED:
62713d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_NOTE, "Command Abort failed at target\n"));
62723d19cdaeSstevel 		pkt = cmd->cmd_pkt;
62733d19cdaeSstevel 		cmd->cmd_state = SF_STATE_IDLE;
62743d19cdaeSstevel 		pkt->pkt_reason = CMD_TIMEOUT;
62753d19cdaeSstevel 		pkt->pkt_statistics |= STAT_TIMEOUT;
62763d19cdaeSstevel 		tgt_id = pkt->pkt_address.a_target;
62773d19cdaeSstevel 		sf->sf_stats.tstats[tgt_id].abts_failures++;
62783d19cdaeSstevel 		if (pkt->pkt_comp != NULL) {
62793d19cdaeSstevel 			(*pkt->pkt_comp)(pkt);
62803d19cdaeSstevel 		}
62813d19cdaeSstevel 		break;
62823d19cdaeSstevel 
62833d19cdaeSstevel 	case FCAL_BAD_ABORT:
62843d19cdaeSstevel 		if (sf_core && (sf_core & SF_CORE_BAD_ABORT)) {
62853d19cdaeSstevel 			sf_token = (int *)(uintptr_t)fpkt->fcal_socal_request.\
62863d19cdaeSstevel 			    sr_soc_hdr.sh_request_token;
62873d19cdaeSstevel 			(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
62883d19cdaeSstevel 			sf_core = 0;
62893d19cdaeSstevel 		}
62903d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_NOTE, "Command Abort bad abort\n"));
62913d19cdaeSstevel 		cmd->cmd_timeout = sf_watchdog_time + cmd->cmd_pkt->pkt_time
62923d19cdaeSstevel 		    + 20;
62933d19cdaeSstevel 		break;
62943d19cdaeSstevel 
62953d19cdaeSstevel 	case FCAL_TIMEOUT:
62963d19cdaeSstevel 		retval = TRUE;
62973d19cdaeSstevel 		break;
62983d19cdaeSstevel 
62993d19cdaeSstevel 	default:
63003d19cdaeSstevel 		pkt = cmd->cmd_pkt;
63013d19cdaeSstevel 		tgt_id = pkt->pkt_address.a_target;
63023d19cdaeSstevel 		sf_log(sf, CE_WARN,
63033d19cdaeSstevel 		"Command Abort failed target 0x%x, forcing a LIP\n", tgt_id);
63043d19cdaeSstevel 		if (sf_core && (sf_core & SF_CORE_ABORT_TIMEOUT)) {
63053d19cdaeSstevel 			sf_token = (int *)(uintptr_t)fpkt->fcal_socal_request.\
63063d19cdaeSstevel 			    sr_soc_hdr.sh_request_token;
63073d19cdaeSstevel 			(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
63083d19cdaeSstevel 			sf_core = 0;
63093d19cdaeSstevel 		}
63103d19cdaeSstevel 		sf_force_lip(sf);
63113d19cdaeSstevel 		retval = TRUE;
63123d19cdaeSstevel 		break;
63133d19cdaeSstevel 	}
63143d19cdaeSstevel 
63153d19cdaeSstevel 	return (retval);
63163d19cdaeSstevel }
63173d19cdaeSstevel 
63183d19cdaeSstevel 
63193d19cdaeSstevel /*
63203d19cdaeSstevel  * an ELS command has timed out
63213d19cdaeSstevel  * return ???
63223d19cdaeSstevel  */
63233d19cdaeSstevel static struct sf_els_hdr *
63243d19cdaeSstevel sf_els_timeout(struct sf *sf, struct sf_els_hdr *privp)
63253d19cdaeSstevel {
63263d19cdaeSstevel 	struct fcal_packet *fpkt;
63273d19cdaeSstevel 	int rval, dflag, timeout = SF_ELS_TIMEOUT;
63283d19cdaeSstevel 	uint_t lip_cnt = privp->lip_cnt;
63293d19cdaeSstevel 	uchar_t els_code = privp->els_code;
63303d19cdaeSstevel 	struct sf_target *target = privp->target;
63313d19cdaeSstevel 	char what[64];
63323d19cdaeSstevel 
63333d19cdaeSstevel 	fpkt = privp->fpkt;
63343d19cdaeSstevel 	dflag = privp->delayed_retry;
63353d19cdaeSstevel 	/* use as temporary state variable */
63363d19cdaeSstevel 	privp->timeout = SF_INVALID_TIMEOUT;
63373d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
63383d19cdaeSstevel 
63393d19cdaeSstevel 	if (privp->fpkt->fcal_pkt_comp == sf_els_callback) {
63403d19cdaeSstevel 		/*
63413d19cdaeSstevel 		 * take socal core if required. Timeouts for IB and hosts
63423d19cdaeSstevel 		 * are not very interesting, so we take socal core only
63433d19cdaeSstevel 		 * if the timeout is *not* for a IB or host.
63443d19cdaeSstevel 		 */
63453d19cdaeSstevel 		if (sf_core && (sf_core & SF_CORE_ELS_TIMEOUT) &&
634619397407SSherry Moore 		    ((sf_alpa_to_switch[privp->dest_nport_id] &
634719397407SSherry Moore 		    0x0d) != 0x0d) && ((privp->dest_nport_id != 1) ||
634819397407SSherry Moore 		    (privp->dest_nport_id != 2) ||
634919397407SSherry Moore 		    (privp->dest_nport_id != 4) ||
635019397407SSherry Moore 		    (privp->dest_nport_id != 8) ||
63513d19cdaeSstevel 		    (privp->dest_nport_id != 0xf))) {
63523d19cdaeSstevel 			sf_token = (int *)(uintptr_t)fpkt->fcal_socal_request.\
63533d19cdaeSstevel 			    sr_soc_hdr.sh_request_token;
63543d19cdaeSstevel 			(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
63553d19cdaeSstevel 			sf_core = 0;
63563d19cdaeSstevel 		}
63573d19cdaeSstevel 		(void) sprintf(what, "ELS 0x%x", privp->els_code);
63583d19cdaeSstevel 	} else if (privp->fpkt->fcal_pkt_comp == sf_reportlun_callback) {
63593d19cdaeSstevel 		if (sf_core && (sf_core & SF_CORE_REPORTLUN_TIMEOUT)) {
63603d19cdaeSstevel 			sf_token = (int *)(uintptr_t)fpkt->fcal_socal_request.\
63613d19cdaeSstevel 			    sr_soc_hdr.sh_request_token;
63623d19cdaeSstevel 			(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
63633d19cdaeSstevel 			sf_core = 0;
63643d19cdaeSstevel 		}
63653d19cdaeSstevel 		timeout = SF_FCP_TIMEOUT;
63663d19cdaeSstevel 		(void) sprintf(what, "REPORT_LUNS");
63673d19cdaeSstevel 	} else if (privp->fpkt->fcal_pkt_comp == sf_inq_callback) {
63683d19cdaeSstevel 		if (sf_core && (sf_core & SF_CORE_INQUIRY_TIMEOUT)) {
63693d19cdaeSstevel 			sf_token = (int *)(uintptr_t)
63703d19cdaeSstevel 			    fpkt->fcal_socal_request.\
63713d19cdaeSstevel 			    sr_soc_hdr.sh_request_token;
63723d19cdaeSstevel 			(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
63733d19cdaeSstevel 			sf_core = 0;
63743d19cdaeSstevel 		}
63753d19cdaeSstevel 		timeout = SF_FCP_TIMEOUT;
63763d19cdaeSstevel 		(void) sprintf(what, "INQUIRY to LUN 0x%lx",
63773d19cdaeSstevel 		    (long)SCSA_LUN(target));
63783d19cdaeSstevel 	} else {
63793d19cdaeSstevel 		(void) sprintf(what, "UNKNOWN OPERATION");
63803d19cdaeSstevel 	}
63813d19cdaeSstevel 
63823d19cdaeSstevel 	if (dflag) {
63833d19cdaeSstevel 		/* delayed retry */
63843d19cdaeSstevel 		SF_DEBUG(2, (sf, CE_CONT,
63853d19cdaeSstevel 		    "!sf%d: %s to target %x delayed retry\n",
63863d19cdaeSstevel 		    ddi_get_instance(sf->sf_dip), what,
63873d19cdaeSstevel 		    sf_alpa_to_switch[privp->dest_nport_id]));
63883d19cdaeSstevel 		privp->delayed_retry = FALSE;
63893d19cdaeSstevel 		goto try_again;
63903d19cdaeSstevel 	}
63913d19cdaeSstevel 
63923d19cdaeSstevel 	sf_log(sf, CE_NOTE, "!%s to target 0x%x alpa 0x%x timed out\n",
63933d19cdaeSstevel 	    what, sf_alpa_to_switch[privp->dest_nport_id],
63943d19cdaeSstevel 	    privp->dest_nport_id);
63953d19cdaeSstevel 
63963d19cdaeSstevel 	rval = soc_abort(sf->sf_sochandle, sf->sf_socp, sf->sf_sochandle
63973d19cdaeSstevel 	    ->fcal_portno, fpkt, 1);
63983d19cdaeSstevel 	if (rval == FCAL_ABORTED || rval == FCAL_ABORT_FAILED) {
63993d19cdaeSstevel 	SF_DEBUG(1, (sf, CE_NOTE, "!%s abort to al_pa %x succeeded\n",
64003d19cdaeSstevel 	    what, privp->dest_nport_id));
64013d19cdaeSstevel try_again:
64023d19cdaeSstevel 
64033d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
64043d19cdaeSstevel 		if (privp->prev != NULL) {
64053d19cdaeSstevel 			privp->prev->next = privp->next;
64063d19cdaeSstevel 		}
64073d19cdaeSstevel 		if (sf->sf_els_list == privp) {
64083d19cdaeSstevel 			sf->sf_els_list = privp->next;
64093d19cdaeSstevel 		}
64103d19cdaeSstevel 		if (privp->next != NULL) {
64113d19cdaeSstevel 			privp->next->prev = privp->prev;
64123d19cdaeSstevel 		}
64133d19cdaeSstevel 		privp->prev = privp->next = NULL;
64143d19cdaeSstevel 		if (lip_cnt == sf->sf_lip_cnt) {
64153d19cdaeSstevel 			privp->timeout = sf_watchdog_time + timeout;
64163d19cdaeSstevel 			if ((++(privp->retries) < sf_els_retries) ||
64173d19cdaeSstevel 			    (dflag && (privp->retries < SF_BSY_RETRIES))) {
64183d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
64193d19cdaeSstevel 				sf_log(sf, CE_NOTE,
64203d19cdaeSstevel 				    "!%s to target 0x%x retrying\n",
64213d19cdaeSstevel 				    what,
64223d19cdaeSstevel 				    sf_alpa_to_switch[privp->dest_nport_id]);
64233d19cdaeSstevel 				if (sf_els_transport(sf, privp) == 1) {
64243d19cdaeSstevel 					mutex_enter(&sf->sf_mutex);
64253d19cdaeSstevel 					return (sf->sf_els_list); /* success */
64263d19cdaeSstevel 				}
64273d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
64283d19cdaeSstevel 				fpkt = NULL;
64293d19cdaeSstevel 			}
64303d19cdaeSstevel 			if ((lip_cnt == sf->sf_lip_cnt) &&
64313d19cdaeSstevel 			    (els_code != LA_ELS_LOGO)) {
64323d19cdaeSstevel 				if (target != NULL) {
64333d19cdaeSstevel 					sf_offline_target(sf, target);
64343d19cdaeSstevel 				}
64353d19cdaeSstevel 				if (sf->sf_lip_cnt == lip_cnt) {
64363d19cdaeSstevel 					sf->sf_device_count--;
64373d19cdaeSstevel 					ASSERT(sf->sf_device_count >= 0);
64383d19cdaeSstevel 					if (sf->sf_device_count == 0) {
64393d19cdaeSstevel 						sf_finish_init(sf,
64403d19cdaeSstevel 						    sf->sf_lip_cnt);
64413d19cdaeSstevel 					}
64423d19cdaeSstevel 				}
64433d19cdaeSstevel 			}
64443d19cdaeSstevel 			privp = sf->sf_els_list;
64453d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
64463d19cdaeSstevel 			if (fpkt != NULL) {
64473d19cdaeSstevel 				sf_els_free(fpkt);
64483d19cdaeSstevel 			}
64493d19cdaeSstevel 		} else {
64503d19cdaeSstevel 			mutex_exit(&sf->sf_mutex);
64513d19cdaeSstevel 			sf_els_free(privp->fpkt);
64523d19cdaeSstevel 			privp = NULL;
64533d19cdaeSstevel 		}
64543d19cdaeSstevel 	} else {
64553d19cdaeSstevel 		if (sf_core && (sf_core & SF_CORE_ELS_FAILED)) {
64563d19cdaeSstevel 			sf_token = (int *)(uintptr_t)
64573d19cdaeSstevel 			    fpkt->fcal_socal_request.\
64583d19cdaeSstevel 			    sr_soc_hdr.sh_request_token;
64593d19cdaeSstevel 			(void) soc_take_core(sf->sf_sochandle, sf->sf_socp);
64603d19cdaeSstevel 			sf_core = 0;
64613d19cdaeSstevel 		}
64623d19cdaeSstevel 		sf_log(sf, CE_NOTE, "%s abort to target 0x%x failed. "
64633d19cdaeSstevel 		    "status=0x%x, forcing LIP\n", what,
64643d19cdaeSstevel 		    sf_alpa_to_switch[privp->dest_nport_id], rval);
64653d19cdaeSstevel 		privp = NULL;
64663d19cdaeSstevel 		if (sf->sf_lip_cnt == lip_cnt) {
64673d19cdaeSstevel 			sf_force_lip(sf);
64683d19cdaeSstevel 		}
64693d19cdaeSstevel 	}
64703d19cdaeSstevel 
64713d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
64723d19cdaeSstevel 	return (privp);
64733d19cdaeSstevel }
64743d19cdaeSstevel 
64753d19cdaeSstevel 
64763d19cdaeSstevel /*
64773d19cdaeSstevel  * called by timeout when a reset times out
64783d19cdaeSstevel  */
64793d19cdaeSstevel /*ARGSUSED*/
64803d19cdaeSstevel static void
64813d19cdaeSstevel sf_check_reset_delay(void *arg)
64823d19cdaeSstevel {
64833d19cdaeSstevel 	struct sf *sf;
64843d19cdaeSstevel 	struct sf_target *target;
64853d19cdaeSstevel 	struct sf_reset_list *rp, *tp;
64863d19cdaeSstevel 	uint_t lip_cnt, reset_timeout_flag = FALSE;
64873d19cdaeSstevel 	clock_t lb;
64883d19cdaeSstevel 
64893d19cdaeSstevel 	lb = ddi_get_lbolt();
64903d19cdaeSstevel 
64913d19cdaeSstevel 	mutex_enter(&sf_global_mutex);
64923d19cdaeSstevel 
64933d19cdaeSstevel 	sf_reset_timeout_id = 0;
64943d19cdaeSstevel 
64953d19cdaeSstevel 	for (sf = sf_head; sf != NULL; sf = sf->sf_next) {
64963d19cdaeSstevel 
64973d19cdaeSstevel 		mutex_exit(&sf_global_mutex);
64983d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
64993d19cdaeSstevel 
65003d19cdaeSstevel 		/* is this type cast needed? */
65013d19cdaeSstevel 		tp = (struct sf_reset_list *)&sf->sf_reset_list;
65023d19cdaeSstevel 
65033d19cdaeSstevel 		rp = sf->sf_reset_list;
65043d19cdaeSstevel 		while (rp != NULL) {
65053d19cdaeSstevel 			if (((rp->timeout - lb) < 0) &&
65063d19cdaeSstevel 			    (rp->lip_cnt == sf->sf_lip_cnt)) {
65073d19cdaeSstevel 				tp->next = rp->next;
65083d19cdaeSstevel 				mutex_exit(&sf->sf_mutex);
65093d19cdaeSstevel 				target = rp->target;
65103d19cdaeSstevel 				lip_cnt = rp->lip_cnt;
65113d19cdaeSstevel 				kmem_free(rp, sizeof (struct sf_reset_list));
65123d19cdaeSstevel 				/* abort all cmds for this target */
65133d19cdaeSstevel 				while (target) {
65143d19cdaeSstevel 					sf_abort_all(sf, target, FALSE,
65153d19cdaeSstevel 					    lip_cnt, TRUE);
65163d19cdaeSstevel 					mutex_enter(&target->sft_mutex);
65173d19cdaeSstevel 					if (lip_cnt == sf->sf_lip_cnt) {
65183d19cdaeSstevel 						target->sft_state &=
65193d19cdaeSstevel 						    ~SF_TARGET_BUSY;
65203d19cdaeSstevel 					}
65213d19cdaeSstevel 					mutex_exit(&target->sft_mutex);
65223d19cdaeSstevel 					target = target->sft_next_lun;
65233d19cdaeSstevel 				}
65243d19cdaeSstevel 				mutex_enter(&sf->sf_mutex);
65253d19cdaeSstevel 				tp = (struct sf_reset_list *)
65263d19cdaeSstevel 				    &sf->sf_reset_list;
65273d19cdaeSstevel 				rp = sf->sf_reset_list;
65283d19cdaeSstevel 				lb = ddi_get_lbolt();
65293d19cdaeSstevel 			} else if (rp->lip_cnt != sf->sf_lip_cnt) {
65303d19cdaeSstevel 				tp->next = rp->next;
65313d19cdaeSstevel 				kmem_free(rp, sizeof (struct sf_reset_list));
65323d19cdaeSstevel 				rp = tp->next;
65333d19cdaeSstevel 			} else {
65343d19cdaeSstevel 				reset_timeout_flag = TRUE;
65353d19cdaeSstevel 				tp = rp;
65363d19cdaeSstevel 				rp = rp->next;
65373d19cdaeSstevel 			}
65383d19cdaeSstevel 		}
65393d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
65403d19cdaeSstevel 		mutex_enter(&sf_global_mutex);
65413d19cdaeSstevel 	}
65423d19cdaeSstevel 
65433d19cdaeSstevel 	if (reset_timeout_flag && (sf_reset_timeout_id == 0)) {
65443d19cdaeSstevel 		sf_reset_timeout_id = timeout(sf_check_reset_delay,
65453d19cdaeSstevel 		    NULL, drv_usectohz(SF_TARGET_RESET_DELAY));
65463d19cdaeSstevel 	}
65473d19cdaeSstevel 
65483d19cdaeSstevel 	mutex_exit(&sf_global_mutex);
65493d19cdaeSstevel }
65503d19cdaeSstevel 
65513d19cdaeSstevel 
65523d19cdaeSstevel /*
65533d19cdaeSstevel  * called to "reset the bus", i.e. force loop initialization (and address
65543d19cdaeSstevel  * re-negotiation)
65553d19cdaeSstevel  */
65563d19cdaeSstevel static void
65573d19cdaeSstevel sf_force_lip(struct sf *sf)
65583d19cdaeSstevel {
65593d19cdaeSstevel 	int i;
65603d19cdaeSstevel 	struct sf_target *target;
65613d19cdaeSstevel 
65623d19cdaeSstevel 
65633d19cdaeSstevel 	/* disable restart of lip if we're suspended */
65643d19cdaeSstevel 	mutex_enter(&sf->sf_mutex);
65653d19cdaeSstevel 	if (sf->sf_state & SF_STATE_SUSPENDED) {
65663d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
65673d19cdaeSstevel 		SF_DEBUG(1, (sf, CE_CONT,
65683d19cdaeSstevel 		    "sf_force_lip, sf%d: lip restart disabled "
65693d19cdaeSstevel 		    "due to DDI_SUSPEND\n",
65703d19cdaeSstevel 		    ddi_get_instance(sf->sf_dip)));
65713d19cdaeSstevel 		return;
65723d19cdaeSstevel 	}
65733d19cdaeSstevel 
65743d19cdaeSstevel 	sf_log(sf, CE_NOTE, "Forcing lip\n");
65753d19cdaeSstevel 
65763d19cdaeSstevel 	for (i = 0; i < sf_max_targets; i++) {
65773d19cdaeSstevel 		target = sf->sf_targets[i];
65783d19cdaeSstevel 		while (target != NULL) {
65793d19cdaeSstevel 			mutex_enter(&target->sft_mutex);
65803d19cdaeSstevel 			if (!(target->sft_state & SF_TARGET_OFFLINE))
65813d19cdaeSstevel 				target->sft_state |= SF_TARGET_BUSY;
65823d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
65833d19cdaeSstevel 			target = target->sft_next_lun;
65843d19cdaeSstevel 		}
65853d19cdaeSstevel 	}
65863d19cdaeSstevel 
65873d19cdaeSstevel 	sf->sf_lip_cnt++;
65883d19cdaeSstevel 	sf->sf_timer = sf_watchdog_time + SF_OFFLINE_TIMEOUT;
65893d19cdaeSstevel 	sf->sf_state = SF_STATE_OFFLINE;
65903d19cdaeSstevel 	mutex_exit(&sf->sf_mutex);
65913d19cdaeSstevel 	sf->sf_stats.lip_count++;		/* no mutex for this? */
65923d19cdaeSstevel 
65933d19cdaeSstevel #ifdef DEBUG
65943d19cdaeSstevel 	/* are we allowing LIPs ?? */
65953d19cdaeSstevel 	if (sf_lip_flag != 0) {
65963d19cdaeSstevel #endif
65973d19cdaeSstevel 		/* call the transport to force loop initialization */
65983d19cdaeSstevel 		if (((i = soc_force_lip(sf->sf_sochandle, sf->sf_socp,
65993d19cdaeSstevel 		    sf->sf_sochandle->fcal_portno, 1,
66003d19cdaeSstevel 		    FCAL_FORCE_LIP)) != FCAL_SUCCESS) &&
66013d19cdaeSstevel 		    (i != FCAL_TIMEOUT)) {
66023d19cdaeSstevel 			/* force LIP failed */
66033d19cdaeSstevel 			if (sf_core && (sf_core & SF_CORE_LIP_FAILED)) {
66043d19cdaeSstevel 				(void) soc_take_core(sf->sf_sochandle,
66053d19cdaeSstevel 				    sf->sf_socp);
66063d19cdaeSstevel 				sf_core = 0;
66073d19cdaeSstevel 			}
66083d19cdaeSstevel #ifdef DEBUG
66093d19cdaeSstevel 			/* are we allowing reset after LIP failed ?? */
66103d19cdaeSstevel 			if (sf_reset_flag != 0) {
66113d19cdaeSstevel #endif
66123d19cdaeSstevel 				/* restart socal after resetting it */
66133d19cdaeSstevel 				sf_log(sf, CE_NOTE,
661419397407SSherry Moore 				    "!Force lip failed Status code 0x%x."
661519397407SSherry Moore 				    " Reseting\n", i);
66163d19cdaeSstevel 				/* call transport to force a reset */
66173d19cdaeSstevel 				soc_force_reset(sf->sf_sochandle, sf->sf_socp,
66183d19cdaeSstevel 				    sf->sf_sochandle->fcal_portno, 1);
66193d19cdaeSstevel #ifdef	DEBUG
66203d19cdaeSstevel 			}
66213d19cdaeSstevel #endif
66223d19cdaeSstevel 		}
66233d19cdaeSstevel #ifdef	DEBUG
66243d19cdaeSstevel 	}
66253d19cdaeSstevel #endif
66263d19cdaeSstevel }
66273d19cdaeSstevel 
66283d19cdaeSstevel 
66293d19cdaeSstevel /*
66303d19cdaeSstevel  * called by the transport when an unsolicited ELS is received
66313d19cdaeSstevel  */
66323d19cdaeSstevel static void
66333d19cdaeSstevel sf_unsol_els_callback(void *arg, soc_response_t *srp, caddr_t payload)
66343d19cdaeSstevel {
66353d19cdaeSstevel 	struct sf *sf = (struct sf *)arg;
66363d19cdaeSstevel 	els_payload_t	*els = (els_payload_t *)payload;
66373d19cdaeSstevel 	struct la_els_rjt *rsp;
66383d19cdaeSstevel 	int	i, tgt_id;
66393d19cdaeSstevel 	uchar_t dest_id;
66403d19cdaeSstevel 	struct fcal_packet *fpkt;
66413d19cdaeSstevel 	fc_frame_header_t *hp;
66423d19cdaeSstevel 	struct sf_els_hdr *privp;
66433d19cdaeSstevel 
66443d19cdaeSstevel 
66453d19cdaeSstevel 	if ((els == NULL) || ((i = srp->sr_soc_hdr.sh_byte_cnt) == 0)) {
66463d19cdaeSstevel 		return;
66473d19cdaeSstevel 	}
66483d19cdaeSstevel 
66493d19cdaeSstevel 	if (i > SOC_CQE_PAYLOAD) {
66503d19cdaeSstevel 		i = SOC_CQE_PAYLOAD;
66513d19cdaeSstevel 	}
66523d19cdaeSstevel 
66533d19cdaeSstevel 	dest_id = (uchar_t)srp->sr_fc_frame_hdr.s_id;
66543d19cdaeSstevel 	tgt_id = sf_alpa_to_switch[dest_id];
66553d19cdaeSstevel 
66563d19cdaeSstevel 	switch (els->els_cmd.c.ls_command) {
66573d19cdaeSstevel 
66583d19cdaeSstevel 	case LA_ELS_LOGO:
66593d19cdaeSstevel 		/*
66603d19cdaeSstevel 		 * logout received -- log the fact
66613d19cdaeSstevel 		 */
66623d19cdaeSstevel 		sf->sf_stats.tstats[tgt_id].logouts_recvd++;
66633d19cdaeSstevel 		sf_log(sf, CE_NOTE, "!LOGO recvd from target %x, %s\n",
66643d19cdaeSstevel 		    tgt_id,
66653d19cdaeSstevel 		    sf_lip_on_plogo ? "Forcing LIP...." : "");
66663d19cdaeSstevel 		if (sf_lip_on_plogo) {
66673d19cdaeSstevel 			sf_force_lip(sf);
66683d19cdaeSstevel 		}
66693d19cdaeSstevel 		break;
66703d19cdaeSstevel 
66713d19cdaeSstevel 	default:  /* includes LA_ELS_PLOGI */
66723d19cdaeSstevel 		/*
66733d19cdaeSstevel 		 * something besides a logout received -- we don't handle
66743d19cdaeSstevel 		 * this so send back a reject saying its unsupported
66753d19cdaeSstevel 		 */
66763d19cdaeSstevel 
66773d19cdaeSstevel 		sf_log(sf, CE_NOTE, "!ELS 0x%x recvd from target 0x%x\n",
66783d19cdaeSstevel 		    els->els_cmd.c.ls_command, tgt_id);
66793d19cdaeSstevel 
66803d19cdaeSstevel 
66813d19cdaeSstevel 		/* allocate room for a response */
66823d19cdaeSstevel 		if (sf_els_alloc(sf, dest_id, sizeof (struct sf_els_hdr),
66833d19cdaeSstevel 		    sizeof (struct la_els_rjt), sizeof (union sf_els_rsp),
66843d19cdaeSstevel 		    (caddr_t *)&privp, (caddr_t *)&rsp) == NULL) {
66853d19cdaeSstevel 			break;
66863d19cdaeSstevel 		}
66873d19cdaeSstevel 
66883d19cdaeSstevel 		fpkt = privp->fpkt;
66893d19cdaeSstevel 
66903d19cdaeSstevel 		/* fill in pkt header */
66913d19cdaeSstevel 		hp = &fpkt->fcal_socal_request.sr_fc_frame_hdr;
66923d19cdaeSstevel 		hp->r_ctl = R_CTL_ELS_RSP;
66933d19cdaeSstevel 		hp->f_ctl = F_CTL_LAST_SEQ | F_CTL_XCHG_CONTEXT;
66943d19cdaeSstevel 		hp->ox_id = srp->sr_fc_frame_hdr.ox_id;
66953d19cdaeSstevel 		hp->rx_id = srp->sr_fc_frame_hdr.rx_id;
66963d19cdaeSstevel 		fpkt->fcal_socal_request.sr_cqhdr.cq_hdr_type =
66973d19cdaeSstevel 		    CQ_TYPE_OUTBOUND;
66983d19cdaeSstevel 
66993d19cdaeSstevel 		fpkt->fcal_socal_request.sr_soc_hdr.sh_seg_cnt = 1;
67003d19cdaeSstevel 
67013d19cdaeSstevel 		/* fill in response */
67023d19cdaeSstevel 		rsp->ls_code = LA_ELS_RJT;	/* reject this ELS */
67033d19cdaeSstevel 		rsp->mbz[0] = 0;
67043d19cdaeSstevel 		rsp->mbz[1] = 0;
67053d19cdaeSstevel 		rsp->mbz[2] = 0;
67063d19cdaeSstevel 		((struct la_els_logi *)privp->rsp)->ls_code = LA_ELS_ACC;
67073d19cdaeSstevel 		*((int *)&rsp->reserved) = 0;
67083d19cdaeSstevel 		rsp->reason_code = RJT_UNSUPPORTED;
67093d19cdaeSstevel 		privp->retries = sf_els_retries;
67103d19cdaeSstevel 		privp->els_code = LA_ELS_RJT;
67113d19cdaeSstevel 		privp->timeout = (unsigned)0xffffffff;
67123d19cdaeSstevel 		(void) sf_els_transport(sf, privp);
67133d19cdaeSstevel 		break;
67143d19cdaeSstevel 	}
67153d19cdaeSstevel }
67163d19cdaeSstevel 
67173d19cdaeSstevel 
67183d19cdaeSstevel /*
67193d19cdaeSstevel  * Error logging, printing, and debug print routines
67203d19cdaeSstevel  */
67213d19cdaeSstevel 
67223d19cdaeSstevel /*PRINTFLIKE3*/
67233d19cdaeSstevel static void
67243d19cdaeSstevel sf_log(struct sf *sf, int level, const char *fmt, ...)
67253d19cdaeSstevel {
67263d19cdaeSstevel 	char buf[256];
67273d19cdaeSstevel 	dev_info_t *dip;
67283d19cdaeSstevel 	va_list ap;
67293d19cdaeSstevel 
67303d19cdaeSstevel 	if (sf != NULL) {
67313d19cdaeSstevel 		dip = sf->sf_dip;
67323d19cdaeSstevel 	} else {
67333d19cdaeSstevel 		dip = NULL;
67343d19cdaeSstevel 	}
67353d19cdaeSstevel 
67363d19cdaeSstevel 	va_start(ap, fmt);
67373d19cdaeSstevel 	(void) vsprintf(buf, fmt, ap);
67383d19cdaeSstevel 	va_end(ap);
67393d19cdaeSstevel 	scsi_log(dip, "sf", level, buf);
67403d19cdaeSstevel }
67413d19cdaeSstevel 
67423d19cdaeSstevel 
67433d19cdaeSstevel /*
67443d19cdaeSstevel  * called to get some sf kstats -- return 0 on success else return errno
67453d19cdaeSstevel  */
67463d19cdaeSstevel static int
67473d19cdaeSstevel sf_kstat_update(kstat_t *ksp, int rw)
67483d19cdaeSstevel {
67493d19cdaeSstevel 	struct sf *sf;
67503d19cdaeSstevel 
67513d19cdaeSstevel 	if (rw == KSTAT_WRITE) {
67523d19cdaeSstevel 		/* can't write */
67533d19cdaeSstevel 		return (EACCES);
67543d19cdaeSstevel 	}
67553d19cdaeSstevel 
67563d19cdaeSstevel 	sf = ksp->ks_private;
67573d19cdaeSstevel 	sf->sf_stats.ncmds = sf->sf_ncmds;
67583d19cdaeSstevel 	sf->sf_stats.throttle_limit = sf->sf_throttle;
67593d19cdaeSstevel 	sf->sf_stats.cr_pool_size = sf->sf_cr_pool_cnt;
67603d19cdaeSstevel 
67613d19cdaeSstevel 	return (0);				/* success */
67623d19cdaeSstevel }
67633d19cdaeSstevel 
67643d19cdaeSstevel 
67653d19cdaeSstevel /*
67663d19cdaeSstevel  * Unix Entry Points
67673d19cdaeSstevel  */
67683d19cdaeSstevel 
67693d19cdaeSstevel /*
67703d19cdaeSstevel  * driver entry point for opens on control device
67713d19cdaeSstevel  */
67723d19cdaeSstevel /* ARGSUSED */
67733d19cdaeSstevel static int
67743d19cdaeSstevel sf_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
67753d19cdaeSstevel {
67763d19cdaeSstevel 	dev_t dev = *dev_p;
67773d19cdaeSstevel 	struct sf *sf;
67783d19cdaeSstevel 
67793d19cdaeSstevel 
67803d19cdaeSstevel 	/* just ensure soft state exists for this device */
67813d19cdaeSstevel 	sf = ddi_get_soft_state(sf_state, SF_MINOR2INST(getminor(dev)));
67823d19cdaeSstevel 	if (sf == NULL) {
67833d19cdaeSstevel 		return (ENXIO);
67843d19cdaeSstevel 	}
67853d19cdaeSstevel 
67863d19cdaeSstevel 	++(sf->sf_check_n_close);
67873d19cdaeSstevel 
67883d19cdaeSstevel 	return (0);
67893d19cdaeSstevel }
67903d19cdaeSstevel 
67913d19cdaeSstevel 
67923d19cdaeSstevel /*
67933d19cdaeSstevel  * driver entry point for last close on control device
67943d19cdaeSstevel  */
67953d19cdaeSstevel /* ARGSUSED */
67963d19cdaeSstevel static int
67973d19cdaeSstevel sf_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
67983d19cdaeSstevel {
67993d19cdaeSstevel 	struct sf *sf;
68003d19cdaeSstevel 
68013d19cdaeSstevel 	sf = ddi_get_soft_state(sf_state, SF_MINOR2INST(getminor(dev)));
68023d19cdaeSstevel 	if (sf == NULL) {
68033d19cdaeSstevel 		return (ENXIO);
68043d19cdaeSstevel 	}
68053d19cdaeSstevel 
68063d19cdaeSstevel 	if (!sf->sf_check_n_close) { /* if this flag is zero */
68073d19cdaeSstevel 		cmn_err(CE_WARN, "sf%d: trying to close unopened instance",
68083d19cdaeSstevel 		    SF_MINOR2INST(getminor(dev)));
68093d19cdaeSstevel 		return (ENODEV);
68103d19cdaeSstevel 	} else {
68113d19cdaeSstevel 		--(sf->sf_check_n_close);
68123d19cdaeSstevel 	}
68133d19cdaeSstevel 	return (0);
68143d19cdaeSstevel }
68153d19cdaeSstevel 
68163d19cdaeSstevel 
68173d19cdaeSstevel /*
68183d19cdaeSstevel  * driver entry point for sf ioctl commands
68193d19cdaeSstevel  */
68203d19cdaeSstevel /* ARGSUSED */
68213d19cdaeSstevel static int
68223d19cdaeSstevel sf_ioctl(dev_t dev,
68233d19cdaeSstevel     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
68243d19cdaeSstevel {
68253d19cdaeSstevel 	struct sf *sf;
68263d19cdaeSstevel 	struct sf_target *target;
68273d19cdaeSstevel 	uchar_t al_pa;
68283d19cdaeSstevel 	struct sf_al_map map;
68293d19cdaeSstevel 	int cnt, i;
68303d19cdaeSstevel 	int	retval;				/* return value */
68313d19cdaeSstevel 	struct devctl_iocdata *dcp;
68323d19cdaeSstevel 	dev_info_t *cdip;
68333d19cdaeSstevel 	struct scsi_address ap;
6834602ca9eaScth 	scsi_hba_tran_t *tran;
68353d19cdaeSstevel 
68363d19cdaeSstevel 
68373d19cdaeSstevel 	sf = ddi_get_soft_state(sf_state, SF_MINOR2INST(getminor(dev)));
68383d19cdaeSstevel 	if (sf == NULL) {
68393d19cdaeSstevel 		return (ENXIO);
68403d19cdaeSstevel 	}
68413d19cdaeSstevel 
68423d19cdaeSstevel 	/* handle all ioctls */
68433d19cdaeSstevel 	switch (cmd) {
68443d19cdaeSstevel 
68453d19cdaeSstevel 	/*
68463d19cdaeSstevel 	 * We can use the generic implementation for these ioctls
68473d19cdaeSstevel 	 */
68483d19cdaeSstevel 	case DEVCTL_DEVICE_GETSTATE:
68493d19cdaeSstevel 	case DEVCTL_DEVICE_ONLINE:
68503d19cdaeSstevel 	case DEVCTL_DEVICE_OFFLINE:
68513d19cdaeSstevel 	case DEVCTL_BUS_GETSTATE:
68523d19cdaeSstevel 		return (ndi_devctl_ioctl(sf->sf_dip, cmd, arg, mode, 0));
68533d19cdaeSstevel 
68543d19cdaeSstevel 	/*
68553d19cdaeSstevel 	 * return FC map
68563d19cdaeSstevel 	 */
68573d19cdaeSstevel 	case SFIOCGMAP:
68583d19cdaeSstevel 		if ((sf->sf_lilp_map->lilp_magic != FCAL_LILP_MAGIC &&
68593d19cdaeSstevel 		    sf->sf_lilp_map->lilp_magic != FCAL_BADLILP_MAGIC) ||
68603d19cdaeSstevel 		    sf->sf_state != SF_STATE_ONLINE) {
68613d19cdaeSstevel 			retval = ENOENT;
68623d19cdaeSstevel 			goto dun;
68633d19cdaeSstevel 		}
68643d19cdaeSstevel 		mutex_enter(&sf->sf_mutex);
68653d19cdaeSstevel 		if (sf->sf_lilp_map->lilp_magic == FCAL_BADLILP_MAGIC) {
68663d19cdaeSstevel 			int i, j = 0;
68673d19cdaeSstevel 
68683d19cdaeSstevel 			/* Need to generate a fake lilp map */
68693d19cdaeSstevel 			for (i = 0; i < sf_max_targets; i++) {
68703d19cdaeSstevel 				if (sf->sf_targets[i])
68713d19cdaeSstevel 					sf->sf_lilp_map->lilp_alpalist[j++] =
68723d19cdaeSstevel 					    sf->sf_targets[i]->
68733d19cdaeSstevel 					    sft_hard_address;
68743d19cdaeSstevel 			}
68753d19cdaeSstevel 			sf->sf_lilp_map->lilp_length = (uchar_t)j;
68763d19cdaeSstevel 		}
68773d19cdaeSstevel 		cnt = sf->sf_lilp_map->lilp_length;
68783d19cdaeSstevel 		map.sf_count = (short)cnt;
68793d19cdaeSstevel 		bcopy((caddr_t)&sf->sf_sochandle->fcal_n_wwn,
68803d19cdaeSstevel 		    (caddr_t)&map.sf_hba_addr.sf_node_wwn,
68813d19cdaeSstevel 		    sizeof (la_wwn_t));
68823d19cdaeSstevel 		bcopy((caddr_t)&sf->sf_sochandle->fcal_p_wwn,
68833d19cdaeSstevel 		    (caddr_t)&map.sf_hba_addr.sf_port_wwn,
68843d19cdaeSstevel 		    sizeof (la_wwn_t));
68853d19cdaeSstevel 		map.sf_hba_addr.sf_al_pa = sf->sf_al_pa;
68863d19cdaeSstevel 		map.sf_hba_addr.sf_hard_address = 0;
68873d19cdaeSstevel 		map.sf_hba_addr.sf_inq_dtype = DTYPE_UNKNOWN;
68883d19cdaeSstevel 		for (i = 0; i < cnt; i++) {
68893d19cdaeSstevel 			al_pa = sf->sf_lilp_map->lilp_alpalist[i];
68903d19cdaeSstevel 			map.sf_addr_pair[i].sf_al_pa = al_pa;
68913d19cdaeSstevel 			if (al_pa == sf->sf_al_pa) {
68923d19cdaeSstevel 				(void) bcopy((caddr_t)&sf->sf_sochandle
68933d19cdaeSstevel 				    ->fcal_n_wwn, (caddr_t)&map.
68943d19cdaeSstevel 				    sf_addr_pair[i].sf_node_wwn,
68953d19cdaeSstevel 				    sizeof (la_wwn_t));
68963d19cdaeSstevel 				(void) bcopy((caddr_t)&sf->sf_sochandle
68973d19cdaeSstevel 				    ->fcal_p_wwn, (caddr_t)&map.
68983d19cdaeSstevel 				    sf_addr_pair[i].sf_port_wwn,
68993d19cdaeSstevel 				    sizeof (la_wwn_t));
69003d19cdaeSstevel 				map.sf_addr_pair[i].sf_hard_address =
69013d19cdaeSstevel 				    al_pa;
69023d19cdaeSstevel 				map.sf_addr_pair[i].sf_inq_dtype =
69033d19cdaeSstevel 				    DTYPE_PROCESSOR;
69043d19cdaeSstevel 				continue;
69053d19cdaeSstevel 			}
69063d19cdaeSstevel 			target = sf->sf_targets[sf_alpa_to_switch[
69073d19cdaeSstevel 			    al_pa]];
69083d19cdaeSstevel 			if (target != NULL) {
69093d19cdaeSstevel 				mutex_enter(&target->sft_mutex);
69103d19cdaeSstevel 				if (!(target->sft_state &
69113d19cdaeSstevel 				    (SF_TARGET_OFFLINE |
69123d19cdaeSstevel 				    SF_TARGET_BUSY))) {
69133d19cdaeSstevel 					bcopy((caddr_t)&target->
69143d19cdaeSstevel 					    sft_node_wwn,
69153d19cdaeSstevel 					    (caddr_t)&map.sf_addr_pair
69163d19cdaeSstevel 					    [i].sf_node_wwn,
69173d19cdaeSstevel 					    sizeof (la_wwn_t));
69183d19cdaeSstevel 					bcopy((caddr_t)&target->
69193d19cdaeSstevel 					    sft_port_wwn,
69203d19cdaeSstevel 					    (caddr_t)&map.sf_addr_pair
69213d19cdaeSstevel 					    [i].sf_port_wwn,
69223d19cdaeSstevel 					    sizeof (la_wwn_t));
69233d19cdaeSstevel 					map.sf_addr_pair[i].
69243d19cdaeSstevel 					    sf_hard_address
69253d19cdaeSstevel 					    = target->sft_hard_address;
69263d19cdaeSstevel 					map.sf_addr_pair[i].
69273d19cdaeSstevel 					    sf_inq_dtype
69283d19cdaeSstevel 					    = target->sft_device_type;
69293d19cdaeSstevel 					mutex_exit(&target->sft_mutex);
69303d19cdaeSstevel 					continue;
69313d19cdaeSstevel 				}
69323d19cdaeSstevel 				mutex_exit(&target->sft_mutex);
69333d19cdaeSstevel 			}
69343d19cdaeSstevel 			bzero((caddr_t)&map.sf_addr_pair[i].
69353d19cdaeSstevel 			    sf_node_wwn, sizeof (la_wwn_t));
69363d19cdaeSstevel 			bzero((caddr_t)&map.sf_addr_pair[i].
69373d19cdaeSstevel 			    sf_port_wwn, sizeof (la_wwn_t));
69383d19cdaeSstevel 			map.sf_addr_pair[i].sf_inq_dtype =
69393d19cdaeSstevel 			    DTYPE_UNKNOWN;
69403d19cdaeSstevel 		}
69413d19cdaeSstevel 		mutex_exit(&sf->sf_mutex);
69423d19cdaeSstevel 		if (ddi_copyout((caddr_t)&map, (caddr_t)arg,
69433d19cdaeSstevel 		    sizeof (struct sf_al_map), mode) != 0) {
69443d19cdaeSstevel 			retval = EFAULT;
69453d19cdaeSstevel 			goto dun;
69463d19cdaeSstevel 		}
69473d19cdaeSstevel 		break;
69483d19cdaeSstevel 
69493d19cdaeSstevel 	/*
69503d19cdaeSstevel 	 * handle device control ioctls
69513d19cdaeSstevel 	 */
69523d19cdaeSstevel 	case DEVCTL_DEVICE_RESET:
69533d19cdaeSstevel 		if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) {
69543d19cdaeSstevel 			retval = EFAULT;
69553d19cdaeSstevel 			goto dun;
69563d19cdaeSstevel 		}
69573d19cdaeSstevel 		if ((ndi_dc_getname(dcp) == NULL) ||
69583d19cdaeSstevel 		    (ndi_dc_getaddr(dcp) == NULL)) {
69593d19cdaeSstevel 			ndi_dc_freehdl(dcp);
69603d19cdaeSstevel 			retval = EINVAL;
69613d19cdaeSstevel 			goto dun;
69623d19cdaeSstevel 		}
69633d19cdaeSstevel 		cdip = ndi_devi_find(sf->sf_dip,
69643d19cdaeSstevel 		    ndi_dc_getname(dcp), ndi_dc_getaddr(dcp));
69653d19cdaeSstevel 		ndi_dc_freehdl(dcp);
69663d19cdaeSstevel 
69673d19cdaeSstevel 		if (cdip == NULL) {
69683d19cdaeSstevel 			retval = ENXIO;
69693d19cdaeSstevel 			goto dun;
69703d19cdaeSstevel 		}
69713d19cdaeSstevel 
69723d19cdaeSstevel 		if ((target = sf_get_target_from_dip(sf, cdip)) == NULL) {
69733d19cdaeSstevel 			retval = ENXIO;
69743d19cdaeSstevel 			goto dun;
69753d19cdaeSstevel 		}
69763d19cdaeSstevel 		mutex_enter(&target->sft_mutex);
69773d19cdaeSstevel 		if (!(target->sft_state & SF_TARGET_INIT_DONE)) {
69783d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
69793d19cdaeSstevel 			retval = ENXIO;
69803d19cdaeSstevel 			goto dun;
69813d19cdaeSstevel 		}
6982602ca9eaScth 
6983602ca9eaScth 		/* This is ugly */
6984602ca9eaScth 		tran = kmem_zalloc(scsi_hba_tran_size(), KM_SLEEP);
6985602ca9eaScth 		bcopy(target->sft_tran, tran, scsi_hba_tran_size());
69863d19cdaeSstevel 		mutex_exit(&target->sft_mutex);
6987602ca9eaScth 		ap.a_hba_tran = tran;
69883d19cdaeSstevel 		ap.a_target = sf_alpa_to_switch[target->sft_al_pa];
69893d19cdaeSstevel 		if (sf_reset(&ap, RESET_TARGET) == FALSE) {
69903d19cdaeSstevel 			retval = EIO;
6991602ca9eaScth 		} else {
6992602ca9eaScth 			retval = 0;
69933d19cdaeSstevel 		}
6994602ca9eaScth 		kmem_free(tran, scsi_hba_tran_size());
6995602ca9eaScth 		goto dun;
69963d19cdaeSstevel 
69973d19cdaeSstevel 	case DEVCTL_BUS_QUIESCE:
69983d19cdaeSstevel 	case DEVCTL_BUS_UNQUIESCE:
69993d19cdaeSstevel 		retval = ENOTSUP;
70003d19cdaeSstevel 		goto dun;
70013d19cdaeSstevel 
70023d19cdaeSstevel 	case DEVCTL_BUS_RESET:
70033d19cdaeSstevel 	case DEVCTL_BUS_RESETALL:
70043d19cdaeSstevel 		sf_force_lip(sf);
70053d19cdaeSstevel 		break;
70063d19cdaeSstevel 
70073d19cdaeSstevel 	default:
70083d19cdaeSstevel 		retval = ENOTTY;
70093d19cdaeSstevel 		goto dun;
70103d19cdaeSstevel 	}
70113d19cdaeSstevel 
70123d19cdaeSstevel 	retval = 0;				/* success */
70133d19cdaeSstevel 
70143d19cdaeSstevel dun:
70153d19cdaeSstevel 	return (retval);
70163d19cdaeSstevel }
70173d19cdaeSstevel 
70183d19cdaeSstevel 
70193d19cdaeSstevel /*
70203d19cdaeSstevel  * get the target given a DIP
70213d19cdaeSstevel  */
70223d19cdaeSstevel static struct sf_target *
70233d19cdaeSstevel sf_get_target_from_dip(struct sf *sf, dev_info_t *dip)
70243d19cdaeSstevel {
70253d19cdaeSstevel 	int i;
70263d19cdaeSstevel 	struct sf_target *target;
70273d19cdaeSstevel 
70283d19cdaeSstevel 
70293d19cdaeSstevel 	/* scan each hash queue for the DIP in question */
70303d19cdaeSstevel 	for (i = 0; i < SF_NUM_HASH_QUEUES; i++) {
70313d19cdaeSstevel 		target = sf->sf_wwn_lists[i];
70323d19cdaeSstevel 		while (target != NULL) {
70333d19cdaeSstevel 			if (target->sft_dip == dip) {
70343d19cdaeSstevel 				return (target); /* success: target found */
70353d19cdaeSstevel 			}
70363d19cdaeSstevel 			target = target->sft_next;
70373d19cdaeSstevel 		}
70383d19cdaeSstevel 	}
70393d19cdaeSstevel 	return (NULL);				/* failure: target not found */
70403d19cdaeSstevel }
70413d19cdaeSstevel 
70423d19cdaeSstevel 
70433d19cdaeSstevel /*
70443d19cdaeSstevel  * called by the transport to get an event cookie
70453d19cdaeSstevel  */
70463d19cdaeSstevel static int
70473d19cdaeSstevel sf_bus_get_eventcookie(dev_info_t *dip, dev_info_t *rdip, char *name,
70483d19cdaeSstevel     ddi_eventcookie_t *event_cookiep)
70493d19cdaeSstevel {
70503d19cdaeSstevel 	struct sf *sf;
70513d19cdaeSstevel 
70523d19cdaeSstevel 	sf = ddi_get_soft_state(sf_state, ddi_get_instance(dip));
70533d19cdaeSstevel 	if (sf == NULL) {
70543d19cdaeSstevel 		/* can't find instance for this device */
70553d19cdaeSstevel 		return (DDI_FAILURE);
70563d19cdaeSstevel 	}
70573d19cdaeSstevel 
70583d19cdaeSstevel 	return (ndi_event_retrieve_cookie(sf->sf_event_hdl, rdip, name,
70593d19cdaeSstevel 	    event_cookiep, NDI_EVENT_NOPASS));
70603d19cdaeSstevel 
70613d19cdaeSstevel }
70623d19cdaeSstevel 
70633d19cdaeSstevel 
70643d19cdaeSstevel /*
70653d19cdaeSstevel  * called by the transport to add an event callback
70663d19cdaeSstevel  */
70673d19cdaeSstevel static int
70683d19cdaeSstevel sf_bus_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
70693d19cdaeSstevel     ddi_eventcookie_t eventid, void (*callback)(dev_info_t *dip,
70703d19cdaeSstevel     ddi_eventcookie_t event, void *arg, void *impl_data), void *arg,
70713d19cdaeSstevel     ddi_callback_id_t *cb_id)
70723d19cdaeSstevel {
70733d19cdaeSstevel 	struct sf *sf;
70743d19cdaeSstevel 
70753d19cdaeSstevel 	sf = ddi_get_soft_state(sf_state, ddi_get_instance(dip));
70763d19cdaeSstevel 	if (sf == NULL) {
70773d19cdaeSstevel 		/* can't find instance for this device */
70783d19cdaeSstevel 		return (DDI_FAILURE);
70793d19cdaeSstevel 	}
70803d19cdaeSstevel 
70813d19cdaeSstevel 	return (ndi_event_add_callback(sf->sf_event_hdl, rdip,
70823d19cdaeSstevel 	    eventid, callback, arg, NDI_SLEEP, cb_id));
70833d19cdaeSstevel 
70843d19cdaeSstevel }
70853d19cdaeSstevel 
70863d19cdaeSstevel 
70873d19cdaeSstevel /*
70883d19cdaeSstevel  * called by the transport to remove an event callback
70893d19cdaeSstevel  */
70903d19cdaeSstevel static int
70913d19cdaeSstevel sf_bus_remove_eventcall(dev_info_t *devi, ddi_callback_id_t cb_id)
70923d19cdaeSstevel {
70933d19cdaeSstevel 	struct sf *sf;
70943d19cdaeSstevel 
70953d19cdaeSstevel 	sf = ddi_get_soft_state(sf_state, ddi_get_instance(devi));
70963d19cdaeSstevel 	if (sf == NULL) {
70973d19cdaeSstevel 		/* can't find instance for this device */
70983d19cdaeSstevel 		return (DDI_FAILURE);
70993d19cdaeSstevel 	}
71003d19cdaeSstevel 
71013d19cdaeSstevel 	return (ndi_event_remove_callback(sf->sf_event_hdl, cb_id));
71023d19cdaeSstevel }
71033d19cdaeSstevel 
71043d19cdaeSstevel 
71053d19cdaeSstevel /*
71063d19cdaeSstevel  * called by the transport to post an event
71073d19cdaeSstevel  */
71083d19cdaeSstevel static int
71093d19cdaeSstevel sf_bus_post_event(dev_info_t *dip, dev_info_t *rdip,
71103d19cdaeSstevel     ddi_eventcookie_t eventid, void *impldata)
71113d19cdaeSstevel {
71123d19cdaeSstevel 	ddi_eventcookie_t remove_cookie, cookie;
71133d19cdaeSstevel 
71143d19cdaeSstevel 	/* is this a remove event ?? */
71153d19cdaeSstevel 	struct sf *sf = ddi_get_soft_state(sf_state, ddi_get_instance(dip));
71163d19cdaeSstevel 	remove_cookie = ndi_event_tag_to_cookie(sf->sf_event_hdl,
71173d19cdaeSstevel 	    SF_EVENT_TAG_REMOVE);
71183d19cdaeSstevel 
71193d19cdaeSstevel 	if (remove_cookie == eventid) {
71203d19cdaeSstevel 		struct sf_target *target;
71213d19cdaeSstevel 
71223d19cdaeSstevel 		/* handle remove event */
71233d19cdaeSstevel 
71243d19cdaeSstevel 		if (sf == NULL) {
71253d19cdaeSstevel 			/* no sf instance for this device */
71263d19cdaeSstevel 			return (NDI_FAILURE);
71273d19cdaeSstevel 		}
71283d19cdaeSstevel 
71293d19cdaeSstevel 		/* get the target for this event */
71303d19cdaeSstevel 		if ((target = sf_get_target_from_dip(sf, rdip)) != NULL) {
71313d19cdaeSstevel 			/*
71323d19cdaeSstevel 			 * clear device info for this target and mark as
71333d19cdaeSstevel 			 * not done
71343d19cdaeSstevel 			 */
71353d19cdaeSstevel 			mutex_enter(&target->sft_mutex);
71363d19cdaeSstevel 			target->sft_dip = NULL;
71373d19cdaeSstevel 			target->sft_state &= ~SF_TARGET_INIT_DONE;
71383d19cdaeSstevel 			mutex_exit(&target->sft_mutex);
71393d19cdaeSstevel 			return (NDI_SUCCESS); /* event handled */
71403d19cdaeSstevel 		}
71413d19cdaeSstevel 
71423d19cdaeSstevel 		/* no target for this event */
71433d19cdaeSstevel 		return (NDI_FAILURE);
71443d19cdaeSstevel 	}
71453d19cdaeSstevel 
71463d19cdaeSstevel 	/* an insertion event */
71473d19cdaeSstevel 	if (ndi_busop_get_eventcookie(dip, rdip, FCAL_INSERT_EVENT, &cookie)
71483d19cdaeSstevel 	    != NDI_SUCCESS) {
71493d19cdaeSstevel 		return (NDI_FAILURE);
71503d19cdaeSstevel 	}
71513d19cdaeSstevel 
71523d19cdaeSstevel 	return (ndi_post_event(dip, rdip, cookie, impldata));
71533d19cdaeSstevel }
71543d19cdaeSstevel 
71553d19cdaeSstevel 
71563d19cdaeSstevel /*
71573d19cdaeSstevel  * the sf hotplug daemon, one thread per sf instance
71583d19cdaeSstevel  */
71593d19cdaeSstevel static void
71603d19cdaeSstevel sf_hp_daemon(void *arg)
71613d19cdaeSstevel {
71623d19cdaeSstevel 	struct sf *sf = (struct sf *)arg;
71633d19cdaeSstevel 	struct sf_hp_elem *elem;
71643d19cdaeSstevel 	struct sf_target *target;
71653d19cdaeSstevel 	int tgt_id;
71663d19cdaeSstevel 	callb_cpr_t cprinfo;
71673d19cdaeSstevel 
71683d19cdaeSstevel 	CALLB_CPR_INIT(&cprinfo, &sf->sf_hp_daemon_mutex,
71693d19cdaeSstevel 	    callb_generic_cpr, "sf_hp_daemon");
71703d19cdaeSstevel 
71713d19cdaeSstevel 	mutex_enter(&sf->sf_hp_daemon_mutex);
71723d19cdaeSstevel 
71733d19cdaeSstevel 	do {
71743d19cdaeSstevel 		while (sf->sf_hp_elem_head != NULL) {
71753d19cdaeSstevel 
71763d19cdaeSstevel 			/* save ptr to head of list */
71773d19cdaeSstevel 			elem = sf->sf_hp_elem_head;
71783d19cdaeSstevel 
71793d19cdaeSstevel 			/* take element off of list */
71803d19cdaeSstevel 			if (sf->sf_hp_elem_head == sf->sf_hp_elem_tail) {
71813d19cdaeSstevel 				/* element only one in list -- list now empty */
71823d19cdaeSstevel 				sf->sf_hp_elem_head = NULL;
71833d19cdaeSstevel 				sf->sf_hp_elem_tail = NULL;
71843d19cdaeSstevel 			} else {
71853d19cdaeSstevel 				/* remove element from head of list */
71863d19cdaeSstevel 				sf->sf_hp_elem_head = sf->sf_hp_elem_head->next;
71873d19cdaeSstevel 			}
71883d19cdaeSstevel 
71893d19cdaeSstevel 			mutex_exit(&sf->sf_hp_daemon_mutex);
71903d19cdaeSstevel 
71913d19cdaeSstevel 			switch (elem->what) {
71923d19cdaeSstevel 			case SF_ONLINE:
71933d19cdaeSstevel 				/* online this target */
71943d19cdaeSstevel 				target = elem->target;
71953d19cdaeSstevel 				(void) ndi_devi_online(elem->dip, 0);
71963d19cdaeSstevel 				(void) ndi_event_retrieve_cookie(
71973d19cdaeSstevel 				    sf->sf_event_hdl,
71983d19cdaeSstevel 				    target->sft_dip, FCAL_INSERT_EVENT,
71993d19cdaeSstevel 				    &sf_insert_eid, NDI_EVENT_NOPASS);
72003d19cdaeSstevel 				(void) ndi_event_run_callbacks(sf->sf_event_hdl,
72013d19cdaeSstevel 				    target->sft_dip, sf_insert_eid, NULL);
72023d19cdaeSstevel 				break;
72033d19cdaeSstevel 			case SF_OFFLINE:
72043d19cdaeSstevel 				/* offline this target */
72053d19cdaeSstevel 				target = elem->target;
72063d19cdaeSstevel 				tgt_id = sf_alpa_to_switch[target->sft_al_pa];
72073d19cdaeSstevel 				/* don't do NDI_DEVI_REMOVE for now */
72083d19cdaeSstevel 				if (ndi_devi_offline(elem->dip, 0) !=
72093d19cdaeSstevel 				    NDI_SUCCESS) {
72103d19cdaeSstevel 					SF_DEBUG(1, (sf, CE_WARN, "target %x, "
72113d19cdaeSstevel 					    "device offline failed", tgt_id));
72123d19cdaeSstevel 				} else {
72133d19cdaeSstevel 					SF_DEBUG(1, (sf, CE_NOTE, "target %x, "
72143d19cdaeSstevel 					    "device offline succeeded\n",
72153d19cdaeSstevel 					    tgt_id));
72163d19cdaeSstevel 				}
72173d19cdaeSstevel 				break;
72183d19cdaeSstevel 			}
72193d19cdaeSstevel 			kmem_free(elem, sizeof (struct sf_hp_elem));
72203d19cdaeSstevel 			mutex_enter(&sf->sf_hp_daemon_mutex);
72213d19cdaeSstevel 		}
72223d19cdaeSstevel 
72233d19cdaeSstevel 		/* if exit is not already signaled */
72243d19cdaeSstevel 		if (sf->sf_hp_exit == 0) {
72253d19cdaeSstevel 			/* wait to be signaled by work or exit */
72263d19cdaeSstevel 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
72273d19cdaeSstevel 			cv_wait(&sf->sf_hp_daemon_cv, &sf->sf_hp_daemon_mutex);
72283d19cdaeSstevel 			CALLB_CPR_SAFE_END(&cprinfo, &sf->sf_hp_daemon_mutex);
72293d19cdaeSstevel 		}
72303d19cdaeSstevel 	} while (sf->sf_hp_exit == 0);
72313d19cdaeSstevel 
72323d19cdaeSstevel 	/* sf_hp_daemon_mutex is dropped by CALLB_CPR_EXIT */
72333d19cdaeSstevel 	CALLB_CPR_EXIT(&cprinfo);
72343d19cdaeSstevel 	thread_exit();			/* no more hotplug thread */
72353d19cdaeSstevel 	/* NOTREACHED */
72363d19cdaeSstevel }
7237