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