11e1ddd6cScth /*
21e1ddd6cScth  * CDDL HEADER START
31e1ddd6cScth  *
41e1ddd6cScth  * The contents of this file are subject to the terms of the
51e1ddd6cScth  * Common Development and Distribution License (the "License").
61e1ddd6cScth  * You may not use this file except in compliance with the License.
71e1ddd6cScth  *
81e1ddd6cScth  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91e1ddd6cScth  * or http://www.opensolaris.org/os/licensing.
101e1ddd6cScth  * See the License for the specific language governing permissions
111e1ddd6cScth  * and limitations under the License.
121e1ddd6cScth  *
131e1ddd6cScth  * When distributing Covered Code, include this CDDL HEADER in each
141e1ddd6cScth  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151e1ddd6cScth  * If applicable, add the following below this CDDL HEADER, with the
161e1ddd6cScth  * fields enclosed by brackets "[]" replaced with your own identifying
171e1ddd6cScth  * information: Portions Copyright [yyyy] [name of copyright owner]
181e1ddd6cScth  *
191e1ddd6cScth  * CDDL HEADER END
201e1ddd6cScth  */
211e1ddd6cScth /*
2240764c95SVictor Li  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
231e1ddd6cScth  */
24aa6577ebSJosef 'Jeff' Sipek /*
25aa6577ebSJosef 'Jeff' Sipek  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
2648bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
273fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
28*28c5054dSJason King  * Copyright 2024 RackTop Systems, Inc.
29aa6577ebSJosef 'Jeff' Sipek  */
301e1ddd6cScth 
311e1ddd6cScth /*
321e1ddd6cScth  * Multiplexed I/O SCSI vHCI implementation
331e1ddd6cScth  */
341e1ddd6cScth 
351e1ddd6cScth #include <sys/conf.h>
361e1ddd6cScth #include <sys/file.h>
371e1ddd6cScth #include <sys/ddi.h>
381e1ddd6cScth #include <sys/sunddi.h>
391e1ddd6cScth #include <sys/scsi/scsi.h>
401e1ddd6cScth #include <sys/scsi/impl/scsi_reset_notify.h>
410205780bSrralphs #include <sys/scsi/impl/services.h>
421e1ddd6cScth #include <sys/sunmdi.h>
431e1ddd6cScth #include <sys/mdi_impldefs.h>
441e1ddd6cScth #include <sys/scsi/adapters/scsi_vhci.h>
451e1ddd6cScth #include <sys/disp.h>
461e1ddd6cScth #include <sys/byteorder.h>
471e1ddd6cScth 
481e1ddd6cScth extern uintptr_t scsi_callback_id;
491e1ddd6cScth extern ddi_dma_attr_t scsi_alloc_attr;
501e1ddd6cScth 
511e1ddd6cScth #ifdef	DEBUG
521e1ddd6cScth int	vhci_debug = VHCI_DEBUG_DEFAULT_VAL;
531e1ddd6cScth #endif
541e1ddd6cScth 
551e1ddd6cScth /* retry for the vhci_do_prout command when a not ready is returned */
561e1ddd6cScth int vhci_prout_not_ready_retry = 180;
571e1ddd6cScth 
581e1ddd6cScth /*
591e1ddd6cScth  * These values are defined to support the internal retry of
601e1ddd6cScth  * SCSI packets for better sense code handling.
611e1ddd6cScth  */
621e1ddd6cScth #define	VHCI_CMD_CMPLT	0
631e1ddd6cScth #define	VHCI_CMD_RETRY	1
641e1ddd6cScth #define	VHCI_CMD_ERROR	-1
651e1ddd6cScth 
661e1ddd6cScth #define	PROPFLAGS (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM)
671e1ddd6cScth #define	VHCI_SCSI_PERR		0x47
681e1ddd6cScth #define	VHCI_PGR_ILLEGALOP	-2
691e1ddd6cScth #define	VHCI_NUM_UPDATE_TASKQ	8
707bc7346cScm136836 /* changed to 132 to accomodate HDS */
711e1ddd6cScth 
721e1ddd6cScth /*
731e1ddd6cScth  * Version Macros
741e1ddd6cScth  */
7519397407SSherry Moore #define	VHCI_NAME_VERSION	"SCSI VHCI Driver"
761e1ddd6cScth char		vhci_version_name[] = VHCI_NAME_VERSION;
771e1ddd6cScth 
781e1ddd6cScth int		vhci_first_time = 0;
791e1ddd6cScth clock_t		vhci_to_ticks = 0;
801e1ddd6cScth int		vhci_init_wait_timeout = VHCI_INIT_WAIT_TIMEOUT;
811e1ddd6cScth kcondvar_t	vhci_cv;
821e1ddd6cScth kmutex_t	vhci_global_mutex;
831e1ddd6cScth void		*vhci_softstate = NULL; /* for soft state */
841e1ddd6cScth 
851e1ddd6cScth /*
861e1ddd6cScth  * Flag to delay the retry of the reserve command
871e1ddd6cScth  */
881e1ddd6cScth int		vhci_reserve_delay = 100000;
891e1ddd6cScth static int	vhci_path_quiesce_timeout = 60;
901e1ddd6cScth static uchar_t	zero_key[MHIOC_RESV_KEY_SIZE];
911e1ddd6cScth 
921e1ddd6cScth /* uscsi delay for a TRAN_BUSY */
931e1ddd6cScth static int vhci_uscsi_delay = 100000;
941e1ddd6cScth static int vhci_uscsi_retry_count = 180;
951e1ddd6cScth /* uscsi_restart_sense timeout id in case it needs to get canceled */
961e1ddd6cScth static timeout_id_t vhci_restart_timeid = 0;
971e1ddd6cScth 
9853a7b6b6SChris Horne static int	vhci_bus_config_debug = 0;
9953a7b6b6SChris Horne 
1001e1ddd6cScth /*
1011e1ddd6cScth  * Bidirectional map of 'target-port' to port id <pid> for support of
102bbf21555SRichard Lowe  * iostat(8) '-Xx' and '-Yx' output.
1031e1ddd6cScth  */
1041e1ddd6cScth static kmutex_t		vhci_targetmap_mutex;
1051e1ddd6cScth static uint_t		vhci_targetmap_pid = 1;
1061e1ddd6cScth static mod_hash_t	*vhci_targetmap_bypid;	/* <pid> -> 'target-port' */
1071e1ddd6cScth static mod_hash_t	*vhci_targetmap_byport;	/* 'target-port' -> <pid> */
1081e1ddd6cScth 
1091e1ddd6cScth /*
1101e1ddd6cScth  * functions exported by scsi_vhci struct cb_ops
1111e1ddd6cScth  */
1121e1ddd6cScth static int vhci_open(dev_t *, int, int, cred_t *);
1131e1ddd6cScth static int vhci_close(dev_t, int, int, cred_t *);
1141e1ddd6cScth static int vhci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1151e1ddd6cScth 
1161e1ddd6cScth /*
1171e1ddd6cScth  * functions exported by scsi_vhci struct dev_ops
1181e1ddd6cScth  */
1191e1ddd6cScth static int vhci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1201e1ddd6cScth static int vhci_attach(dev_info_t *, ddi_attach_cmd_t);
1211e1ddd6cScth static int vhci_detach(dev_info_t *, ddi_detach_cmd_t);
1221e1ddd6cScth 
1231e1ddd6cScth /*
1241e1ddd6cScth  * functions exported by scsi_vhci scsi_hba_tran_t transport table
1251e1ddd6cScth  */
1261e1ddd6cScth static int vhci_scsi_tgt_init(dev_info_t *, dev_info_t *,
1271e1ddd6cScth     scsi_hba_tran_t *, struct scsi_device *);
1281e1ddd6cScth static void vhci_scsi_tgt_free(dev_info_t *, dev_info_t *, scsi_hba_tran_t *,
1291e1ddd6cScth     struct scsi_device *);
1301e1ddd6cScth static int vhci_pgr_register_start(scsi_vhci_lun_t *, struct scsi_pkt *);
1311e1ddd6cScth static int vhci_scsi_start(struct scsi_address *, struct scsi_pkt *);
1321e1ddd6cScth static int vhci_scsi_abort(struct scsi_address *, struct scsi_pkt *);
1331e1ddd6cScth static int vhci_scsi_reset(struct scsi_address *, int);
1341e1ddd6cScth static int vhci_scsi_reset_target(struct scsi_address *, int level,
1351e1ddd6cScth     uint8_t select_path);
1361e1ddd6cScth static int vhci_scsi_reset_bus(struct scsi_address *);
1371e1ddd6cScth static int vhci_scsi_getcap(struct scsi_address *, char *, int);
1381e1ddd6cScth static int vhci_scsi_setcap(struct scsi_address *, char *, int, int);
1391e1ddd6cScth static int vhci_commoncap(struct scsi_address *, char *, int, int, int);
1401e1ddd6cScth static int vhci_pHCI_cap(struct scsi_address *ap, char *cap, int val, int whom,
1411e1ddd6cScth     mdi_pathinfo_t *pip);
1421e1ddd6cScth static struct scsi_pkt *vhci_scsi_init_pkt(struct scsi_address *,
1431e1ddd6cScth     struct scsi_pkt *, struct buf *, int, int, int, int, int (*)(), caddr_t);
1441e1ddd6cScth static void vhci_scsi_destroy_pkt(struct scsi_address *, struct scsi_pkt *);
1451e1ddd6cScth static void vhci_scsi_dmafree(struct scsi_address *, struct scsi_pkt *);
1461e1ddd6cScth static void vhci_scsi_sync_pkt(struct scsi_address *, struct scsi_pkt *);
1471e1ddd6cScth static int vhci_scsi_reset_notify(struct scsi_address *, int, void (*)(caddr_t),
1481e1ddd6cScth     caddr_t);
1491e1ddd6cScth static int vhci_scsi_get_bus_addr(struct scsi_device *, char *, int);
1501e1ddd6cScth static int vhci_scsi_get_name(struct scsi_device *, char *, int);
1511e1ddd6cScth static int vhci_scsi_bus_power(dev_info_t *, void *, pm_bus_power_op_t,
1521e1ddd6cScth     void *, void *);
1531e1ddd6cScth static int vhci_scsi_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
1541e1ddd6cScth     void *, dev_info_t **);
15553a7b6b6SChris Horne static int vhci_scsi_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
15653a7b6b6SChris Horne     void *);
15755e592a2SRandall Ralphs static struct scsi_failover_ops *vhci_dev_fo(dev_info_t *, struct scsi_device *,
15855e592a2SRandall Ralphs     void **, char **);
1591e1ddd6cScth 
1601e1ddd6cScth /*
1611e1ddd6cScth  * functions registered with the mpxio framework via mdi_vhci_ops_t
1621e1ddd6cScth  */
1631e1ddd6cScth static int vhci_pathinfo_init(dev_info_t *, mdi_pathinfo_t *, int);
1641e1ddd6cScth static int vhci_pathinfo_uninit(dev_info_t *, mdi_pathinfo_t *, int);
1651e1ddd6cScth static int vhci_pathinfo_state_change(dev_info_t *, mdi_pathinfo_t *,
1661e1ddd6cScth     mdi_pathinfo_state_t, uint32_t, int);
1671e1ddd6cScth static int vhci_pathinfo_online(dev_info_t *, mdi_pathinfo_t *, int);
1681e1ddd6cScth static int vhci_pathinfo_offline(dev_info_t *, mdi_pathinfo_t *, int);
1691e1ddd6cScth static int vhci_failover(dev_info_t *, dev_info_t *, int);
1701e1ddd6cScth static void vhci_client_attached(dev_info_t *);
17155e592a2SRandall Ralphs static int vhci_is_dev_supported(dev_info_t *, dev_info_t *, void *);
1721e1ddd6cScth 
1731e1ddd6cScth static int vhci_ctl(dev_t, int, intptr_t, int, cred_t *, int *);
1741e1ddd6cScth static int vhci_devctl(dev_t, int, intptr_t, int, cred_t *, int *);
1751e1ddd6cScth static int vhci_ioc_get_phci_path(sv_iocdata_t *, caddr_t, int, caddr_t);
1761e1ddd6cScth static int vhci_ioc_get_client_path(sv_iocdata_t *, caddr_t, int, caddr_t);
1771e1ddd6cScth static int vhci_ioc_get_paddr(sv_iocdata_t *, caddr_t, int, caddr_t);
1781e1ddd6cScth static int vhci_ioc_send_client_path(caddr_t, sv_iocdata_t *, int, caddr_t);
1791e1ddd6cScth static void vhci_ioc_devi_to_path(dev_info_t *, caddr_t);
1801e1ddd6cScth static int vhci_get_phci_path_list(dev_info_t *, sv_path_info_t *, uint_t);
1811e1ddd6cScth static int vhci_get_client_path_list(dev_info_t *, sv_path_info_t *, uint_t);
1821e1ddd6cScth static int vhci_get_iocdata(const void *, sv_iocdata_t *, int, caddr_t);
1831e1ddd6cScth static int vhci_get_iocswitchdata(const void *, sv_switch_to_cntlr_iocdata_t *,
1841e1ddd6cScth     int, caddr_t);
1851e1ddd6cScth static int vhci_ioc_alloc_pathinfo(sv_path_info_t **, sv_path_info_t **,
1861e1ddd6cScth     uint_t, sv_iocdata_t *, int, caddr_t);
1871e1ddd6cScth static void vhci_ioc_free_pathinfo(sv_path_info_t *, sv_path_info_t *, uint_t);
1881e1ddd6cScth static int vhci_ioc_send_pathinfo(sv_path_info_t *, sv_path_info_t *, uint_t,
1891e1ddd6cScth     sv_iocdata_t *, int, caddr_t);
1901e1ddd6cScth static int vhci_handle_ext_fo(struct scsi_pkt *, int);
1911e1ddd6cScth static int vhci_efo_watch_cb(caddr_t, struct scsi_watch_result *);
1921e1ddd6cScth static int vhci_quiesce_lun(struct scsi_vhci_lun *);
1931e1ddd6cScth static int vhci_pgr_validate_and_register(scsi_vhci_priv_t *);
1941e1ddd6cScth static void vhci_dispatch_scsi_start(void *);
1951e1ddd6cScth static void vhci_efo_done(void *);
1961e1ddd6cScth static void vhci_initiate_auto_failback(void *);
1971e1ddd6cScth static void vhci_update_pHCI_pkt(struct vhci_pkt *, struct scsi_pkt *);
1981e1ddd6cScth static int vhci_update_pathinfo(struct scsi_device *, mdi_pathinfo_t *,
1991e1ddd6cScth     struct scsi_failover_ops *, scsi_vhci_lun_t *, struct scsi_vhci *);
2001e1ddd6cScth static void vhci_kstat_create_pathinfo(mdi_pathinfo_t *);
2011e1ddd6cScth static int vhci_quiesce_paths(dev_info_t *, dev_info_t *,
2021e1ddd6cScth     scsi_vhci_lun_t *, char *, char *);
2031e1ddd6cScth 
2041e1ddd6cScth static char *vhci_devnm_to_guid(char *);
2051e1ddd6cScth static int vhci_bind_transport(struct scsi_address *, struct vhci_pkt *,
2061e1ddd6cScth     int, int (*func)(caddr_t));
2071e1ddd6cScth static void vhci_intr(struct scsi_pkt *);
2081e1ddd6cScth static int vhci_do_prout(scsi_vhci_priv_t *);
2091e1ddd6cScth static void vhci_run_cmd(void *);
2103f51917eSrralphs static int vhci_do_prin(struct vhci_pkt **);
2111e1ddd6cScth static struct scsi_pkt *vhci_create_retry_pkt(struct vhci_pkt *);
2121e1ddd6cScth static struct vhci_pkt *vhci_sync_retry_pkt(struct vhci_pkt *);
2131e1ddd6cScth static struct scsi_vhci_lun *vhci_lun_lookup(dev_info_t *);
2141e1ddd6cScth static struct scsi_vhci_lun *vhci_lun_lookup_alloc(dev_info_t *, char *, int *);
21529c56acbSRaghuram Prahlada static void vhci_lun_free(struct scsi_vhci_lun *dvlp, struct scsi_device *sd);
2161e1ddd6cScth static int vhci_recovery_reset(scsi_vhci_lun_t *, struct scsi_address *,
2171e1ddd6cScth     uint8_t, uint8_t);
2181e1ddd6cScth void vhci_update_pathstates(void *);
2191e1ddd6cScth 
2201e1ddd6cScth #ifdef DEBUG
2211e1ddd6cScth static void vhci_print_prin_keys(vhci_prin_readkeys_t *, int);
2220205780bSrralphs static void vhci_print_cdb(dev_info_t *dip, uint_t level,
2230205780bSrralphs     char *title, uchar_t *cdb);
2240205780bSrralphs static void vhci_clean_print(dev_info_t *dev, uint_t level,
2250205780bSrralphs     char *title, uchar_t *data, int len);
22682b70b7cSrralphs #endif
2271e1ddd6cScth static void vhci_print_prout_keys(scsi_vhci_lun_t *, char *);
2281e1ddd6cScth static void vhci_uscsi_iodone(struct scsi_pkt *pkt);
2294c06356bSdh142964 static void vhci_invalidate_mpapi_lu(struct scsi_vhci *, scsi_vhci_lun_t *);
2301e1ddd6cScth 
2311e1ddd6cScth /*
2321e1ddd6cScth  * MP-API related functions
2331e1ddd6cScth  */
2341e1ddd6cScth extern int vhci_mpapi_init(struct scsi_vhci *);
2351e1ddd6cScth extern void vhci_mpapi_add_dev_prod(struct scsi_vhci *, char *);
2361e1ddd6cScth extern int vhci_mpapi_ctl(dev_t, int, intptr_t, int, cred_t *, int *);
2371e1ddd6cScth extern void vhci_update_mpapi_data(struct scsi_vhci *,
2381e1ddd6cScth     scsi_vhci_lun_t *, mdi_pathinfo_t *);
2391e1ddd6cScth extern void* vhci_get_mpapi_item(struct scsi_vhci *, mpapi_list_header_t *,
2401e1ddd6cScth     uint8_t, void*);
2411e1ddd6cScth extern void vhci_mpapi_set_path_state(dev_info_t *, mdi_pathinfo_t *, int);
2421e1ddd6cScth extern int vhci_mpapi_update_tpg_acc_state_for_lu(struct scsi_vhci *,
2431e1ddd6cScth     scsi_vhci_lun_t *);
2441e1ddd6cScth 
2450205780bSrralphs #define	VHCI_DMA_MAX_XFER_CAP	INT_MAX
2461e1ddd6cScth 
2471e1ddd6cScth #define	VHCI_MAX_PGR_RETRIES	3
2481e1ddd6cScth 
2491e1ddd6cScth /*
2501e1ddd6cScth  * Macros for the device-type mpxio options
2511e1ddd6cScth  */
2521e1ddd6cScth #define	LOAD_BALANCE_OPTIONS		"load-balance-options"
2531e1ddd6cScth #define	LOGICAL_BLOCK_REGION_SIZE	"region-size"
2541e1ddd6cScth #define	MPXIO_OPTIONS_LIST		"device-type-mpxio-options-list"
2551e1ddd6cScth #define	DEVICE_TYPE_STR			"device-type"
2561e1ddd6cScth #define	isdigit(ch)			((ch) >= '0' && (ch) <= '9')
2571e1ddd6cScth 
2581e1ddd6cScth static struct cb_ops vhci_cb_ops = {
2591e1ddd6cScth 	vhci_open,			/* open */
2601e1ddd6cScth 	vhci_close,			/* close */
2611e1ddd6cScth 	nodev,				/* strategy */
2621e1ddd6cScth 	nodev,				/* print */
2631e1ddd6cScth 	nodev,				/* dump */
2641e1ddd6cScth 	nodev,				/* read */
2651e1ddd6cScth 	nodev,				/* write */
2661e1ddd6cScth 	vhci_ioctl,			/* ioctl */
2671e1ddd6cScth 	nodev,				/* devmap */
2681e1ddd6cScth 	nodev,				/* mmap */
2691e1ddd6cScth 	nodev,				/* segmap */
2701e1ddd6cScth 	nochpoll,			/* chpoll */
2711e1ddd6cScth 	ddi_prop_op,			/* cb_prop_op */
2721e1ddd6cScth 	0,				/* streamtab */
2731e1ddd6cScth 	D_NEW | D_MP,			/* cb_flag */
2741e1ddd6cScth 	CB_REV,				/* rev */
2751e1ddd6cScth 	nodev,				/* aread */
2761e1ddd6cScth 	nodev				/* awrite */
2771e1ddd6cScth };
2781e1ddd6cScth 
2791e1ddd6cScth static struct dev_ops vhci_ops = {
2801e1ddd6cScth 	DEVO_REV,
2811e1ddd6cScth 	0,
2821e1ddd6cScth 	vhci_getinfo,
2831e1ddd6cScth 	nulldev,		/* identify */
2841e1ddd6cScth 	nulldev,		/* probe */
2851e1ddd6cScth 	vhci_attach,		/* attach and detach are mandatory */
2861e1ddd6cScth 	vhci_detach,
2871e1ddd6cScth 	nodev,			/* reset */
2881e1ddd6cScth 	&vhci_cb_ops,		/* cb_ops */
2891e1ddd6cScth 	NULL,			/* bus_ops */
2901e1ddd6cScth 	NULL,			/* power */
29119397407SSherry Moore 	ddi_quiesce_not_needed,	/* quiesce */
2921e1ddd6cScth };
2931e1ddd6cScth 
2941e1ddd6cScth extern struct mod_ops mod_driverops;
2951e1ddd6cScth 
2961e1ddd6cScth static struct modldrv modldrv = {
2971e1ddd6cScth 	&mod_driverops,
2981e1ddd6cScth 	vhci_version_name,	/* module name */
2991e1ddd6cScth 	&vhci_ops
3001e1ddd6cScth };
3011e1ddd6cScth 
3021e1ddd6cScth static struct modlinkage modlinkage = {
3031e1ddd6cScth 	MODREV_1,
3041e1ddd6cScth 	&modldrv,
3051e1ddd6cScth 	NULL
3061e1ddd6cScth };
3071e1ddd6cScth 
3081e1ddd6cScth static mdi_vhci_ops_t vhci_opinfo = {
3091e1ddd6cScth 	MDI_VHCI_OPS_REV,
3101e1ddd6cScth 	vhci_pathinfo_init,		/* Pathinfo node init callback */
3111e1ddd6cScth 	vhci_pathinfo_uninit,		/* Pathinfo uninit callback */
3121e1ddd6cScth 	vhci_pathinfo_state_change,	/* Pathinfo node state change */
3131e1ddd6cScth 	vhci_failover,			/* failover callback */
31455e592a2SRandall Ralphs 	vhci_client_attached,		/* client attached callback	*/
31555e592a2SRandall Ralphs 	vhci_is_dev_supported		/* is device supported by mdi */
3161e1ddd6cScth };
3171e1ddd6cScth 
3181e1ddd6cScth /*
3191e1ddd6cScth  * The scsi_failover table defines an ordered set of 'fops' modules supported
3201e1ddd6cScth  * by scsi_vhci.  Currently, initialize this table from the 'ddi-forceload'
3211e1ddd6cScth  * property specified in scsi_vhci.conf.
3221e1ddd6cScth  */
32355e592a2SRandall Ralphs static struct scsi_failover {
3241e1ddd6cScth 	ddi_modhandle_t			sf_mod;
3251e1ddd6cScth 	struct scsi_failover_ops	*sf_sfo;
3261e1ddd6cScth } *scsi_failover_table;
32755e592a2SRandall Ralphs static uint_t	scsi_nfailover;
3281e1ddd6cScth 
3291e1ddd6cScth int
_init(void)3301e1ddd6cScth _init(void)
3311e1ddd6cScth {
3321e1ddd6cScth 	int	rval;
3331e1ddd6cScth 
3341e1ddd6cScth 	/*
3351e1ddd6cScth 	 * Allocate soft state and prepare to do ddi_soft_state_zalloc()
3361e1ddd6cScth 	 * before registering with the transport first.
3371e1ddd6cScth 	 */
3381e1ddd6cScth 	if ((rval = ddi_soft_state_init(&vhci_softstate,
3391e1ddd6cScth 	    sizeof (struct scsi_vhci), 1)) != 0) {
3401e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL,
3411e1ddd6cScth 		    "!_init:soft state init failed\n"));
3421e1ddd6cScth 		return (rval);
3431e1ddd6cScth 	}
3441e1ddd6cScth 
3451e1ddd6cScth 	if ((rval = scsi_hba_init(&modlinkage)) != 0) {
3461e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL,
3471e1ddd6cScth 		    "!_init: scsi hba init failed\n"));
3481e1ddd6cScth 		ddi_soft_state_fini(&vhci_softstate);
3491e1ddd6cScth 		return (rval);
3501e1ddd6cScth 	}
3511e1ddd6cScth 
3521e1ddd6cScth 	mutex_init(&vhci_global_mutex, NULL, MUTEX_DRIVER, NULL);
3531e1ddd6cScth 	cv_init(&vhci_cv, NULL, CV_DRIVER, NULL);
3541e1ddd6cScth 
3551e1ddd6cScth 	mutex_init(&vhci_targetmap_mutex, NULL, MUTEX_DRIVER, NULL);
3561e1ddd6cScth 	vhci_targetmap_byport = mod_hash_create_strhash(
3571e1ddd6cScth 	    "vhci_targetmap_byport", 256, mod_hash_null_valdtor);
3581e1ddd6cScth 	vhci_targetmap_bypid = mod_hash_create_idhash(
3591e1ddd6cScth 	    "vhci_targetmap_bypid", 256, mod_hash_null_valdtor);
3601e1ddd6cScth 
3611e1ddd6cScth 	if ((rval = mod_install(&modlinkage)) != 0) {
3621e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!_init: mod_install failed\n"));
3631e1ddd6cScth 		if (vhci_targetmap_bypid)
3641e1ddd6cScth 			mod_hash_destroy_idhash(vhci_targetmap_bypid);
3651e1ddd6cScth 		if (vhci_targetmap_byport)
3661e1ddd6cScth 			mod_hash_destroy_strhash(vhci_targetmap_byport);
3671e1ddd6cScth 		mutex_destroy(&vhci_targetmap_mutex);
3681e1ddd6cScth 		cv_destroy(&vhci_cv);
3691e1ddd6cScth 		mutex_destroy(&vhci_global_mutex);
3701e1ddd6cScth 		scsi_hba_fini(&modlinkage);
3711e1ddd6cScth 		ddi_soft_state_fini(&vhci_softstate);
3721e1ddd6cScth 	}
3731e1ddd6cScth 	return (rval);
3741e1ddd6cScth }
3751e1ddd6cScth 
3761e1ddd6cScth 
3771e1ddd6cScth /*
3781e1ddd6cScth  * the system is done with us as a driver, so clean up
3791e1ddd6cScth  */
3801e1ddd6cScth int
_fini(void)3811e1ddd6cScth _fini(void)
3821e1ddd6cScth {
3831e1ddd6cScth 	int rval;
3841e1ddd6cScth 
3851e1ddd6cScth 	/*
3861e1ddd6cScth 	 * don't start cleaning up until we know that the module remove
3871e1ddd6cScth 	 * has worked  -- if this works, then we know that each instance
3881e1ddd6cScth 	 * has successfully been DDI_DETACHed
3891e1ddd6cScth 	 */
3901e1ddd6cScth 	if ((rval = mod_remove(&modlinkage)) != 0) {
3911e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!_fini: mod_remove failed\n"));
3921e1ddd6cScth 		return (rval);
3931e1ddd6cScth 	}
3941e1ddd6cScth 
3951e1ddd6cScth 	if (vhci_targetmap_bypid)
3961e1ddd6cScth 		mod_hash_destroy_idhash(vhci_targetmap_bypid);
3971e1ddd6cScth 	if (vhci_targetmap_byport)
3981e1ddd6cScth 		mod_hash_destroy_strhash(vhci_targetmap_byport);
3991e1ddd6cScth 	mutex_destroy(&vhci_targetmap_mutex);
4001e1ddd6cScth 	cv_destroy(&vhci_cv);
4011e1ddd6cScth 	mutex_destroy(&vhci_global_mutex);
4021e1ddd6cScth 	scsi_hba_fini(&modlinkage);
4031e1ddd6cScth 	ddi_soft_state_fini(&vhci_softstate);
4041e1ddd6cScth 
4051e1ddd6cScth 	return (rval);
4061e1ddd6cScth }
4071e1ddd6cScth 
4081e1ddd6cScth int
_info(struct modinfo * modinfop)4091e1ddd6cScth _info(struct modinfo *modinfop)
4101e1ddd6cScth {
4111e1ddd6cScth 	return (mod_info(&modlinkage, modinfop));
4121e1ddd6cScth }
4131e1ddd6cScth 
4141e1ddd6cScth /*
4151e1ddd6cScth  * Lookup scsi_failover by "short name" of failover module.
4161e1ddd6cScth  */
4171e1ddd6cScth struct scsi_failover_ops *
vhci_failover_ops_by_name(char * name)4181e1ddd6cScth vhci_failover_ops_by_name(char *name)
4191e1ddd6cScth {
4201e1ddd6cScth 	struct scsi_failover	*sf;
4211e1ddd6cScth 
4221e1ddd6cScth 	for (sf = scsi_failover_table; sf->sf_mod; sf++) {
4231e1ddd6cScth 		if (sf->sf_sfo == NULL)
4241e1ddd6cScth 			continue;
4251e1ddd6cScth 		if (strcmp(sf->sf_sfo->sfo_name, name) == 0)
4261e1ddd6cScth 			return (sf->sf_sfo);
4271e1ddd6cScth 	}
4281e1ddd6cScth 	return (NULL);
4291e1ddd6cScth }
4301e1ddd6cScth 
4311e1ddd6cScth /*
4321e1ddd6cScth  * Load all scsi_failover_ops 'fops' modules.
4331e1ddd6cScth  */
4341e1ddd6cScth static void
vhci_failover_modopen(struct scsi_vhci * vhci)4351e1ddd6cScth vhci_failover_modopen(struct scsi_vhci *vhci)
4361e1ddd6cScth {
4371e1ddd6cScth 	char			**module;
4381e1ddd6cScth 	int			i;
4391e1ddd6cScth 	struct scsi_failover	*sf;
4401e1ddd6cScth 	char			**dt;
4411e1ddd6cScth 	int			e;
4421e1ddd6cScth 
4431e1ddd6cScth 	if (scsi_failover_table)
4441e1ddd6cScth 		return;
4451e1ddd6cScth 
4461e1ddd6cScth 	/* Get the list of modules from scsi_vhci.conf */
4471e1ddd6cScth 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY,
4481e1ddd6cScth 	    vhci->vhci_dip, DDI_PROP_DONTPASS, "ddi-forceload",
4491e1ddd6cScth 	    &module, &scsi_nfailover) != DDI_PROP_SUCCESS) {
4501e1ddd6cScth 		cmn_err(CE_WARN, "scsi_vhci: "
4511e1ddd6cScth 		    "scsi_vhci.conf is missing 'ddi-forceload'");
4521e1ddd6cScth 		return;
4531e1ddd6cScth 	}
4541e1ddd6cScth 	if (scsi_nfailover == 0) {
4551e1ddd6cScth 		cmn_err(CE_WARN, "scsi_vhci: "
4561e1ddd6cScth 		    "scsi_vhci.conf has empty 'ddi-forceload'");
457247bd249Scth 		ddi_prop_free(module);
4581e1ddd6cScth 		return;
4591e1ddd6cScth 	}
4601e1ddd6cScth 
4611e1ddd6cScth 	/* allocate failover table based on number of modules */
4621e1ddd6cScth 	scsi_failover_table = (struct scsi_failover *)
4631e1ddd6cScth 	    kmem_zalloc(sizeof (struct scsi_failover) * (scsi_nfailover + 1),
4641e1ddd6cScth 	    KM_SLEEP);
4651e1ddd6cScth 
4661e1ddd6cScth 	/* loop over modules specified in scsi_vhci.conf and open each module */
4671e1ddd6cScth 	for (i = 0, sf = scsi_failover_table; i < scsi_nfailover; i++) {
4681e1ddd6cScth 		if (module[i] == NULL)
4691e1ddd6cScth 			continue;
4701e1ddd6cScth 
4711e1ddd6cScth 		sf->sf_mod = ddi_modopen(module[i], KRTLD_MODE_FIRST, &e);
4721e1ddd6cScth 		if (sf->sf_mod == NULL) {
4731e1ddd6cScth 			/*
4741e1ddd6cScth 			 * A module returns EEXIST if other software is
4751e1ddd6cScth 			 * supporting the intended function: for example
4761e1ddd6cScth 			 * the scsi_vhci_f_sum_emc module returns EEXIST
4771e1ddd6cScth 			 * from _init if EMC powerpath software is installed.
4781e1ddd6cScth 			 */
4791e1ddd6cScth 			if (e != EEXIST)
4801e1ddd6cScth 				cmn_err(CE_WARN, "scsi_vhci: unable to open "
4811e1ddd6cScth 				    "module '%s', error %d", module[i], e);
4821e1ddd6cScth 			continue;
4831e1ddd6cScth 		}
4841e1ddd6cScth 		sf->sf_sfo = ddi_modsym(sf->sf_mod,
4851e1ddd6cScth 		    "scsi_vhci_failover_ops", &e);
4861e1ddd6cScth 		if (sf->sf_sfo == NULL) {
4871e1ddd6cScth 			cmn_err(CE_WARN, "scsi_vhci: "
4881e1ddd6cScth 			    "unable to import 'scsi_failover_ops' from '%s', "
4891e1ddd6cScth 			    "error %d", module[i], e);
4901e1ddd6cScth 			(void) ddi_modclose(sf->sf_mod);
4911e1ddd6cScth 			sf->sf_mod = NULL;
4921e1ddd6cScth 			continue;
4931e1ddd6cScth 		}
4941e1ddd6cScth 
4951e1ddd6cScth 		/* register vid/pid of devices supported with mpapi */
4961e1ddd6cScth 		for (dt = sf->sf_sfo->sfo_devices; *dt; dt++)
4971e1ddd6cScth 			vhci_mpapi_add_dev_prod(vhci, *dt);
4981e1ddd6cScth 		sf++;
4991e1ddd6cScth 	}
5001e1ddd6cScth 
5011e1ddd6cScth 	/* verify that at least the "well-known" modules were there */
5021e1ddd6cScth 	if (vhci_failover_ops_by_name(SFO_NAME_SYM) == NULL)
5031e1ddd6cScth 		cmn_err(CE_WARN, "scsi_vhci: well-known module \""
5041e1ddd6cScth 		    SFO_NAME_SYM "\" not defined in scsi_vhci.conf's "
5051e1ddd6cScth 		    "'ddi-forceload'");
5061e1ddd6cScth 	if (vhci_failover_ops_by_name(SFO_NAME_TPGS) == NULL)
5071e1ddd6cScth 		cmn_err(CE_WARN, "scsi_vhci: well-known module \""
5081e1ddd6cScth 		    SFO_NAME_TPGS "\" not defined in scsi_vhci.conf's "
5091e1ddd6cScth 		    "'ddi-forceload'");
5101e1ddd6cScth 
5111e1ddd6cScth 	/* call sfo_init for modules that need it */
5121e1ddd6cScth 	for (sf = scsi_failover_table; sf->sf_mod; sf++) {
5131e1ddd6cScth 		if (sf->sf_sfo && sf->sf_sfo->sfo_init)
5140205780bSrralphs 			sf->sf_sfo->sfo_init();
5151e1ddd6cScth 	}
516247bd249Scth 
517247bd249Scth 	ddi_prop_free(module);
5181e1ddd6cScth }
5191e1ddd6cScth 
5201e1ddd6cScth /*
5211e1ddd6cScth  * unload all loaded scsi_failover_ops modules
5221e1ddd6cScth  */
5231e1ddd6cScth static void
vhci_failover_modclose()5241e1ddd6cScth vhci_failover_modclose()
5251e1ddd6cScth {
5261e1ddd6cScth 	struct scsi_failover	*sf;
5271e1ddd6cScth 
5281e1ddd6cScth 	for (sf = scsi_failover_table; sf->sf_mod; sf++) {
5291e1ddd6cScth 		if ((sf->sf_mod == NULL) || (sf->sf_sfo == NULL))
5301e1ddd6cScth 			continue;
5311e1ddd6cScth 		(void) ddi_modclose(sf->sf_mod);
5321e1ddd6cScth 		sf->sf_mod = NULL;
5331e1ddd6cScth 		sf->sf_sfo = NULL;
5341e1ddd6cScth 	}
5351e1ddd6cScth 
5361e1ddd6cScth 	if (scsi_failover_table && scsi_nfailover)
5371e1ddd6cScth 		kmem_free(scsi_failover_table,
5381e1ddd6cScth 		    sizeof (struct scsi_failover) * (scsi_nfailover + 1));
5391e1ddd6cScth 	scsi_failover_table = NULL;
5401e1ddd6cScth 	scsi_nfailover = 0;
5411e1ddd6cScth }
5421e1ddd6cScth 
5431e1ddd6cScth /* ARGSUSED */
5441e1ddd6cScth static int
vhci_open(dev_t * devp,int flag,int otype,cred_t * credp)5451e1ddd6cScth vhci_open(dev_t *devp, int flag, int otype, cred_t *credp)
5461e1ddd6cScth {
5471e1ddd6cScth 	struct scsi_vhci	*vhci;
5481e1ddd6cScth 
5491e1ddd6cScth 	if (otype != OTYP_CHR) {
5501e1ddd6cScth 		return (EINVAL);
5511e1ddd6cScth 	}
5521e1ddd6cScth 
5531e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(*devp)));
5541e1ddd6cScth 	if (vhci == NULL) {
5551e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_open: failed ENXIO\n"));
5561e1ddd6cScth 		return (ENXIO);
5571e1ddd6cScth 	}
5581e1ddd6cScth 
5591e1ddd6cScth 	mutex_enter(&vhci->vhci_mutex);
5601e1ddd6cScth 	if ((flag & FEXCL) && (vhci->vhci_state & VHCI_STATE_OPEN)) {
5611e1ddd6cScth 		mutex_exit(&vhci->vhci_mutex);
5621e1ddd6cScth 		vhci_log(CE_NOTE, vhci->vhci_dip,
5631e1ddd6cScth 		    "!vhci%d: Already open\n", getminor(*devp));
5641e1ddd6cScth 		return (EBUSY);
5651e1ddd6cScth 	}
5661e1ddd6cScth 
5671e1ddd6cScth 	vhci->vhci_state |= VHCI_STATE_OPEN;
5681e1ddd6cScth 	mutex_exit(&vhci->vhci_mutex);
5691e1ddd6cScth 	return (0);
5701e1ddd6cScth }
5711e1ddd6cScth 
5721e1ddd6cScth 
5731e1ddd6cScth /* ARGSUSED */
5741e1ddd6cScth static int
vhci_close(dev_t dev,int flag,int otype,cred_t * credp)5751e1ddd6cScth vhci_close(dev_t dev, int flag, int otype, cred_t *credp)
5761e1ddd6cScth {
5771e1ddd6cScth 	struct scsi_vhci	*vhci;
5781e1ddd6cScth 
5791e1ddd6cScth 	if (otype != OTYP_CHR) {
5801e1ddd6cScth 		return (EINVAL);
5811e1ddd6cScth 	}
5821e1ddd6cScth 
5831e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(dev)));
5841e1ddd6cScth 	if (vhci == NULL) {
5851e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_close: failed ENXIO\n"));
5861e1ddd6cScth 		return (ENXIO);
5871e1ddd6cScth 	}
5881e1ddd6cScth 
5891e1ddd6cScth 	mutex_enter(&vhci->vhci_mutex);
5901e1ddd6cScth 	vhci->vhci_state &= ~VHCI_STATE_OPEN;
5911e1ddd6cScth 	mutex_exit(&vhci->vhci_mutex);
5921e1ddd6cScth 
5931e1ddd6cScth 	return (0);
5941e1ddd6cScth }
5951e1ddd6cScth 
5961e1ddd6cScth /* ARGSUSED */
5971e1ddd6cScth static int
vhci_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rval)5981e1ddd6cScth vhci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
5991e1ddd6cScth     cred_t *credp, int *rval)
6001e1ddd6cScth {
6011e1ddd6cScth 	if (IS_DEVCTL(cmd)) {
6021e1ddd6cScth 		return (vhci_devctl(dev, cmd, data, mode, credp, rval));
6031e1ddd6cScth 	} else if (cmd == MP_CMD) {
6041e1ddd6cScth 		return (vhci_mpapi_ctl(dev, cmd, data, mode, credp, rval));
6051e1ddd6cScth 	} else {
6061e1ddd6cScth 		return (vhci_ctl(dev, cmd, data, mode, credp, rval));
6071e1ddd6cScth 	}
6081e1ddd6cScth }
6091e1ddd6cScth 
6101e1ddd6cScth /*
6111e1ddd6cScth  * attach the module
6121e1ddd6cScth  */
6131e1ddd6cScth static int
vhci_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)6141e1ddd6cScth vhci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
6151e1ddd6cScth {
6161e1ddd6cScth 	int			rval = DDI_FAILURE;
6171e1ddd6cScth 	int			scsi_hba_attached = 0;
6181e1ddd6cScth 	int			vhci_attached = 0;
6191e1ddd6cScth 	int			mutex_initted = 0;
6201e1ddd6cScth 	int			instance;
6211e1ddd6cScth 	struct scsi_vhci	*vhci;
6221e1ddd6cScth 	scsi_hba_tran_t		*tran;
6231e1ddd6cScth 	char			cache_name_buf[64];
6241e1ddd6cScth 	char			*data;
6251e1ddd6cScth 
6261e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_attach: cmd=0x%x\n", cmd));
6271e1ddd6cScth 
6281e1ddd6cScth 	instance = ddi_get_instance(dip);
6291e1ddd6cScth 
6301e1ddd6cScth 	switch (cmd) {
6311e1ddd6cScth 	case DDI_ATTACH:
6321e1ddd6cScth 		break;
6331e1ddd6cScth 
6341e1ddd6cScth 	case DDI_RESUME:
6351e1ddd6cScth 	case DDI_PM_RESUME:
6361e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_attach: resume not yet"
6371e1ddd6cScth 		    "implemented\n"));
6381e1ddd6cScth 		return (rval);
6391e1ddd6cScth 
6401e1ddd6cScth 	default:
6411e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL,
6421e1ddd6cScth 		    "!vhci_attach: unknown ddi command\n"));
6431e1ddd6cScth 		return (rval);
6441e1ddd6cScth 	}
6451e1ddd6cScth 
6461e1ddd6cScth 	/*
6471e1ddd6cScth 	 * Allocate vhci data structure.
6481e1ddd6cScth 	 */
6491e1ddd6cScth 	if (ddi_soft_state_zalloc(vhci_softstate, instance) != DDI_SUCCESS) {
6501e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
6511e1ddd6cScth 		    "soft state alloc failed\n"));
6521e1ddd6cScth 		return (DDI_FAILURE);
6531e1ddd6cScth 	}
6541e1ddd6cScth 
6551e1ddd6cScth 	if ((vhci = ddi_get_soft_state(vhci_softstate, instance)) == NULL) {
6561e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
6571e1ddd6cScth 		    "bad soft state\n"));
6581e1ddd6cScth 		ddi_soft_state_free(vhci_softstate, instance);
6591e1ddd6cScth 		return (DDI_FAILURE);
6601e1ddd6cScth 	}
6611e1ddd6cScth 
6621e1ddd6cScth 	/* Allocate packet cache */
6631e1ddd6cScth 	(void) snprintf(cache_name_buf, sizeof (cache_name_buf),
6641e1ddd6cScth 	    "vhci%d_cache", instance);
6651e1ddd6cScth 
6661e1ddd6cScth 	mutex_init(&vhci->vhci_mutex, NULL, MUTEX_DRIVER, NULL);
6671e1ddd6cScth 	mutex_initted++;
6681e1ddd6cScth 
6691e1ddd6cScth 	/*
6701e1ddd6cScth 	 * Allocate a transport structure
6711e1ddd6cScth 	 */
6721e1ddd6cScth 	tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
6731e1ddd6cScth 	ASSERT(tran != NULL);
6741e1ddd6cScth 
6751e1ddd6cScth 	vhci->vhci_tran		= tran;
6761e1ddd6cScth 	vhci->vhci_dip		= dip;
6771e1ddd6cScth 	vhci->vhci_instance	= instance;
6781e1ddd6cScth 
6791e1ddd6cScth 	tran->tran_hba_private	= vhci;
6801e1ddd6cScth 	tran->tran_tgt_init	= vhci_scsi_tgt_init;
6811e1ddd6cScth 	tran->tran_tgt_probe	= NULL;
6821e1ddd6cScth 	tran->tran_tgt_free	= vhci_scsi_tgt_free;
6831e1ddd6cScth 
6841e1ddd6cScth 	tran->tran_start	= vhci_scsi_start;
6851e1ddd6cScth 	tran->tran_abort	= vhci_scsi_abort;
6861e1ddd6cScth 	tran->tran_reset	= vhci_scsi_reset;
6871e1ddd6cScth 	tran->tran_getcap	= vhci_scsi_getcap;
6881e1ddd6cScth 	tran->tran_setcap	= vhci_scsi_setcap;
6891e1ddd6cScth 	tran->tran_init_pkt	= vhci_scsi_init_pkt;
6901e1ddd6cScth 	tran->tran_destroy_pkt	= vhci_scsi_destroy_pkt;
6911e1ddd6cScth 	tran->tran_dmafree	= vhci_scsi_dmafree;
6921e1ddd6cScth 	tran->tran_sync_pkt	= vhci_scsi_sync_pkt;
6931e1ddd6cScth 	tran->tran_reset_notify = vhci_scsi_reset_notify;
6941e1ddd6cScth 
6951e1ddd6cScth 	tran->tran_get_bus_addr	= vhci_scsi_get_bus_addr;
6961e1ddd6cScth 	tran->tran_get_name	= vhci_scsi_get_name;
6971e1ddd6cScth 	tran->tran_bus_reset	= NULL;
6981e1ddd6cScth 	tran->tran_quiesce	= NULL;
6991e1ddd6cScth 	tran->tran_unquiesce	= NULL;
7001e1ddd6cScth 
7011e1ddd6cScth 	/*
7021e1ddd6cScth 	 * register event notification routines with scsa
7031e1ddd6cScth 	 */
7041e1ddd6cScth 	tran->tran_get_eventcookie = NULL;
7051e1ddd6cScth 	tran->tran_add_eventcall = NULL;
7061e1ddd6cScth 	tran->tran_remove_eventcall = NULL;
7071e1ddd6cScth 	tran->tran_post_event	= NULL;
7081e1ddd6cScth 
7091e1ddd6cScth 	tran->tran_bus_power	= vhci_scsi_bus_power;
7101e1ddd6cScth 
7111e1ddd6cScth 	tran->tran_bus_config	= vhci_scsi_bus_config;
71253a7b6b6SChris Horne 	tran->tran_bus_unconfig	= vhci_scsi_bus_unconfig;
7131e1ddd6cScth 
7141e1ddd6cScth 	/*
7151e1ddd6cScth 	 * Attach this instance with the mpxio framework
7161e1ddd6cScth 	 */
7171e1ddd6cScth 	if (mdi_vhci_register(MDI_HCI_CLASS_SCSI, dip, &vhci_opinfo, 0)
7181e1ddd6cScth 	    != MDI_SUCCESS) {
7191e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
7201e1ddd6cScth 		    "mdi_vhci_register failed\n"));
7211e1ddd6cScth 		goto attach_fail;
7221e1ddd6cScth 	}
7231e1ddd6cScth 	vhci_attached++;
7241e1ddd6cScth 
7251e1ddd6cScth 	/*
7261e1ddd6cScth 	 * Attach this instance of the hba.
7271e1ddd6cScth 	 *
7281e1ddd6cScth 	 * Regarding dma attributes: Since scsi_vhci is a virtual scsi HBA
7291e1ddd6cScth 	 * driver, it has nothing to do with DMA. However, when calling
7301e1ddd6cScth 	 * scsi_hba_attach_setup() we need to pass something valid in the
7311e1ddd6cScth 	 * dma attributes parameter. So we just use scsi_alloc_attr.
7321e1ddd6cScth 	 * SCSA itself seems to care only for dma_attr_minxfer and
7331e1ddd6cScth 	 * dma_attr_burstsizes fields of dma attributes structure.
7341e1ddd6cScth 	 * It expects those fileds to be non-zero.
7351e1ddd6cScth 	 */
7361e1ddd6cScth 	if (scsi_hba_attach_setup(dip, &scsi_alloc_attr, tran,
73753a7b6b6SChris Horne 	    SCSI_HBA_ADDR_COMPLEX) != DDI_SUCCESS) {
7381e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
7391e1ddd6cScth 		    "hba attach failed\n"));
7401e1ddd6cScth 		goto attach_fail;
7411e1ddd6cScth 	}
7421e1ddd6cScth 	scsi_hba_attached++;
7431e1ddd6cScth 
7441e1ddd6cScth 	if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
7451e1ddd6cScth 	    INST2DEVCTL(instance), DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) {
7461e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
7471e1ddd6cScth 		    " ddi_create_minor_node failed\n"));
7481e1ddd6cScth 		goto attach_fail;
7491e1ddd6cScth 	}
7501e1ddd6cScth 
7511e1ddd6cScth 	/*
7521e1ddd6cScth 	 * Set pm-want-child-notification property for
7531e1ddd6cScth 	 * power management of the phci and client
7541e1ddd6cScth 	 */
7551e1ddd6cScth 	if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
7564e40636aSToomas Soome 	    "pm-want-child-notification?", NULL, 0) != DDI_PROP_SUCCESS) {
7571e1ddd6cScth 		cmn_err(CE_WARN,
7581e1ddd6cScth 		    "%s%d fail to create pm-want-child-notification? prop",
7591e1ddd6cScth 		    ddi_driver_name(dip), ddi_get_instance(dip));
7601e1ddd6cScth 		goto attach_fail;
7611e1ddd6cScth 	}
7621e1ddd6cScth 
7631e1ddd6cScth 	vhci->vhci_taskq = taskq_create("vhci_taskq", 1, MINCLSYSPRI, 1, 4, 0);
7641e1ddd6cScth 	vhci->vhci_update_pathstates_taskq =
7651e1ddd6cScth 	    taskq_create("vhci_update_pathstates", VHCI_NUM_UPDATE_TASKQ,
7661e1ddd6cScth 	    MINCLSYSPRI, 1, 4, 0);
7671e1ddd6cScth 	ASSERT(vhci->vhci_taskq);
7681e1ddd6cScth 	ASSERT(vhci->vhci_update_pathstates_taskq);
7691e1ddd6cScth 
7701e1ddd6cScth 	/*
7711e1ddd6cScth 	 * Set appropriate configuration flags based on options set in
7721e1ddd6cScth 	 * conf file.
7731e1ddd6cScth 	 */
7741e1ddd6cScth 	vhci->vhci_conf_flags = 0;
7751e1ddd6cScth 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, PROPFLAGS,
7761e1ddd6cScth 	    "auto-failback", &data) == DDI_SUCCESS) {
7771e1ddd6cScth 		if (strcmp(data, "enable") == 0)
7781e1ddd6cScth 			vhci->vhci_conf_flags |= VHCI_CONF_FLAGS_AUTO_FAILBACK;
7791e1ddd6cScth 		ddi_prop_free(data);
7801e1ddd6cScth 	}
7811e1ddd6cScth 
7821e1ddd6cScth 	if (!(vhci->vhci_conf_flags & VHCI_CONF_FLAGS_AUTO_FAILBACK))
7831e1ddd6cScth 		vhci_log(CE_NOTE, dip, "!Auto-failback capability "
7841e1ddd6cScth 		    "disabled through scsi_vhci.conf file.");
7851e1ddd6cScth 
7861e1ddd6cScth 	/*
7871e1ddd6cScth 	 * Allocate an mpapi private structure
7881e1ddd6cScth 	 */
7891e1ddd6cScth 	vhci->mp_priv = kmem_zalloc(sizeof (mpapi_priv_t), KM_SLEEP);
7901e1ddd6cScth 	if (vhci_mpapi_init(vhci) != 0) {
7911e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_attach: "
7921e1ddd6cScth 		    "vhci_mpapi_init() failed"));
7931e1ddd6cScth 	}
7941e1ddd6cScth 
7951e1ddd6cScth 	vhci_failover_modopen(vhci);		/* load failover modules */
7961e1ddd6cScth 
7971e1ddd6cScth 	ddi_report_dev(dip);
7981e1ddd6cScth 	return (DDI_SUCCESS);
7991e1ddd6cScth 
8001e1ddd6cScth attach_fail:
8011e1ddd6cScth 	if (vhci_attached)
8021e1ddd6cScth 		(void) mdi_vhci_unregister(dip, 0);
8031e1ddd6cScth 
8041e1ddd6cScth 	if (scsi_hba_attached)
8051e1ddd6cScth 		(void) scsi_hba_detach(dip);
8061e1ddd6cScth 
8071e1ddd6cScth 	if (vhci->vhci_tran)
8081e1ddd6cScth 		scsi_hba_tran_free(vhci->vhci_tran);
8091e1ddd6cScth 
8101e1ddd6cScth 	if (mutex_initted) {
8111e1ddd6cScth 		mutex_destroy(&vhci->vhci_mutex);
8121e1ddd6cScth 	}
8131e1ddd6cScth 
8141e1ddd6cScth 	ddi_soft_state_free(vhci_softstate, instance);
8151e1ddd6cScth 	return (DDI_FAILURE);
8161e1ddd6cScth }
8171e1ddd6cScth 
8181e1ddd6cScth 
8191e1ddd6cScth /*ARGSUSED*/
8201e1ddd6cScth static int
vhci_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)8211e1ddd6cScth vhci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8221e1ddd6cScth {
8231e1ddd6cScth 	int			instance = ddi_get_instance(dip);
8241e1ddd6cScth 	scsi_hba_tran_t		*tran;
8251e1ddd6cScth 	struct scsi_vhci	*vhci;
8261e1ddd6cScth 
8271e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_detach: cmd=0x%x\n", cmd));
8281e1ddd6cScth 
8291e1ddd6cScth 	if ((tran = ddi_get_driver_private(dip)) == NULL)
8301e1ddd6cScth 		return (DDI_FAILURE);
8311e1ddd6cScth 
8321e1ddd6cScth 	vhci = TRAN2HBAPRIVATE(tran);
8331e1ddd6cScth 	if (!vhci) {
8341e1ddd6cScth 		return (DDI_FAILURE);
8351e1ddd6cScth 	}
8361e1ddd6cScth 
8371e1ddd6cScth 	switch (cmd) {
8381e1ddd6cScth 	case DDI_DETACH:
8391e1ddd6cScth 		break;
8401e1ddd6cScth 
8411e1ddd6cScth 	case DDI_SUSPEND:
8421e1ddd6cScth 	case DDI_PM_SUSPEND:
8431e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_detach: suspend/pm not yet"
8441e1ddd6cScth 		    "implemented\n"));
8451e1ddd6cScth 		return (DDI_FAILURE);
8461e1ddd6cScth 
8471e1ddd6cScth 	default:
8481e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL,
8491e1ddd6cScth 		    "!vhci_detach: unknown ddi command\n"));
8501e1ddd6cScth 		return (DDI_FAILURE);
8511e1ddd6cScth 	}
8521e1ddd6cScth 
8531e1ddd6cScth 	(void) mdi_vhci_unregister(dip, 0);
8541e1ddd6cScth 	(void) scsi_hba_detach(dip);
8551e1ddd6cScth 	scsi_hba_tran_free(tran);
8561e1ddd6cScth 
8571e1ddd6cScth 	if (ddi_prop_remove(DDI_DEV_T_NONE, dip,
8581e1ddd6cScth 	    "pm-want-child-notification?") != DDI_PROP_SUCCESS) {
8591e1ddd6cScth 		cmn_err(CE_WARN,
8601e1ddd6cScth 		    "%s%d unable to remove prop pm-want_child_notification?",
8611e1ddd6cScth 		    ddi_driver_name(dip), ddi_get_instance(dip));
8621e1ddd6cScth 	}
8631e1ddd6cScth 	if (vhci_restart_timeid != 0) {
8641e1ddd6cScth 		(void) untimeout(vhci_restart_timeid);
8651e1ddd6cScth 	}
8661e1ddd6cScth 	vhci_restart_timeid = 0;
8671e1ddd6cScth 
8681e1ddd6cScth 	mutex_destroy(&vhci->vhci_mutex);
8691e1ddd6cScth 	vhci->vhci_dip = NULL;
8701e1ddd6cScth 	vhci->vhci_tran = NULL;
8711e1ddd6cScth 	taskq_destroy(vhci->vhci_taskq);
8721e1ddd6cScth 	taskq_destroy(vhci->vhci_update_pathstates_taskq);
8731e1ddd6cScth 	ddi_remove_minor_node(dip, NULL);
8741e1ddd6cScth 	ddi_soft_state_free(vhci_softstate, instance);
8751e1ddd6cScth 
8761e1ddd6cScth 	vhci_failover_modclose();		/* unload failover modules */
8771e1ddd6cScth 	return (DDI_SUCCESS);
8781e1ddd6cScth }
8791e1ddd6cScth 
8801e1ddd6cScth /*
8811e1ddd6cScth  * vhci_getinfo()
8821e1ddd6cScth  * Given the device number, return the devinfo pointer or the
8831e1ddd6cScth  * instance number.
8841e1ddd6cScth  * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
8851e1ddd6cScth  */
8861e1ddd6cScth 
8871e1ddd6cScth /*ARGSUSED*/
8881e1ddd6cScth static int
vhci_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)8891e1ddd6cScth vhci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
8901e1ddd6cScth {
8911e1ddd6cScth 	struct scsi_vhci	*vhcip;
8921e1ddd6cScth 	int			instance = MINOR2INST(getminor((dev_t)arg));
8931e1ddd6cScth 
8941e1ddd6cScth 	switch (cmd) {
8951e1ddd6cScth 	case DDI_INFO_DEVT2DEVINFO:
8961e1ddd6cScth 		vhcip = ddi_get_soft_state(vhci_softstate, instance);
8971e1ddd6cScth 		if (vhcip != NULL)
8981e1ddd6cScth 			*result = vhcip->vhci_dip;
8991e1ddd6cScth 		else {
9001e1ddd6cScth 			*result = NULL;
9011e1ddd6cScth 			return (DDI_FAILURE);
9021e1ddd6cScth 		}
9031e1ddd6cScth 		break;
9041e1ddd6cScth 
9051e1ddd6cScth 	case DDI_INFO_DEVT2INSTANCE:
9061e1ddd6cScth 		*result = (void *)(uintptr_t)instance;
9071e1ddd6cScth 		break;
9081e1ddd6cScth 
9091e1ddd6cScth 	default:
9101e1ddd6cScth 		return (DDI_FAILURE);
9111e1ddd6cScth 	}
9121e1ddd6cScth 
9131e1ddd6cScth 	return (DDI_SUCCESS);
9141e1ddd6cScth }
9151e1ddd6cScth 
9161e1ddd6cScth /*ARGSUSED*/
9171e1ddd6cScth static int
vhci_scsi_tgt_init(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * hba_tran,struct scsi_device * sd)9181e1ddd6cScth vhci_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
9191e1ddd6cScth     scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
9201e1ddd6cScth {
9211e1ddd6cScth 	char			*guid;
9221e1ddd6cScth 	scsi_vhci_lun_t		*vlun;
9231e1ddd6cScth 	struct scsi_vhci	*vhci;
9241e1ddd6cScth 	clock_t			from_ticks;
9251e1ddd6cScth 	mdi_pathinfo_t		*pip;
9261e1ddd6cScth 	int			rval;
9271e1ddd6cScth 
9281e1ddd6cScth 	ASSERT(hba_dip != NULL);
9291e1ddd6cScth 	ASSERT(tgt_dip != NULL);
9301e1ddd6cScth 
9315c44817cSjw149990 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip, PROPFLAGS,
9325c44817cSjw149990 	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
9335c44817cSjw149990 		/*
9345c44817cSjw149990 		 * This must be the .conf node without GUID property.
9355c44817cSjw149990 		 * The node under fp already inserts a delay, so we
9365c44817cSjw149990 		 * just return from here. We rely on this delay to have
9375c44817cSjw149990 		 * all dips be posted to the ndi hotplug thread's newdev
9385c44817cSjw149990 		 * list. This is necessary for the deferred attach
9395c44817cSjw149990 		 * mechanism to work and opens() done soon after boot to
9405c44817cSjw149990 		 * succeed.
9415c44817cSjw149990 		 */
9425c44817cSjw149990 		VHCI_DEBUG(4, (CE_WARN, hba_dip, "tgt_init: lun guid "
9435c44817cSjw149990 		    "property failed"));
9445c44817cSjw149990 		return (DDI_NOT_WELL_FORMED);
9455c44817cSjw149990 	}
9465c44817cSjw149990 
9475c44817cSjw149990 	if (ndi_dev_is_persistent_node(tgt_dip) == 0) {
9485c44817cSjw149990 		/*
9495c44817cSjw149990 		 * This must be .conf node with the GUID property. We don't
9505c44817cSjw149990 		 * merge property by ndi_merge_node() here  because the
9515c44817cSjw149990 		 * devi_addr_buf of .conf node is "" always according the
9525c44817cSjw149990 		 * implementation of vhci_scsi_get_name_bus_addr().
9535c44817cSjw149990 		 */
9545c44817cSjw149990 		ddi_set_name_addr(tgt_dip, NULL);
9555c44817cSjw149990 		return (DDI_FAILURE);
9565c44817cSjw149990 	}
9575c44817cSjw149990 
9581e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(hba_dip));
9591e1ddd6cScth 	ASSERT(vhci != NULL);
9601e1ddd6cScth 
9611e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, hba_dip,
9621e1ddd6cScth 	    "!tgt_init: called for %s (instance %d)\n",
9631e1ddd6cScth 	    ddi_driver_name(tgt_dip), ddi_get_instance(tgt_dip)));
9641e1ddd6cScth 
9651e1ddd6cScth 	vlun = vhci_lun_lookup(tgt_dip);
9661e1ddd6cScth 
9671e1ddd6cScth 	mutex_enter(&vhci_global_mutex);
9681e1ddd6cScth 
9691e1ddd6cScth 	from_ticks = ddi_get_lbolt();
9701e1ddd6cScth 	if (vhci_to_ticks == 0) {
9711e1ddd6cScth 		vhci_to_ticks = from_ticks +
9721e1ddd6cScth 		    drv_usectohz(vhci_init_wait_timeout);
9731e1ddd6cScth 	}
9741e1ddd6cScth 
9751e1ddd6cScth #if DEBUG
9761e1ddd6cScth 	if (vlun) {
9771e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, hba_dip, "tgt_init: "
9781e1ddd6cScth 		    "vhci_scsi_tgt_init: guid %s : found vlun 0x%p "
9791e1ddd6cScth 		    "from_ticks %lx to_ticks %lx",
9801e1ddd6cScth 		    guid, (void *)vlun, from_ticks, vhci_to_ticks));
9811e1ddd6cScth 	} else {
9821e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, hba_dip, "tgt_init: "
9831e1ddd6cScth 		    "vhci_scsi_tgt_init: guid %s : vlun not found "
9841e1ddd6cScth 		    "from_ticks %lx to_ticks %lx", guid, from_ticks,
9851e1ddd6cScth 		    vhci_to_ticks));
9861e1ddd6cScth 	}
9871e1ddd6cScth #endif
9881e1ddd6cScth 
9891e1ddd6cScth 	rval = mdi_select_path(tgt_dip, NULL,
9901e1ddd6cScth 	    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH), NULL, &pip);
9911e1ddd6cScth 	if (rval == MDI_SUCCESS) {
9921e1ddd6cScth 		mdi_rele_path(pip);
9931e1ddd6cScth 	}
9941e1ddd6cScth 
9951e1ddd6cScth 	/*
9961e1ddd6cScth 	 * Wait for the following conditions :
9971e1ddd6cScth 	 *	1. no vlun available yet
9981e1ddd6cScth 	 *	2. no path established
9991e1ddd6cScth 	 *	3. timer did not expire
10001e1ddd6cScth 	 */
10011e1ddd6cScth 	while ((vlun == NULL) || (mdi_client_get_path_count(tgt_dip) == 0) ||
10021e1ddd6cScth 	    (rval != MDI_SUCCESS)) {
10031e1ddd6cScth 		if (vlun && vlun->svl_not_supported) {
10041e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, hba_dip, "tgt_init: "
10051e1ddd6cScth 			    "vlun 0x%p lun guid %s not supported!",
10061e1ddd6cScth 			    (void *)vlun, guid));
10071e1ddd6cScth 			mutex_exit(&vhci_global_mutex);
10081e1ddd6cScth 			ddi_prop_free(guid);
10091e1ddd6cScth 			return (DDI_NOT_WELL_FORMED);
10101e1ddd6cScth 		}
10111e1ddd6cScth 		if ((vhci_first_time == 0) && (from_ticks >= vhci_to_ticks)) {
10121e1ddd6cScth 			vhci_first_time = 1;
10131e1ddd6cScth 		}
10141e1ddd6cScth 		if (vhci_first_time == 1) {
10151e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, hba_dip, "vhci_scsi_tgt_init: "
10161e1ddd6cScth 			    "no wait for %s. from_tick %lx, to_tick %lx",
10171e1ddd6cScth 			    guid, from_ticks, vhci_to_ticks));
10181e1ddd6cScth 			mutex_exit(&vhci_global_mutex);
10191e1ddd6cScth 			ddi_prop_free(guid);
10201e1ddd6cScth 			return (DDI_NOT_WELL_FORMED);
10211e1ddd6cScth 		}
10221e1ddd6cScth 
10231e1ddd6cScth 		if (cv_timedwait(&vhci_cv,
10241e1ddd6cScth 		    &vhci_global_mutex, vhci_to_ticks) == -1) {
10251e1ddd6cScth 			/* Timed out */
10261e1ddd6cScth #ifdef DEBUG
10271e1ddd6cScth 			if (vlun == NULL) {
10281e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, hba_dip,
10291e1ddd6cScth 				    "tgt_init: no vlun for %s!", guid));
10301e1ddd6cScth 			} else if (mdi_client_get_path_count(tgt_dip) == 0) {
10311e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, hba_dip,
10321e1ddd6cScth 				    "tgt_init: client path count is "
10331e1ddd6cScth 				    "zero for %s!", guid));
10341e1ddd6cScth 			} else {
10351e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, hba_dip,
10361e1ddd6cScth 				    "tgt_init: client path not "
10371e1ddd6cScth 				    "available yet for %s!", guid));
10381e1ddd6cScth 			}
10391e1ddd6cScth #endif /* DEBUG */
10401e1ddd6cScth 			mutex_exit(&vhci_global_mutex);
10411e1ddd6cScth 			ddi_prop_free(guid);
10421e1ddd6cScth 			return (DDI_NOT_WELL_FORMED);
10431e1ddd6cScth 		}
10441e1ddd6cScth 		vlun = vhci_lun_lookup(tgt_dip);
10451e1ddd6cScth 		rval = mdi_select_path(tgt_dip, NULL,
10461e1ddd6cScth 		    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
10471e1ddd6cScth 		    NULL, &pip);
10481e1ddd6cScth 		if (rval == MDI_SUCCESS) {
10491e1ddd6cScth 			mdi_rele_path(pip);
10501e1ddd6cScth 		}
10511e1ddd6cScth 		from_ticks = ddi_get_lbolt();
10521e1ddd6cScth 	}
10531e1ddd6cScth 	mutex_exit(&vhci_global_mutex);
10541e1ddd6cScth 
10551e1ddd6cScth 	ASSERT(vlun != NULL);
10561e1ddd6cScth 	ddi_prop_free(guid);
105753a7b6b6SChris Horne 
105853a7b6b6SChris Horne 	scsi_device_hba_private_set(sd, vlun);
10591e1ddd6cScth 
10601e1ddd6cScth 	return (DDI_SUCCESS);
10611e1ddd6cScth }
10621e1ddd6cScth 
10631e1ddd6cScth /*ARGSUSED*/
10641e1ddd6cScth static void
vhci_scsi_tgt_free(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * hba_tran,struct scsi_device * sd)10651e1ddd6cScth vhci_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
10661e1ddd6cScth     scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
10671e1ddd6cScth {
106829c56acbSRaghuram Prahlada 	struct scsi_vhci_lun *dvlp;
106929c56acbSRaghuram Prahlada 	ASSERT(mdi_client_get_path_count(tgt_dip) <= 0);
107029c56acbSRaghuram Prahlada 	dvlp = (struct scsi_vhci_lun *)scsi_device_hba_private_get(sd);
107129c56acbSRaghuram Prahlada 	ASSERT(dvlp != NULL);
107229c56acbSRaghuram Prahlada 
107329c56acbSRaghuram Prahlada 	vhci_lun_free(dvlp, sd);
10741e1ddd6cScth }
10751e1ddd6cScth 
10761e1ddd6cScth /*
10771e1ddd6cScth  * a PGR register command has started; copy the info we need
10781e1ddd6cScth  */
10791e1ddd6cScth int
vhci_pgr_register_start(scsi_vhci_lun_t * vlun,struct scsi_pkt * pkt)10801e1ddd6cScth vhci_pgr_register_start(scsi_vhci_lun_t *vlun, struct scsi_pkt *pkt)
10811e1ddd6cScth {
10821e1ddd6cScth 	struct vhci_pkt		*vpkt = TGTPKT2VHCIPKT(pkt);
10831e1ddd6cScth 	void			*addr;
10841e1ddd6cScth 
10851e1ddd6cScth 	if (!vpkt->vpkt_tgt_init_bp)
10861e1ddd6cScth 		return (TRAN_BADPKT);
10871e1ddd6cScth 
10881e1ddd6cScth 	addr = bp_mapin_common(vpkt->vpkt_tgt_init_bp,
10891e1ddd6cScth 	    (vpkt->vpkt_flags & CFLAG_NOWAIT) ? VM_NOSLEEP : VM_SLEEP);
10901e1ddd6cScth 	if (addr == NULL)
10911e1ddd6cScth 		return (TRAN_BUSY);
10921e1ddd6cScth 
10931e1ddd6cScth 	mutex_enter(&vlun->svl_mutex);
10941e1ddd6cScth 
10951e1ddd6cScth 	vhci_print_prout_keys(vlun, "v_pgr_reg_start: before bcopy:");
10961e1ddd6cScth 
10971e1ddd6cScth 	bcopy(addr, &vlun->svl_prout, sizeof (vhci_prout_t) -
10981e1ddd6cScth 	    (2 * MHIOC_RESV_KEY_SIZE * sizeof (char)));
10991e1ddd6cScth 	bcopy(pkt->pkt_cdbp, vlun->svl_cdb, sizeof (vlun->svl_cdb));
11001e1ddd6cScth 
11011e1ddd6cScth 	vhci_print_prout_keys(vlun, "v_pgr_reg_start: after bcopy:");
11021e1ddd6cScth 
11031e1ddd6cScth 	vlun->svl_time = pkt->pkt_time;
11041e1ddd6cScth 	vlun->svl_bcount = vpkt->vpkt_tgt_init_bp->b_bcount;
11051e1ddd6cScth 	vlun->svl_first_path = vpkt->vpkt_path;
11061e1ddd6cScth 	mutex_exit(&vlun->svl_mutex);
11071e1ddd6cScth 	return (0);
11081e1ddd6cScth }
11091e1ddd6cScth 
11101e1ddd6cScth /*
11111e1ddd6cScth  * Function name : vhci_scsi_start()
11121e1ddd6cScth  *
11131e1ddd6cScth  * Return Values : TRAN_FATAL_ERROR	- vhci has been shutdown
11141e1ddd6cScth  *					  or other fatal failure
11151e1ddd6cScth  *					  preventing packet transportation
11161e1ddd6cScth  *		   TRAN_BUSY		- request queue is full
11171e1ddd6cScth  *		   TRAN_ACCEPT		- pkt has been submitted to phci
11181e1ddd6cScth  *					  (or is held in the waitQ)
11191e1ddd6cScth  * Description	 : Implements SCSA's tran_start() entry point for
11201e1ddd6cScth  *		   packet transport
11211e1ddd6cScth  *
11221e1ddd6cScth  */
11231e1ddd6cScth static int
vhci_scsi_start(struct scsi_address * ap,struct scsi_pkt * pkt)11241e1ddd6cScth vhci_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
11251e1ddd6cScth {
11261e1ddd6cScth 	int			rval = TRAN_ACCEPT;
11271e1ddd6cScth 	int			instance, held;
11281e1ddd6cScth 	struct scsi_vhci	*vhci = ADDR2VHCI(ap);
11291e1ddd6cScth 	struct scsi_vhci_lun	*vlun = ADDR2VLUN(ap);
11301e1ddd6cScth 	struct vhci_pkt		*vpkt = TGTPKT2VHCIPKT(pkt);
11311e1ddd6cScth 	int			flags = 0;
11321c8dbe37SMilos Muzik 	scsi_vhci_priv_t	*svp, *svp_resrv;
11331e1ddd6cScth 	dev_info_t		*cdip;
11341e1ddd6cScth 	client_lb_t		lbp;
11351e1ddd6cScth 	int			restore_lbp = 0;
11361e1ddd6cScth 	/* set if pkt is SCSI-II RESERVE cmd */
11371e1ddd6cScth 	int			pkt_reserve_cmd = 0;
11381e1ddd6cScth 	int			reserve_failed = 0;
11391c8dbe37SMilos Muzik 	int			resrv_instance = 0;
11401c8dbe37SMilos Muzik 	mdi_pathinfo_t		*pip;
11411c8dbe37SMilos Muzik 	struct scsi_pkt		*rel_pkt;
11421e1ddd6cScth 
11431e1ddd6cScth 	ASSERT(vhci != NULL);
11441e1ddd6cScth 	ASSERT(vpkt != NULL);
11451e1ddd6cScth 	ASSERT(vpkt->vpkt_state != VHCI_PKT_ISSUED);
11461e1ddd6cScth 	cdip = ADDR2DIP(ap);
11471e1ddd6cScth 
11481e1ddd6cScth 	/*
11491e1ddd6cScth 	 * Block IOs if LUN is held or QUIESCED for IOs.
11501e1ddd6cScth 	 */
11511e1ddd6cScth 	if ((VHCI_LUN_IS_HELD(vlun)) ||
11521e1ddd6cScth 	    ((vlun->svl_flags & VLUN_QUIESCED_FLG) == VLUN_QUIESCED_FLG)) {
11531e1ddd6cScth 		return (TRAN_BUSY);
11541e1ddd6cScth 	}
11551e1ddd6cScth 
11561e1ddd6cScth 	/*
11571e1ddd6cScth 	 * vhci_lun needs to be quiesced before SCSI-II RESERVE command
11581e1ddd6cScth 	 * can be issued.  This may require a cv_timedwait, which is
11591e1ddd6cScth 	 * dangerous to perform in an interrupt context.  So if this
11601e1ddd6cScth 	 * is a RESERVE command a taskq is dispatched to service it.
11611e1ddd6cScth 	 * This taskq shall again call vhci_scsi_start, but we shall be
11621e1ddd6cScth 	 * sure its not in an interrupt context.
11631e1ddd6cScth 	 */
11641e1ddd6cScth 	if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
11651e1ddd6cScth 	    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
11661e1ddd6cScth 		if (!(vpkt->vpkt_state & VHCI_PKT_THRU_TASKQ)) {
11671e1ddd6cScth 			if (taskq_dispatch(vhci->vhci_taskq,
11681e1ddd6cScth 			    vhci_dispatch_scsi_start, (void *) vpkt,
1169fc8ae2ecSToomas Soome 			    KM_NOSLEEP) != TASKQID_INVALID) {
11701e1ddd6cScth 				return (TRAN_ACCEPT);
11711e1ddd6cScth 			} else {
11721e1ddd6cScth 				return (TRAN_BUSY);
11731e1ddd6cScth 			}
11741e1ddd6cScth 		}
11751e1ddd6cScth 
11761e1ddd6cScth 		/*
11771e1ddd6cScth 		 * Here we ensure that simultaneous SCSI-II RESERVE cmds don't
11781e1ddd6cScth 		 * get serviced for a lun.
11791e1ddd6cScth 		 */
11801e1ddd6cScth 		VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
11811e1ddd6cScth 		if (!held) {
11821e1ddd6cScth 			return (TRAN_BUSY);
11831e1ddd6cScth 		} else if ((vlun->svl_flags & VLUN_QUIESCED_FLG) ==
11841e1ddd6cScth 		    VLUN_QUIESCED_FLG) {
11851e1ddd6cScth 			VHCI_RELEASE_LUN(vlun);
11861e1ddd6cScth 			return (TRAN_BUSY);
11871e1ddd6cScth 		}
11881e1ddd6cScth 
11891e1ddd6cScth 		/*
11901e1ddd6cScth 		 * To ensure that no IOs occur for this LUN for the duration
11911e1ddd6cScth 		 * of this pkt set the VLUN_QUIESCED_FLG.
11921e1ddd6cScth 		 * In case this routine needs to exit on error make sure that
11931e1ddd6cScth 		 * this flag is cleared.
11941e1ddd6cScth 		 */
11951e1ddd6cScth 		vlun->svl_flags |= VLUN_QUIESCED_FLG;
11961e1ddd6cScth 		pkt_reserve_cmd = 1;
11971e1ddd6cScth 
11981e1ddd6cScth 		/*
11991e1ddd6cScth 		 * if this is a SCSI-II RESERVE command, set load balancing
12001e1ddd6cScth 		 * policy to be ALTERNATE PATH to ensure that all subsequent
12011e1ddd6cScth 		 * IOs are routed on the same path.  This is because if commands
12021e1ddd6cScth 		 * are routed across multiple paths then IOs on paths other than
12031e1ddd6cScth 		 * the one on which the RESERVE was executed will get a
12041e1ddd6cScth 		 * RESERVATION CONFLICT
12051e1ddd6cScth 		 */
12061e1ddd6cScth 		lbp = mdi_get_lb_policy(cdip);
12071e1ddd6cScth 		if (lbp != LOAD_BALANCE_NONE) {
12081e1ddd6cScth 			if (vhci_quiesce_lun(vlun) != 1) {
12091e1ddd6cScth 				vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
12101e1ddd6cScth 				VHCI_RELEASE_LUN(vlun);
12111e1ddd6cScth 				return (TRAN_FATAL_ERROR);
12121e1ddd6cScth 			}
12131e1ddd6cScth 			vlun->svl_lb_policy_save = lbp;
12141e1ddd6cScth 			if (mdi_set_lb_policy(cdip, LOAD_BALANCE_NONE) !=
12151e1ddd6cScth 			    MDI_SUCCESS) {
12161e1ddd6cScth 				vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
12171e1ddd6cScth 				VHCI_RELEASE_LUN(vlun);
12181e1ddd6cScth 				return (TRAN_FATAL_ERROR);
12191e1ddd6cScth 			}
12201e1ddd6cScth 			restore_lbp = 1;
12211e1ddd6cScth 		}
12221c8dbe37SMilos Muzik 
12231c8dbe37SMilos Muzik 		VHCI_DEBUG(2, (CE_NOTE, vhci->vhci_dip,
12241c8dbe37SMilos Muzik 		    "!vhci_scsi_start: sending SCSI-2 RESERVE, vlun 0x%p, "
12251c8dbe37SMilos Muzik 		    "svl_resrv_pip 0x%p, svl_flags: %x, lb_policy %x",
12261c8dbe37SMilos Muzik 		    (void *)vlun, (void *)vlun->svl_resrv_pip, vlun->svl_flags,
12271c8dbe37SMilos Muzik 		    mdi_get_lb_policy(cdip)));
12281c8dbe37SMilos Muzik 
12291e1ddd6cScth 		/*
12301e1ddd6cScth 		 * See comments for VLUN_RESERVE_ACTIVE_FLG in scsi_vhci.h
12311e1ddd6cScth 		 * To narrow this window where a reserve command may be sent
12321e1ddd6cScth 		 * down an inactive path the path states first need to be
12331e1ddd6cScth 		 * updated.  Before calling vhci_update_pathstates reset
12341e1ddd6cScth 		 * VLUN_RESERVE_ACTIVE_FLG, just in case it was already set
12351e1ddd6cScth 		 * for this lun.  This shall prevent an unnecessary reset
12361c8dbe37SMilos Muzik 		 * from being sent out.  Also remember currently reserved path
12371c8dbe37SMilos Muzik 		 * just for a case the new reservation will go to another path.
12381e1ddd6cScth 		 */
12391c8dbe37SMilos Muzik 		if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
12401c8dbe37SMilos Muzik 			resrv_instance = mdi_pi_get_path_instance(
12411c8dbe37SMilos Muzik 			    vlun->svl_resrv_pip);
12421c8dbe37SMilos Muzik 		}
12431e1ddd6cScth 		vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
12441e1ddd6cScth 		vhci_update_pathstates((void *)vlun);
12451e1ddd6cScth 	}
12461e1ddd6cScth 
12471e1ddd6cScth 	instance = ddi_get_instance(vhci->vhci_dip);
12481e1ddd6cScth 
12491e1ddd6cScth 	/*
12501e1ddd6cScth 	 * If the command is PRIN with action of zero, then the cmd
12511e1ddd6cScth 	 * is reading PR keys which requires filtering on completion.
12521e1ddd6cScth 	 * Data cache sync must be guaranteed.
12531e1ddd6cScth 	 */
12540205780bSrralphs 	if ((pkt->pkt_cdbp[0] == SCMD_PRIN) && (pkt->pkt_cdbp[1] == 0) &&
12551e1ddd6cScth 	    (vpkt->vpkt_org_vpkt == NULL)) {
12561e1ddd6cScth 		vpkt->vpkt_tgt_init_pkt_flags |= PKT_CONSISTENT;
12571e1ddd6cScth 	}
12581e1ddd6cScth 
12591e1ddd6cScth 	/*
12601e1ddd6cScth 	 * Do not defer bind for PKT_DMA_PARTIAL
12611e1ddd6cScth 	 */
12621e1ddd6cScth 	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
12631e1ddd6cScth 
12641e1ddd6cScth 		/* This is a non pkt_dma_partial case */
12651e1ddd6cScth 		if ((rval = vhci_bind_transport(
12661e1ddd6cScth 		    ap, vpkt, vpkt->vpkt_tgt_init_pkt_flags, NULL_FUNC))
12671e1ddd6cScth 		    != TRAN_ACCEPT) {
12681e1ddd6cScth 			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
12691e1ddd6cScth 			    "!vhci%d %x: failed to bind transport: "
12701e1ddd6cScth 			    "vlun 0x%p pkt_reserved %x restore_lbp %x,"
12711e1ddd6cScth 			    "lbp %x", instance, rval, (void *)vlun,
12721e1ddd6cScth 			    pkt_reserve_cmd, restore_lbp, lbp));
12731e1ddd6cScth 			if (restore_lbp)
12741e1ddd6cScth 				(void) mdi_set_lb_policy(cdip, lbp);
12751e1ddd6cScth 			if (pkt_reserve_cmd)
12761e1ddd6cScth 				vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
12771e1ddd6cScth 			return (rval);
12781e1ddd6cScth 		}
12791e1ddd6cScth 		VHCI_DEBUG(8, (CE_NOTE, NULL,
12801e1ddd6cScth 		    "vhci_scsi_start: v_b_t called 0x%p\n", (void *)vpkt));
12811e1ddd6cScth 	}
12821e1ddd6cScth 	ASSERT(vpkt->vpkt_hba_pkt != NULL);
12831e1ddd6cScth 	ASSERT(vpkt->vpkt_path != NULL);
12841e1ddd6cScth 
12851e1ddd6cScth 	/*
12861e1ddd6cScth 	 * This is the chance to adjust the pHCI's pkt and other information
12871e1ddd6cScth 	 * from target driver's pkt.
12881e1ddd6cScth 	 */
12891e1ddd6cScth 	VHCI_DEBUG(8, (CE_NOTE, vhci->vhci_dip, "vhci_scsi_start vpkt %p\n",
12901e1ddd6cScth 	    (void *)vpkt));
12911e1ddd6cScth 	vhci_update_pHCI_pkt(vpkt, pkt);
12921e1ddd6cScth 
12931e1ddd6cScth 	if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
12941e1ddd6cScth 		if (vpkt->vpkt_path != vlun->svl_resrv_pip) {
12951e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, vhci->vhci_dip,
12961e1ddd6cScth 			    "!vhci_bind: reserve flag set for vlun 0x%p, but, "
12971e1ddd6cScth 			    "pktpath 0x%p resrv path 0x%p differ. lb_policy %x",
12981e1ddd6cScth 			    (void *)vlun, (void *)vpkt->vpkt_path,
12991e1ddd6cScth 			    (void *)vlun->svl_resrv_pip,
13001e1ddd6cScth 			    mdi_get_lb_policy(cdip)));
13011e1ddd6cScth 			reserve_failed = 1;
13021e1ddd6cScth 		}
13031e1ddd6cScth 	}
13041e1ddd6cScth 
13050205780bSrralphs 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(vpkt->vpkt_path);
13061e1ddd6cScth 	if (svp == NULL || reserve_failed) {
13071e1ddd6cScth 		if (pkt_reserve_cmd) {
13081e1ddd6cScth 			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
13091e1ddd6cScth 			    "!vhci_bind returned null svp vlun 0x%p",
13101e1ddd6cScth 			    (void *)vlun));
13111e1ddd6cScth 			vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
13121e1ddd6cScth 			if (restore_lbp)
13131e1ddd6cScth 				(void) mdi_set_lb_policy(cdip, lbp);
13141e1ddd6cScth 		}
13151e1ddd6cScth pkt_cleanup:
13161e1ddd6cScth 		if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
13171e1ddd6cScth 			scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
13181e1ddd6cScth 			vpkt->vpkt_hba_pkt = NULL;
13191e1ddd6cScth 			if (vpkt->vpkt_path) {
13201e1ddd6cScth 				mdi_rele_path(vpkt->vpkt_path);
13211e1ddd6cScth 				vpkt->vpkt_path = NULL;
13221e1ddd6cScth 			}
13231e1ddd6cScth 		}
13241e1ddd6cScth 		if ((pkt->pkt_cdbp[0] == SCMD_PROUT) &&
13251e1ddd6cScth 		    (((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
13260205780bSrralphs 		    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE))) {
13271e1ddd6cScth 			sema_v(&vlun->svl_pgr_sema);
13281e1ddd6cScth 		}
13291e1ddd6cScth 		return (TRAN_BUSY);
13301e1ddd6cScth 	}
13311e1ddd6cScth 
13321c8dbe37SMilos Muzik 	if ((resrv_instance != 0) && (resrv_instance !=
13331c8dbe37SMilos Muzik 	    mdi_pi_get_path_instance(vpkt->vpkt_path))) {
13341c8dbe37SMilos Muzik 		/*
13351c8dbe37SMilos Muzik 		 * This is an attempt to reserve vpkt->vpkt_path.  But the
13361c8dbe37SMilos Muzik 		 * previously reserved path referred by resrv_instance might
13371c8dbe37SMilos Muzik 		 * still be reserved.  Hence we will send a release command
13381c8dbe37SMilos Muzik 		 * there in order to avoid a reservation conflict.
13391c8dbe37SMilos Muzik 		 */
13401c8dbe37SMilos Muzik 		VHCI_DEBUG(1, (CE_NOTE, vhci->vhci_dip, "!vhci_scsi_start: "
13411c8dbe37SMilos Muzik 		    "conflicting reservation on another path, vlun 0x%p, "
13421c8dbe37SMilos Muzik 		    "reserved instance %d, new instance: %d, pip: 0x%p",
13431c8dbe37SMilos Muzik 		    (void *)vlun, resrv_instance,
13441c8dbe37SMilos Muzik 		    mdi_pi_get_path_instance(vpkt->vpkt_path),
13451c8dbe37SMilos Muzik 		    (void *)vpkt->vpkt_path));
13461c8dbe37SMilos Muzik 
13471c8dbe37SMilos Muzik 		/*
13481c8dbe37SMilos Muzik 		 * In rare cases, the path referred by resrv_instance could
13491c8dbe37SMilos Muzik 		 * disappear in the meantime. Calling mdi_select_path() below
13501c8dbe37SMilos Muzik 		 * is an attempt to find out if the path still exists. It also
13511c8dbe37SMilos Muzik 		 * ensures that the path will be held when the release is sent.
13521c8dbe37SMilos Muzik 		 */
13531c8dbe37SMilos Muzik 		rval = mdi_select_path(cdip, NULL, MDI_SELECT_PATH_INSTANCE,
13541c8dbe37SMilos Muzik 		    (void *)(intptr_t)resrv_instance, &pip);
13551c8dbe37SMilos Muzik 
13561c8dbe37SMilos Muzik 		if ((rval == MDI_SUCCESS) && (pip != NULL)) {
13571c8dbe37SMilos Muzik 			svp_resrv = (scsi_vhci_priv_t *)
13581c8dbe37SMilos Muzik 			    mdi_pi_get_vhci_private(pip);
13591c8dbe37SMilos Muzik 			rel_pkt = scsi_init_pkt(&svp_resrv->svp_psd->sd_address,
13601c8dbe37SMilos Muzik 			    NULL, NULL, CDB_GROUP0,
13611c8dbe37SMilos Muzik 			    sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC,
13621c8dbe37SMilos Muzik 			    NULL);
13631c8dbe37SMilos Muzik 
13641c8dbe37SMilos Muzik 			if (rel_pkt == NULL) {
13651c8dbe37SMilos Muzik 				char	*p_path;
13661c8dbe37SMilos Muzik 
13671c8dbe37SMilos Muzik 				/*
13681c8dbe37SMilos Muzik 				 * This is very unlikely.
13691c8dbe37SMilos Muzik 				 * scsi_init_pkt(SLEEP_FUNC) does not fail
13701c8dbe37SMilos Muzik 				 * because of resources. But in theory it could
13711c8dbe37SMilos Muzik 				 * fail for some other reason. There is not an
13721c8dbe37SMilos Muzik 				 * easy way how to recover though. Log a warning
13731c8dbe37SMilos Muzik 				 * and return.
13741c8dbe37SMilos Muzik 				 */
13751c8dbe37SMilos Muzik 				p_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
13761c8dbe37SMilos Muzik 				vhci_log(CE_WARN, vhci->vhci_dip, "!Sending "
13771c8dbe37SMilos Muzik 				    "RELEASE(6) to %s failed, a potential "
13781c8dbe37SMilos Muzik 				    "reservation conflict ahead.",
13791c8dbe37SMilos Muzik 				    ddi_pathname(mdi_pi_get_phci(pip), p_path));
13801c8dbe37SMilos Muzik 				kmem_free(p_path, MAXPATHLEN);
13811c8dbe37SMilos Muzik 
13821c8dbe37SMilos Muzik 				if (restore_lbp)
13831c8dbe37SMilos Muzik 					(void) mdi_set_lb_policy(cdip, lbp);
13841c8dbe37SMilos Muzik 
13851c8dbe37SMilos Muzik 				/* no need to check pkt_reserve_cmd here */
13861c8dbe37SMilos Muzik 				vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
13871c8dbe37SMilos Muzik 				return (TRAN_FATAL_ERROR);
13881c8dbe37SMilos Muzik 			}
13891c8dbe37SMilos Muzik 
13901c8dbe37SMilos Muzik 			rel_pkt->pkt_cdbp[0] = SCMD_RELEASE;
13911c8dbe37SMilos Muzik 			rel_pkt->pkt_time = 60;
13921c8dbe37SMilos Muzik 
13931c8dbe37SMilos Muzik 			/*
13941c8dbe37SMilos Muzik 			 * Ignore the return value.  If it will fail
13951c8dbe37SMilos Muzik 			 * then most likely it is no longer reserved
13961c8dbe37SMilos Muzik 			 * anyway.
13971c8dbe37SMilos Muzik 			 */
13981c8dbe37SMilos Muzik 			(void) vhci_do_scsi_cmd(rel_pkt);
13991c8dbe37SMilos Muzik 			VHCI_DEBUG(1, (CE_NOTE, NULL,
14001c8dbe37SMilos Muzik 			    "!vhci_scsi_start: path 0x%p, issued SCSI-2"
14011c8dbe37SMilos Muzik 			    " RELEASE\n", (void *)pip));
14021c8dbe37SMilos Muzik 			scsi_destroy_pkt(rel_pkt);
14031c8dbe37SMilos Muzik 			mdi_rele_path(pip);
14041c8dbe37SMilos Muzik 		}
14051c8dbe37SMilos Muzik 	}
14061c8dbe37SMilos Muzik 
14071e1ddd6cScth 	VHCI_INCR_PATH_CMDCOUNT(svp);
14081e1ddd6cScth 
14091e1ddd6cScth 	/*
14101e1ddd6cScth 	 * Ensure that no other IOs raced ahead, while a RESERVE cmd was
14111e1ddd6cScth 	 * QUIESCING the same lun.
14121e1ddd6cScth 	 */
14131e1ddd6cScth 	if ((!pkt_reserve_cmd) &&
14141e1ddd6cScth 	    ((vlun->svl_flags & VLUN_QUIESCED_FLG) == VLUN_QUIESCED_FLG)) {
14151e1ddd6cScth 		VHCI_DECR_PATH_CMDCOUNT(svp);
14161e1ddd6cScth 		goto pkt_cleanup;
14171e1ddd6cScth 	}
14181e1ddd6cScth 
14191e1ddd6cScth 	if ((pkt->pkt_cdbp[0] == SCMD_PRIN) ||
14201e1ddd6cScth 	    (pkt->pkt_cdbp[0] == SCMD_PROUT)) {
14211e1ddd6cScth 		/*
14221e1ddd6cScth 		 * currently this thread only handles running PGR
14231e1ddd6cScth 		 * commands, so don't bother creating it unless
14241e1ddd6cScth 		 * something interesting is going to happen (like
14251e1ddd6cScth 		 * either a PGR out, or a PGR in with enough space
14261e1ddd6cScth 		 * to hold the keys that are getting returned)
14271e1ddd6cScth 		 */
14281e1ddd6cScth 		mutex_enter(&vlun->svl_mutex);
14291e1ddd6cScth 		if (((vlun->svl_flags & VLUN_TASK_D_ALIVE_FLG) == 0) &&
14301e1ddd6cScth 		    (pkt->pkt_cdbp[0] == SCMD_PROUT)) {
14311e1ddd6cScth 			vlun->svl_taskq = taskq_create("vlun_pgr_task_daemon",
14321e1ddd6cScth 			    1, MINCLSYSPRI, 1, 4, 0);
14331e1ddd6cScth 			vlun->svl_flags |= VLUN_TASK_D_ALIVE_FLG;
14341e1ddd6cScth 		}
14351e1ddd6cScth 		mutex_exit(&vlun->svl_mutex);
14361e1ddd6cScth 		if ((pkt->pkt_cdbp[0] == SCMD_PROUT) &&
14371e1ddd6cScth 		    (((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
14380205780bSrralphs 		    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE))) {
14391e1ddd6cScth 			if (rval = vhci_pgr_register_start(vlun, pkt)) {
14401e1ddd6cScth 				/* an error */
14411e1ddd6cScth 				sema_v(&vlun->svl_pgr_sema);
14421e1ddd6cScth 				return (rval);
14431e1ddd6cScth 			}
14441e1ddd6cScth 		}
14451e1ddd6cScth 	}
14461e1ddd6cScth 
14471e1ddd6cScth 	/*
14481e1ddd6cScth 	 * SCSI-II RESERVE cmd is not expected in polled mode.
14491e1ddd6cScth 	 * If this changes it needs to be handled for the polled scenario.
14501e1ddd6cScth 	 */
14511e1ddd6cScth 	flags = vpkt->vpkt_hba_pkt->pkt_flags;
1452602ca9eaScth 
1453602ca9eaScth 	/*
1454602ca9eaScth 	 * Set the path_instance *before* sending the scsi_pkt down the path
1455602ca9eaScth 	 * to mpxio's pHCI so that additional path abstractions at a pHCI
1456602ca9eaScth 	 * level (like maybe iSCSI at some point in the future) can update
1457602ca9eaScth 	 * the path_instance.
1458602ca9eaScth 	 */
1459602ca9eaScth 	if (scsi_pkt_allocated_correctly(vpkt->vpkt_hba_pkt))
1460602ca9eaScth 		vpkt->vpkt_hba_pkt->pkt_path_instance =
1461602ca9eaScth 		    mdi_pi_get_path_instance(vpkt->vpkt_path);
1462602ca9eaScth 
14631e1ddd6cScth 	rval = scsi_transport(vpkt->vpkt_hba_pkt);
14641e1ddd6cScth 	if (rval == TRAN_ACCEPT) {
14651e1ddd6cScth 		if (flags & FLAG_NOINTR) {
14661e1ddd6cScth 			struct scsi_pkt *tpkt = vpkt->vpkt_tgt_pkt;
14671e1ddd6cScth 			struct scsi_pkt *pkt = vpkt->vpkt_hba_pkt;
14681e1ddd6cScth 
14691e1ddd6cScth 			ASSERT(tpkt != NULL);
14701e1ddd6cScth 			*(tpkt->pkt_scbp) = *(pkt->pkt_scbp);
14711e1ddd6cScth 			tpkt->pkt_resid = pkt->pkt_resid;
14721e1ddd6cScth 			tpkt->pkt_state = pkt->pkt_state;
14731e1ddd6cScth 			tpkt->pkt_statistics = pkt->pkt_statistics;
14741e1ddd6cScth 			tpkt->pkt_reason = pkt->pkt_reason;
14751e1ddd6cScth 
14761e1ddd6cScth 			if ((*(pkt->pkt_scbp) == STATUS_CHECK) &&
14771e1ddd6cScth 			    (pkt->pkt_state & STATE_ARQ_DONE)) {
14781e1ddd6cScth 				bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
14791e1ddd6cScth 				    vpkt->vpkt_tgt_init_scblen);
14801e1ddd6cScth 			}
14811e1ddd6cScth 
14821e1ddd6cScth 			VHCI_DECR_PATH_CMDCOUNT(svp);
14831e1ddd6cScth 			if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
14841e1ddd6cScth 				scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
14851e1ddd6cScth 				vpkt->vpkt_hba_pkt = NULL;
14861e1ddd6cScth 				if (vpkt->vpkt_path) {
14871e1ddd6cScth 					mdi_rele_path(vpkt->vpkt_path);
14881e1ddd6cScth 					vpkt->vpkt_path = NULL;
14891e1ddd6cScth 				}
14901e1ddd6cScth 			}
14911e1ddd6cScth 			/*
14921e1ddd6cScth 			 * This path will not automatically retry pkts
14931e1ddd6cScth 			 * internally, therefore, vpkt_org_vpkt should
14941e1ddd6cScth 			 * never be set.
14951e1ddd6cScth 			 */
14961e1ddd6cScth 			ASSERT(vpkt->vpkt_org_vpkt == NULL);
14979c57abc8Ssrivijitha dugganapalli 			scsi_hba_pkt_comp(tpkt);
14981e1ddd6cScth 		}
14991e1ddd6cScth 		return (rval);
15001e1ddd6cScth 	} else if ((pkt->pkt_cdbp[0] == SCMD_PROUT) &&
15011e1ddd6cScth 	    (((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
15021e1ddd6cScth 	    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE))) {
15031e1ddd6cScth 		/* the command exited with bad status */
15041e1ddd6cScth 		sema_v(&vlun->svl_pgr_sema);
15051e1ddd6cScth 	} else if (vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PRIN) {
15061e1ddd6cScth 		/* the command exited with bad status */
15071e1ddd6cScth 		sema_v(&vlun->svl_pgr_sema);
15081e1ddd6cScth 	} else if (pkt_reserve_cmd) {
15091e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
15101e1ddd6cScth 		    "!vhci_scsi_start: reserve failed vlun 0x%p",
15111e1ddd6cScth 		    (void *)vlun));
15121e1ddd6cScth 		vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
15131e1ddd6cScth 		if (restore_lbp)
15141e1ddd6cScth 			(void) mdi_set_lb_policy(cdip, lbp);
15151e1ddd6cScth 	}
15161e1ddd6cScth 
15171e1ddd6cScth 	ASSERT(vpkt->vpkt_hba_pkt != NULL);
15181e1ddd6cScth 	VHCI_DECR_PATH_CMDCOUNT(svp);
15191e1ddd6cScth 
15201e1ddd6cScth 	/* Do not destroy phci packet information for PKT_DMA_PARTIAL */
15211e1ddd6cScth 	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
15221e1ddd6cScth 		scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
15231e1ddd6cScth 		vpkt->vpkt_hba_pkt = NULL;
15241e1ddd6cScth 		if (vpkt->vpkt_path) {
15251e1ddd6cScth 			MDI_PI_ERRSTAT(vpkt->vpkt_path, MDI_PI_TRANSERR);
15261e1ddd6cScth 			mdi_rele_path(vpkt->vpkt_path);
15271e1ddd6cScth 			vpkt->vpkt_path = NULL;
15281e1ddd6cScth 		}
15291e1ddd6cScth 	}
15301e1ddd6cScth 	return (TRAN_BUSY);
15311e1ddd6cScth }
15321e1ddd6cScth 
15331e1ddd6cScth /*
15341e1ddd6cScth  * Function name : vhci_scsi_reset()
15351e1ddd6cScth  *
15361e1ddd6cScth  * Return Values : 0 - reset failed
15371e1ddd6cScth  *		   1 - reset succeeded
15381e1ddd6cScth  */
15391e1ddd6cScth 
15401e1ddd6cScth /* ARGSUSED */
15411e1ddd6cScth static int
vhci_scsi_reset(struct scsi_address * ap,int level)15421e1ddd6cScth vhci_scsi_reset(struct scsi_address *ap, int level)
15431e1ddd6cScth {
15441e1ddd6cScth 	int rval = 0;
15451e1ddd6cScth 
15461e1ddd6cScth 	cmn_err(CE_WARN, "!vhci_scsi_reset 0x%x", level);
15471e1ddd6cScth 	if ((level == RESET_TARGET) || (level == RESET_LUN)) {
15481e1ddd6cScth 		return (vhci_scsi_reset_target(ap, level, TRUE));
15491e1ddd6cScth 	} else if (level == RESET_ALL) {
15501e1ddd6cScth 		return (vhci_scsi_reset_bus(ap));
15511e1ddd6cScth 	}
15521e1ddd6cScth 
15531e1ddd6cScth 	return (rval);
15541e1ddd6cScth }
15551e1ddd6cScth 
15561e1ddd6cScth /*
15571e1ddd6cScth  * vhci_recovery_reset:
15581e1ddd6cScth  *	Issues reset to the device
15591e1ddd6cScth  * Input:
15601e1ddd6cScth  *	vlun - vhci lun pointer of the device
15611e1ddd6cScth  *	ap - address of the device
15621e1ddd6cScth  *	select_path:
15631e1ddd6cScth  *		If select_path is FALSE, then the address specified in ap is
15641e1ddd6cScth  *		the path on which reset will be issued.
15651e1ddd6cScth  *		If select_path is TRUE, then path is obtained by calling
15661e1ddd6cScth  *		mdi_select_path.
15671e1ddd6cScth  *
15681e1ddd6cScth  *	recovery_depth:
15691e1ddd6cScth  *		Caller can specify the level of reset.
15701e1ddd6cScth  *		VHCI_DEPTH_LUN -
15711e1ddd6cScth  *			Issues LUN RESET if device supports lun reset.
15721e1ddd6cScth  *		VHCI_DEPTH_TARGET -
15731e1ddd6cScth  *			If Lun Reset fails or the device does not support
15741e1ddd6cScth  *			Lun Reset, issues TARGET RESET
15751e1ddd6cScth  *		VHCI_DEPTH_ALL -
15761e1ddd6cScth  *			If Lun Reset fails or the device does not support
15771e1ddd6cScth  *			Lun Reset, issues TARGET RESET.
15781e1ddd6cScth  *			If TARGET RESET does not succeed, issues Bus Reset.
15791e1ddd6cScth  */
15801e1ddd6cScth 
15811e1ddd6cScth static int
vhci_recovery_reset(scsi_vhci_lun_t * vlun,struct scsi_address * ap,uint8_t select_path,uint8_t recovery_depth)15821e1ddd6cScth vhci_recovery_reset(scsi_vhci_lun_t *vlun, struct scsi_address *ap,
15831e1ddd6cScth     uint8_t select_path, uint8_t recovery_depth)
15841e1ddd6cScth {
15851e1ddd6cScth 	int	ret = 0;
15861e1ddd6cScth 
15871e1ddd6cScth 	ASSERT(ap != NULL);
15881e1ddd6cScth 
15891e1ddd6cScth 	if (vlun && vlun->svl_support_lun_reset == 1) {
15901e1ddd6cScth 		ret = vhci_scsi_reset_target(ap, RESET_LUN,
15911e1ddd6cScth 		    select_path);
15921e1ddd6cScth 	}
15931e1ddd6cScth 
15941e1ddd6cScth 	recovery_depth--;
15951e1ddd6cScth 
15961e1ddd6cScth 	if ((ret == 0) && recovery_depth) {
15971e1ddd6cScth 		ret = vhci_scsi_reset_target(ap, RESET_TARGET,
15981e1ddd6cScth 		    select_path);
15991e1ddd6cScth 		recovery_depth--;
16001e1ddd6cScth 	}
16011e1ddd6cScth 
16021e1ddd6cScth 	if ((ret == 0) && recovery_depth) {
16031e1ddd6cScth 		(void) scsi_reset(ap, RESET_ALL);
16041e1ddd6cScth 	}
16051e1ddd6cScth 
16061e1ddd6cScth 	return (ret);
16071e1ddd6cScth }
16081e1ddd6cScth 
16091e1ddd6cScth /*
16101e1ddd6cScth  * Note: The scsi_address passed to this routine could be the scsi_address
16111e1ddd6cScth  * for the virtual device or the physical device. No assumptions should be
161253a7b6b6SChris Horne  * made in this routine about the contents of the ap structure.
161353a7b6b6SChris Horne  * Further, note that the child dip would be the dip of the ssd node regardless
161453a7b6b6SChris Horne  * of the scsi_address passed in.
16151e1ddd6cScth  */
16161e1ddd6cScth static int
vhci_scsi_reset_target(struct scsi_address * ap,int level,uint8_t select_path)16171e1ddd6cScth vhci_scsi_reset_target(struct scsi_address *ap, int level, uint8_t select_path)
16181e1ddd6cScth {
16194c06356bSdh142964 	dev_info_t		*vdip, *cdip;
16201e1ddd6cScth 	mdi_pathinfo_t		*pip = NULL;
16211e1ddd6cScth 	mdi_pathinfo_t		*npip = NULL;
16221e1ddd6cScth 	int			rval = -1;
16231e1ddd6cScth 	scsi_vhci_priv_t	*svp = NULL;
16241e1ddd6cScth 	struct scsi_address	*pap = NULL;
16251e1ddd6cScth 	scsi_hba_tran_t		*hba = NULL;
16261e1ddd6cScth 	int			sps;
16271e1ddd6cScth 	struct scsi_vhci	*vhci = NULL;
16281e1ddd6cScth 
16291e1ddd6cScth 	if (select_path != TRUE) {
16301e1ddd6cScth 		ASSERT(ap != NULL);
16311e1ddd6cScth 		if (level == RESET_LUN) {
16321e1ddd6cScth 			hba = ap->a_hba_tran;
16331e1ddd6cScth 			ASSERT(hba != NULL);
16340205780bSrralphs 			return (hba->tran_reset(ap, RESET_LUN));
16351e1ddd6cScth 		}
16361e1ddd6cScth 		return (scsi_reset(ap, level));
16371e1ddd6cScth 	}
16381e1ddd6cScth 
1639341a308cSChris Liu 	cdip = ADDR2DIP(ap);
16401e1ddd6cScth 	ASSERT(cdip != NULL);
16411e1ddd6cScth 	vdip = ddi_get_parent(cdip);
16421e1ddd6cScth 	ASSERT(vdip != NULL);
16431e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
16441e1ddd6cScth 	ASSERT(vhci != NULL);
16451e1ddd6cScth 
16461e1ddd6cScth 	rval = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH, NULL, &pip);
16471e1ddd6cScth 	if ((rval != MDI_SUCCESS) || (pip == NULL)) {
16481e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_scsi_reset_target: "
16491e1ddd6cScth 		    "Unable to get a path, dip 0x%p", (void *)cdip));
16501e1ddd6cScth 		return (0);
16511e1ddd6cScth 	}
16521e1ddd6cScth again:
16531e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
16541e1ddd6cScth 	if (svp == NULL) {
16551e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_scsi_reset_target: "
16561e1ddd6cScth 		    "priv is NULL, pip 0x%p", (void *)pip));
16571e1ddd6cScth 		mdi_rele_path(pip);
16581e1ddd6cScth 		return (0);
16591e1ddd6cScth 	}
16601e1ddd6cScth 
16611e1ddd6cScth 	if (svp->svp_psd == NULL) {
16621e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_scsi_reset_target: "
16631e1ddd6cScth 		    "psd is NULL, pip 0x%p, svp 0x%p",
16641e1ddd6cScth 		    (void *)pip, (void *)svp));
16651e1ddd6cScth 		mdi_rele_path(pip);
16661e1ddd6cScth 		return (0);
16671e1ddd6cScth 	}
16681e1ddd6cScth 
16691e1ddd6cScth 	pap = &svp->svp_psd->sd_address;
16701e1ddd6cScth 	hba = pap->a_hba_tran;
16711e1ddd6cScth 
16721e1ddd6cScth 	ASSERT(pap != NULL);
16731e1ddd6cScth 	ASSERT(hba != NULL);
16741e1ddd6cScth 
16751e1ddd6cScth 	if (hba->tran_reset != NULL) {
16760205780bSrralphs 		if (hba->tran_reset(pap, level) == 0) {
16774c06356bSdh142964 			vhci_log(CE_WARN, vdip, "!%s%d: "
16784c06356bSdh142964 			    "path %s, reset %d failed",
16791e1ddd6cScth 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
16804c06356bSdh142964 			    mdi_pi_spathname(pip), level);
16811e1ddd6cScth 
16821e1ddd6cScth 			/*
16831e1ddd6cScth 			 * Select next path and issue the reset, repeat
16841e1ddd6cScth 			 * until all paths are exhausted
16851e1ddd6cScth 			 */
16861e1ddd6cScth 			sps = mdi_select_path(cdip, NULL,
16871e1ddd6cScth 			    MDI_SELECT_ONLINE_PATH, pip, &npip);
16881e1ddd6cScth 			if ((sps != MDI_SUCCESS) || (npip == NULL)) {
16891e1ddd6cScth 				mdi_rele_path(pip);
16901e1ddd6cScth 				return (0);
16911e1ddd6cScth 			}
16921e1ddd6cScth 			mdi_rele_path(pip);
16931e1ddd6cScth 			pip = npip;
16941e1ddd6cScth 			goto again;
16951e1ddd6cScth 		}
16961e1ddd6cScth 		mdi_rele_path(pip);
16971e1ddd6cScth 		mutex_enter(&vhci->vhci_mutex);
16981e1ddd6cScth 		scsi_hba_reset_notify_callback(&vhci->vhci_mutex,
16991e1ddd6cScth 		    &vhci->vhci_reset_notify_listf);
17001e1ddd6cScth 		mutex_exit(&vhci->vhci_mutex);
17011e1ddd6cScth 		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_scsi_reset_target: "
17021e1ddd6cScth 		    "reset %d sent down pip:%p for cdip:%p\n", level,
17031e1ddd6cScth 		    (void *)pip, (void *)cdip));
17041e1ddd6cScth 		return (1);
17051e1ddd6cScth 	}
17061e1ddd6cScth 	mdi_rele_path(pip);
17071e1ddd6cScth 	return (0);
17081e1ddd6cScth }
17091e1ddd6cScth 
17101e1ddd6cScth 
17111e1ddd6cScth /* ARGSUSED */
17121e1ddd6cScth static int
vhci_scsi_reset_bus(struct scsi_address * ap)17131e1ddd6cScth vhci_scsi_reset_bus(struct scsi_address *ap)
17141e1ddd6cScth {
17151e1ddd6cScth 	return (1);
17161e1ddd6cScth }
17171e1ddd6cScth 
17181e1ddd6cScth 
17191e1ddd6cScth /*
17201e1ddd6cScth  * called by vhci_getcap and vhci_setcap to get and set (respectively)
17211e1ddd6cScth  * SCSI capabilities
17221e1ddd6cScth  */
17231e1ddd6cScth /* ARGSUSED */
17241e1ddd6cScth static int
vhci_commoncap(struct scsi_address * ap,char * cap,int val,int tgtonly,int doset)17251e1ddd6cScth vhci_commoncap(struct scsi_address *ap, char *cap,
17261e1ddd6cScth     int val, int tgtonly, int doset)
17271e1ddd6cScth {
17281e1ddd6cScth 	struct scsi_vhci		*vhci = ADDR2VHCI(ap);
17291e1ddd6cScth 	struct scsi_vhci_lun		*vlun = ADDR2VLUN(ap);
17301e1ddd6cScth 	int			cidx;
17311e1ddd6cScth 	int			rval = 0;
17321e1ddd6cScth 
17331e1ddd6cScth 	if (cap == (char *)0) {
17341e1ddd6cScth 		VHCI_DEBUG(3, (CE_WARN, vhci->vhci_dip,
17351e1ddd6cScth 		    "!vhci_commoncap: invalid arg"));
17361e1ddd6cScth 		return (rval);
17371e1ddd6cScth 	}
17381e1ddd6cScth 
173949569606Sjw149990 	if (vlun == NULL) {
174049569606Sjw149990 		VHCI_DEBUG(3, (CE_WARN, vhci->vhci_dip,
174149569606Sjw149990 		    "!vhci_commoncap: vlun is null"));
174249569606Sjw149990 		return (rval);
174349569606Sjw149990 	}
174449569606Sjw149990 
17451e1ddd6cScth 	if ((cidx = scsi_hba_lookup_capstr(cap)) == -1) {
17461e1ddd6cScth 		return (UNDEFINED);
17471e1ddd6cScth 	}
17481e1ddd6cScth 
17491e1ddd6cScth 	/*
17501e1ddd6cScth 	 * Process setcap request.
17511e1ddd6cScth 	 */
17521e1ddd6cScth 	if (doset) {
17531e1ddd6cScth 		/*
17541e1ddd6cScth 		 * At present, we can only set binary (0/1) values
17551e1ddd6cScth 		 */
17561e1ddd6cScth 		switch (cidx) {
17571e1ddd6cScth 		case SCSI_CAP_ARQ:
17581e1ddd6cScth 			if (val == 0) {
17591e1ddd6cScth 				rval = 0;
17601e1ddd6cScth 			} else {
17611e1ddd6cScth 				rval = 1;
17621e1ddd6cScth 			}
17631e1ddd6cScth 			break;
17641e1ddd6cScth 
17651e1ddd6cScth 		case SCSI_CAP_LUN_RESET:
17661e1ddd6cScth 			if (tgtonly == 0) {
17671e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, vhci->vhci_dip,
17681e1ddd6cScth 				    "scsi_vhci_setcap: "
17691e1ddd6cScth 				    "Returning error since whom = 0"));
17701e1ddd6cScth 				rval = -1;
17711e1ddd6cScth 				break;
17721e1ddd6cScth 			}
17731e1ddd6cScth 			/*
17741e1ddd6cScth 			 * Set the capability accordingly.
17751e1ddd6cScth 			 */
17761e1ddd6cScth 			mutex_enter(&vlun->svl_mutex);
17771e1ddd6cScth 			vlun->svl_support_lun_reset = val;
17781e1ddd6cScth 			rval = val;
17791e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
17801e1ddd6cScth 			break;
17811e1ddd6cScth 
17821e1ddd6cScth 		case SCSI_CAP_SECTOR_SIZE:
17831e1ddd6cScth 			mutex_enter(&vlun->svl_mutex);
17841e1ddd6cScth 			vlun->svl_sector_size = val;
17851e1ddd6cScth 			vlun->svl_setcap_done = 1;
17861e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
17871e1ddd6cScth 			(void) vhci_pHCI_cap(ap, cap, val, tgtonly, NULL);
17881e1ddd6cScth 
17891e1ddd6cScth 			/* Always return success */
17901e1ddd6cScth 			rval = 1;
17911e1ddd6cScth 			break;
17921e1ddd6cScth 
17931e1ddd6cScth 		default:
17941e1ddd6cScth 			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
17951e1ddd6cScth 			    "!vhci_setcap: unsupported %d", cidx));
17961e1ddd6cScth 			rval = UNDEFINED;
17971e1ddd6cScth 			break;
17981e1ddd6cScth 		}
17991e1ddd6cScth 
18001e1ddd6cScth 		VHCI_DEBUG(6, (CE_NOTE, vhci->vhci_dip,
18011e1ddd6cScth 		    "!set cap: cap=%s, val/tgtonly/doset/rval = "
18021e1ddd6cScth 		    "0x%x/0x%x/0x%x/%d\n",
18031e1ddd6cScth 		    cap, val, tgtonly, doset, rval));
18041e1ddd6cScth 
18051e1ddd6cScth 	} else {
18061e1ddd6cScth 		/*
18071e1ddd6cScth 		 * Process getcap request.
18081e1ddd6cScth 		 */
18091e1ddd6cScth 		switch (cidx) {
18101e1ddd6cScth 		case SCSI_CAP_DMA_MAX:
18110205780bSrralphs 			/*
18120205780bSrralphs 			 * For X86 this capability is caught in scsi_ifgetcap().
18130205780bSrralphs 			 * XXX Should this be getting the value from the pHCI?
18140205780bSrralphs 			 */
18151e1ddd6cScth 			rval = (int)VHCI_DMA_MAX_XFER_CAP;
18161e1ddd6cScth 			break;
18171e1ddd6cScth 
18181e1ddd6cScth 		case SCSI_CAP_INITIATOR_ID:
18191e1ddd6cScth 			rval = 0x00;
18201e1ddd6cScth 			break;
18211e1ddd6cScth 
18221e1ddd6cScth 		case SCSI_CAP_ARQ:
18231e1ddd6cScth 		case SCSI_CAP_RESET_NOTIFICATION:
18241e1ddd6cScth 		case SCSI_CAP_TAGGED_QING:
18251e1ddd6cScth 			rval = 1;
18261e1ddd6cScth 			break;
18271e1ddd6cScth 
18281e1ddd6cScth 		case SCSI_CAP_SCSI_VERSION:
18291e1ddd6cScth 			rval = 3;
18301e1ddd6cScth 			break;
18311e1ddd6cScth 
18321e1ddd6cScth 		case SCSI_CAP_INTERCONNECT_TYPE:
18331e1ddd6cScth 			rval = INTERCONNECT_FABRIC;
18341e1ddd6cScth 			break;
18351e1ddd6cScth 
18361e1ddd6cScth 		case SCSI_CAP_LUN_RESET:
18371e1ddd6cScth 			/*
18381e1ddd6cScth 			 * scsi_vhci will always return success for LUN reset.
18391e1ddd6cScth 			 * When request for doing LUN reset comes
18401e1ddd6cScth 			 * through scsi_reset entry point, at that time attempt
18411e1ddd6cScth 			 * will be made to do reset through all the possible
18421e1ddd6cScth 			 * paths.
18431e1ddd6cScth 			 */
18441e1ddd6cScth 			mutex_enter(&vlun->svl_mutex);
18451e1ddd6cScth 			rval = vlun->svl_support_lun_reset;
18461e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
18471e1ddd6cScth 			VHCI_DEBUG(4, (CE_WARN, vhci->vhci_dip,
18481e1ddd6cScth 			    "scsi_vhci_getcap:"
18491e1ddd6cScth 			    "Getting the Lun reset capability %d", rval));
18501e1ddd6cScth 			break;
18511e1ddd6cScth 
18521e1ddd6cScth 		case SCSI_CAP_SECTOR_SIZE:
18531e1ddd6cScth 			mutex_enter(&vlun->svl_mutex);
18541e1ddd6cScth 			rval = vlun->svl_sector_size;
18551e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
18561e1ddd6cScth 			break;
18571e1ddd6cScth 
18580205780bSrralphs 		case SCSI_CAP_CDB_LEN:
18590205780bSrralphs 			rval = VHCI_SCSI_CDB_SIZE;
18600205780bSrralphs 			break;
18610205780bSrralphs 
18620205780bSrralphs 		case SCSI_CAP_DMA_MAX_ARCH:
18630205780bSrralphs 			/*
18640205780bSrralphs 			 * For X86 this capability is caught in scsi_ifgetcap().
18650205780bSrralphs 			 * XXX Should this be getting the value from the pHCI?
18660205780bSrralphs 			 */
18670205780bSrralphs 			rval = 0;
18680205780bSrralphs 			break;
18690205780bSrralphs 
18701e1ddd6cScth 		default:
18711e1ddd6cScth 			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
18721e1ddd6cScth 			    "!vhci_getcap: unsupported %d", cidx));
18731e1ddd6cScth 			rval = UNDEFINED;
18741e1ddd6cScth 			break;
18751e1ddd6cScth 		}
18761e1ddd6cScth 
18771e1ddd6cScth 		VHCI_DEBUG(6, (CE_NOTE, vhci->vhci_dip,
18781e1ddd6cScth 		    "!get cap: cap=%s, val/tgtonly/doset/rval = "
18791e1ddd6cScth 		    "0x%x/0x%x/0x%x/%d\n",
18801e1ddd6cScth 		    cap, val, tgtonly, doset, rval));
18811e1ddd6cScth 	}
18821e1ddd6cScth 	return (rval);
18831e1ddd6cScth }
18841e1ddd6cScth 
18851e1ddd6cScth 
18861e1ddd6cScth /*
18871e1ddd6cScth  * Function name : vhci_scsi_getcap()
18881e1ddd6cScth  *
18891e1ddd6cScth  */
18901e1ddd6cScth static int
vhci_scsi_getcap(struct scsi_address * ap,char * cap,int whom)18911e1ddd6cScth vhci_scsi_getcap(struct scsi_address *ap, char *cap, int whom)
18921e1ddd6cScth {
18931e1ddd6cScth 	return (vhci_commoncap(ap, cap, 0, whom, 0));
18941e1ddd6cScth }
18951e1ddd6cScth 
18961e1ddd6cScth static int
vhci_scsi_setcap(struct scsi_address * ap,char * cap,int value,int whom)18971e1ddd6cScth vhci_scsi_setcap(struct scsi_address *ap, char *cap, int value, int whom)
18981e1ddd6cScth {
18991e1ddd6cScth 	return (vhci_commoncap(ap, cap, value, whom, 1));
19001e1ddd6cScth }
19011e1ddd6cScth 
19021e1ddd6cScth /*
19031e1ddd6cScth  * Function name : vhci_scsi_abort()
19041e1ddd6cScth  */
19051e1ddd6cScth /* ARGSUSED */
19061e1ddd6cScth static int
vhci_scsi_abort(struct scsi_address * ap,struct scsi_pkt * pkt)19071e1ddd6cScth vhci_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
19081e1ddd6cScth {
19091e1ddd6cScth 	return (0);
19101e1ddd6cScth }
19111e1ddd6cScth 
19121e1ddd6cScth /*
19131e1ddd6cScth  * Function name : vhci_scsi_init_pkt
19141e1ddd6cScth  *
19151e1ddd6cScth  * Return Values : pointer to scsi_pkt, or NULL
19161e1ddd6cScth  */
19171e1ddd6cScth /* ARGSUSED */
19181e1ddd6cScth static struct scsi_pkt *
vhci_scsi_init_pkt(struct scsi_address * ap,struct scsi_pkt * pkt,struct buf * bp,int cmdlen,int statuslen,int tgtlen,int flags,int (* callback)(caddr_t),caddr_t arg)19191e1ddd6cScth vhci_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
19201e1ddd6cScth     struct buf *bp, int cmdlen, int statuslen, int tgtlen,
19211e1ddd6cScth     int flags, int (*callback)(caddr_t), caddr_t arg)
19221e1ddd6cScth {
19231e1ddd6cScth 	struct scsi_vhci	*vhci = ADDR2VHCI(ap);
19241e1ddd6cScth 	struct vhci_pkt		*vpkt;
19251e1ddd6cScth 	int			rval;
19261e1ddd6cScth 	int			newpkt = 0;
19271e1ddd6cScth 	struct scsi_pkt		*pktp;
19281e1ddd6cScth 
19291e1ddd6cScth 
19301e1ddd6cScth 	if (pkt == NULL) {
19311e1ddd6cScth 		if (cmdlen > VHCI_SCSI_CDB_SIZE) {
1932f8404091SRamana.Srikanth 			if ((cmdlen != VHCI_SCSI_OSD_CDB_SIZE) ||
1933f8404091SRamana.Srikanth 			    ((flags & VHCI_SCSI_OSD_PKT_FLAGS) !=
1934f8404091SRamana.Srikanth 			    VHCI_SCSI_OSD_PKT_FLAGS)) {
19351e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
19361e1ddd6cScth 				    "!init pkt: cdb size not supported\n"));
19371e1ddd6cScth 				return (NULL);
19381e1ddd6cScth 			}
1939f8404091SRamana.Srikanth 		}
19401e1ddd6cScth 
19411e1ddd6cScth 		pktp = scsi_hba_pkt_alloc(vhci->vhci_dip,
19421e1ddd6cScth 		    ap, cmdlen, statuslen, tgtlen, sizeof (*vpkt), callback,
19431e1ddd6cScth 		    arg);
19441e1ddd6cScth 
19451e1ddd6cScth 		if (pktp == NULL) {
19461e1ddd6cScth 			return (NULL);
19471e1ddd6cScth 		}
19481e1ddd6cScth 
19491e1ddd6cScth 		/* Get the vhci's private structure */
19501e1ddd6cScth 		vpkt = (struct vhci_pkt *)(pktp->pkt_ha_private);
19511e1ddd6cScth 		ASSERT(vpkt);
19521e1ddd6cScth 
19531e1ddd6cScth 		/* Save the target driver's packet */
19541e1ddd6cScth 		vpkt->vpkt_tgt_pkt = pktp;
19551e1ddd6cScth 
19561e1ddd6cScth 		/*
19571e1ddd6cScth 		 * Save pkt_tgt_init_pkt fields if deferred binding
19581e1ddd6cScth 		 * is needed or for other purposes.
19591e1ddd6cScth 		 */
19601e1ddd6cScth 		vpkt->vpkt_tgt_init_pkt_flags = flags;
19611e1ddd6cScth 		vpkt->vpkt_flags = (callback == NULL_FUNC) ? CFLAG_NOWAIT : 0;
19621e1ddd6cScth 		vpkt->vpkt_state = VHCI_PKT_IDLE;
19631e1ddd6cScth 		vpkt->vpkt_tgt_init_cdblen = cmdlen;
19641e1ddd6cScth 		vpkt->vpkt_tgt_init_scblen = statuslen;
19651e1ddd6cScth 		newpkt = 1;
19661e1ddd6cScth 	} else { /* pkt not NULL */
19671e1ddd6cScth 		vpkt = pkt->pkt_ha_private;
19681e1ddd6cScth 	}
19691e1ddd6cScth 
19701e1ddd6cScth 	VHCI_DEBUG(8, (CE_NOTE, NULL, "vhci_scsi_init_pkt "
19711e1ddd6cScth 	    "vpkt %p flags %x\n", (void *)vpkt, flags));
19721e1ddd6cScth 
19731e1ddd6cScth 	/* Clear any stale error flags */
19741e1ddd6cScth 	if (bp) {
19751e1ddd6cScth 		bioerror(bp, 0);
19761e1ddd6cScth 	}
19771e1ddd6cScth 
19781e1ddd6cScth 	vpkt->vpkt_tgt_init_bp = bp;
19791e1ddd6cScth 
19801e1ddd6cScth 	if (flags & PKT_DMA_PARTIAL) {
19811e1ddd6cScth 
19821e1ddd6cScth 		/*
19831e1ddd6cScth 		 * Immediate binding is needed.
19841e1ddd6cScth 		 * Target driver may not set this flag in next invocation.
19851e1ddd6cScth 		 * vhci has to remember this flag was set during first
19861e1ddd6cScth 		 * invocation of vhci_scsi_init_pkt.
19871e1ddd6cScth 		 */
19881e1ddd6cScth 		vpkt->vpkt_flags |= CFLAG_DMA_PARTIAL;
19891e1ddd6cScth 	}
19901e1ddd6cScth 
19911e1ddd6cScth 	if (vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) {
19921e1ddd6cScth 
19931e1ddd6cScth 		/*
19941e1ddd6cScth 		 * Re-initialize some of the target driver packet state
19951e1ddd6cScth 		 * information.
19961e1ddd6cScth 		 */
19971e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_state = 0;
19981e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_statistics = 0;
19991e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_reason = 0;
20001e1ddd6cScth 
20011e1ddd6cScth 		/*
20021e1ddd6cScth 		 * Binding a vpkt->vpkt_path for this IO at init_time.
20031e1ddd6cScth 		 * If an IO error happens later, target driver will clear
20041e1ddd6cScth 		 * this vpkt->vpkt_path binding before re-init IO again.
20051e1ddd6cScth 		 */
20061e1ddd6cScth 		VHCI_DEBUG(8, (CE_NOTE, NULL,
20071e1ddd6cScth 		    "vhci_scsi_init_pkt: calling v_b_t %p, newpkt %d\n",
20081e1ddd6cScth 		    (void *)vpkt, newpkt));
20091e1ddd6cScth 		if (pkt && vpkt->vpkt_hba_pkt) {
20101e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
20111e1ddd6cScth 			    "v_s_i_p calling update_pHCI_pkt resid %ld\n",
20121e1ddd6cScth 			    pkt->pkt_resid));
20131e1ddd6cScth 			vhci_update_pHCI_pkt(vpkt, pkt);
20141e1ddd6cScth 		}
20151e1ddd6cScth 		if (callback == SLEEP_FUNC) {
20161e1ddd6cScth 			rval = vhci_bind_transport(
20171e1ddd6cScth 			    ap, vpkt, flags, callback);
20181e1ddd6cScth 		} else {
20191e1ddd6cScth 			rval = vhci_bind_transport(
20201e1ddd6cScth 			    ap, vpkt, flags, NULL_FUNC);
20211e1ddd6cScth 		}
20221e1ddd6cScth 		VHCI_DEBUG(8, (CE_NOTE, NULL,
20231e1ddd6cScth 		    "vhci_scsi_init_pkt: v_b_t called 0x%p rval 0x%x\n",
20241e1ddd6cScth 		    (void *)vpkt, rval));
20251e1ddd6cScth 		if (bp) {
20261e1ddd6cScth 			if (rval == TRAN_FATAL_ERROR) {
20271e1ddd6cScth 				/*
20281e1ddd6cScth 				 * No paths available. Could not bind
20291e1ddd6cScth 				 * any pHCI. Setting EFAULT as a way
20301e1ddd6cScth 				 * to indicate no DMA is mapped.
20311e1ddd6cScth 				 */
20321e1ddd6cScth 				bioerror(bp, EFAULT);
20331e1ddd6cScth 			} else {
20341e1ddd6cScth 				/*
20351e1ddd6cScth 				 * Do not indicate any pHCI errors to
20361e1ddd6cScth 				 * target driver otherwise.
20371e1ddd6cScth 				 */
20381e1ddd6cScth 				bioerror(bp, 0);
20391e1ddd6cScth 			}
20401e1ddd6cScth 		}
20411e1ddd6cScth 		if (rval != TRAN_ACCEPT) {
20421e1ddd6cScth 			VHCI_DEBUG(8, (CE_NOTE, NULL,
20431e1ddd6cScth 			    "vhci_scsi_init_pkt: "
20441e1ddd6cScth 			    "v_b_t failed 0x%p newpkt %x\n",
20451e1ddd6cScth 			    (void *)vpkt, newpkt));
20461e1ddd6cScth 			if (newpkt) {
20471e1ddd6cScth 				scsi_hba_pkt_free(ap,
20481e1ddd6cScth 				    vpkt->vpkt_tgt_pkt);
20491e1ddd6cScth 			}
20501e1ddd6cScth 			return (NULL);
20511e1ddd6cScth 		}
20521e1ddd6cScth 		ASSERT(vpkt->vpkt_hba_pkt != NULL);
20531e1ddd6cScth 		ASSERT(vpkt->vpkt_path != NULL);
20541e1ddd6cScth 
20551e1ddd6cScth 		/* Update the resid for the target driver */
20561e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_resid =
20571e1ddd6cScth 		    vpkt->vpkt_hba_pkt->pkt_resid;
20581e1ddd6cScth 	}
20591e1ddd6cScth 
20601e1ddd6cScth 	return (vpkt->vpkt_tgt_pkt);
20611e1ddd6cScth }
20621e1ddd6cScth 
20631e1ddd6cScth /*
20641e1ddd6cScth  * Function name : vhci_scsi_destroy_pkt
20651e1ddd6cScth  *
20661e1ddd6cScth  * Return Values : none
20671e1ddd6cScth  */
20681e1ddd6cScth static void
vhci_scsi_destroy_pkt(struct scsi_address * ap,struct scsi_pkt * pkt)20691e1ddd6cScth vhci_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
20701e1ddd6cScth {
20711e1ddd6cScth 	struct vhci_pkt		*vpkt = (struct vhci_pkt *)pkt->pkt_ha_private;
20721e1ddd6cScth 
20731e1ddd6cScth 	VHCI_DEBUG(8, (CE_NOTE, NULL,
20741e1ddd6cScth 	    "vhci_scsi_destroy_pkt: vpkt 0x%p\n", (void *)vpkt));
20751e1ddd6cScth 
20761e1ddd6cScth 	vpkt->vpkt_tgt_init_pkt_flags = 0;
20771e1ddd6cScth 	if (vpkt->vpkt_hba_pkt) {
20781e1ddd6cScth 		scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
20791e1ddd6cScth 		vpkt->vpkt_hba_pkt = NULL;
20801e1ddd6cScth 	}
20811e1ddd6cScth 	if (vpkt->vpkt_path) {
20821e1ddd6cScth 		mdi_rele_path(vpkt->vpkt_path);
20831e1ddd6cScth 		vpkt->vpkt_path = NULL;
20841e1ddd6cScth 	}
20851e1ddd6cScth 
20861e1ddd6cScth 	ASSERT(vpkt->vpkt_state != VHCI_PKT_ISSUED);
20871e1ddd6cScth 	scsi_hba_pkt_free(ap, vpkt->vpkt_tgt_pkt);
20881e1ddd6cScth }
20891e1ddd6cScth 
20901e1ddd6cScth /*
20911e1ddd6cScth  * Function name : vhci_scsi_dmafree()
20921e1ddd6cScth  *
20931e1ddd6cScth  * Return Values : none
20941e1ddd6cScth  */
20951e1ddd6cScth /*ARGSUSED*/
20961e1ddd6cScth static void
vhci_scsi_dmafree(struct scsi_address * ap,struct scsi_pkt * pkt)20971e1ddd6cScth vhci_scsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
20981e1ddd6cScth {
20991e1ddd6cScth 	struct vhci_pkt	*vpkt = (struct vhci_pkt *)pkt->pkt_ha_private;
21001e1ddd6cScth 
21011e1ddd6cScth 	VHCI_DEBUG(6, (CE_NOTE, NULL,
21021e1ddd6cScth 	    "vhci_scsi_dmafree: vpkt 0x%p\n", (void *)vpkt));
21031e1ddd6cScth 
21041e1ddd6cScth 	ASSERT(vpkt != NULL);
21051e1ddd6cScth 	if (vpkt->vpkt_hba_pkt) {
21061e1ddd6cScth 		scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
21071e1ddd6cScth 		vpkt->vpkt_hba_pkt = NULL;
21081e1ddd6cScth 	}
21091e1ddd6cScth 	if (vpkt->vpkt_path) {
21101e1ddd6cScth 		mdi_rele_path(vpkt->vpkt_path);
21111e1ddd6cScth 		vpkt->vpkt_path = NULL;
21121e1ddd6cScth 	}
21131e1ddd6cScth }
21141e1ddd6cScth 
21151e1ddd6cScth /*
21161e1ddd6cScth  * Function name : vhci_scsi_sync_pkt()
21171e1ddd6cScth  *
21181e1ddd6cScth  * Return Values : none
21191e1ddd6cScth  */
21201e1ddd6cScth /*ARGSUSED*/
21211e1ddd6cScth static void
vhci_scsi_sync_pkt(struct scsi_address * ap,struct scsi_pkt * pkt)21221e1ddd6cScth vhci_scsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
21231e1ddd6cScth {
21241e1ddd6cScth 	struct vhci_pkt	*vpkt = (struct vhci_pkt *)pkt->pkt_ha_private;
21251e1ddd6cScth 
21261e1ddd6cScth 	ASSERT(vpkt != NULL);
21271e1ddd6cScth 	if (vpkt->vpkt_hba_pkt) {
21281e1ddd6cScth 		scsi_sync_pkt(vpkt->vpkt_hba_pkt);
21291e1ddd6cScth 	}
21301e1ddd6cScth }
21311e1ddd6cScth 
21321e1ddd6cScth /*
21331e1ddd6cScth  * routine for reset notification setup, to register or cancel.
21341e1ddd6cScth  */
21351e1ddd6cScth static int
vhci_scsi_reset_notify(struct scsi_address * ap,int flag,void (* callback)(caddr_t),caddr_t arg)21361e1ddd6cScth vhci_scsi_reset_notify(struct scsi_address *ap, int flag,
21371e1ddd6cScth     void (*callback)(caddr_t), caddr_t arg)
21381e1ddd6cScth {
21391e1ddd6cScth 	struct scsi_vhci *vhci = ADDR2VHCI(ap);
21401e1ddd6cScth 	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
21411e1ddd6cScth 	    &vhci->vhci_mutex, &vhci->vhci_reset_notify_listf));
21421e1ddd6cScth }
21431e1ddd6cScth 
21441e1ddd6cScth static int
vhci_scsi_get_name_bus_addr(struct scsi_device * sd,char * name,int len,int bus_addr)21451e1ddd6cScth vhci_scsi_get_name_bus_addr(struct scsi_device *sd,
21461e1ddd6cScth     char *name, int len, int bus_addr)
21471e1ddd6cScth {
21481e1ddd6cScth 	dev_info_t		*cdip;
21491e1ddd6cScth 	char			*guid;
21501e1ddd6cScth 	scsi_vhci_lun_t		*vlun;
21511e1ddd6cScth 
21521e1ddd6cScth 	ASSERT(sd != NULL);
21531e1ddd6cScth 	ASSERT(name != NULL);
21541e1ddd6cScth 
21554c06356bSdh142964 	*name = 0;
21561e1ddd6cScth 	cdip = sd->sd_dev;
21571e1ddd6cScth 
21581e1ddd6cScth 	ASSERT(cdip != NULL);
21591e1ddd6cScth 
21604c06356bSdh142964 	if (mdi_component_is_client(cdip, NULL) != MDI_SUCCESS)
21611e1ddd6cScth 		return (1);
21621e1ddd6cScth 
21631e1ddd6cScth 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip, PROPFLAGS,
21644c06356bSdh142964 	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS)
21651e1ddd6cScth 		return (1);
21661e1ddd6cScth 
21674c06356bSdh142964 	/*
21684c06356bSdh142964 	 * Message is "sd# at scsi_vhci0: unit-address <guid>: <bus_addr>".
21694c06356bSdh142964 	 *	<guid>		bus_addr argument == 0
21704c06356bSdh142964 	 *	<bus_addr>	bus_addr argument != 0
21714c06356bSdh142964 	 * Since the <guid> is already provided with unit-address, we just
21724c06356bSdh142964 	 * provide failover module in <bus_addr> to keep output shorter.
21734c06356bSdh142964 	 */
21741e1ddd6cScth 	vlun = ADDR2VLUN(&sd->sd_address);
21754c06356bSdh142964 	if (bus_addr == 0) {
21764c06356bSdh142964 		/* report the guid:  */
21771e1ddd6cScth 		(void) snprintf(name, len, "g%s", guid);
21784c06356bSdh142964 	} else if (vlun && vlun->svl_fops_name) {
21794c06356bSdh142964 		/* report the name of the failover module */
21804c06356bSdh142964 		(void) snprintf(name, len, "%s", vlun->svl_fops_name);
21811e1ddd6cScth 	}
21821e1ddd6cScth 
21831e1ddd6cScth 	ddi_prop_free(guid);
21841e1ddd6cScth 	return (1);
21851e1ddd6cScth }
21861e1ddd6cScth 
21871e1ddd6cScth static int
vhci_scsi_get_bus_addr(struct scsi_device * sd,char * name,int len)21881e1ddd6cScth vhci_scsi_get_bus_addr(struct scsi_device *sd, char *name, int len)
21891e1ddd6cScth {
21901e1ddd6cScth 	return (vhci_scsi_get_name_bus_addr(sd, name, len, 1));
21911e1ddd6cScth }
21921e1ddd6cScth 
21931e1ddd6cScth static int
vhci_scsi_get_name(struct scsi_device * sd,char * name,int len)21941e1ddd6cScth vhci_scsi_get_name(struct scsi_device *sd, char *name, int len)
21951e1ddd6cScth {
21961e1ddd6cScth 	return (vhci_scsi_get_name_bus_addr(sd, name, len, 0));
21971e1ddd6cScth }
21981e1ddd6cScth 
21991e1ddd6cScth /*
22001e1ddd6cScth  * Return a pointer to the guid part of the devnm.
22011e1ddd6cScth  * devnm format is "nodename@busaddr", busaddr format is "gGUID".
22021e1ddd6cScth  */
22031e1ddd6cScth static char *
vhci_devnm_to_guid(char * devnm)22041e1ddd6cScth vhci_devnm_to_guid(char *devnm)
22051e1ddd6cScth {
22061e1ddd6cScth 	char *cp = devnm;
22071e1ddd6cScth 
22081e1ddd6cScth 	if (devnm == NULL)
22091e1ddd6cScth 		return (NULL);
22101e1ddd6cScth 
22111e1ddd6cScth 	while (*cp != '\0' && *cp != '@')
22121e1ddd6cScth 		cp++;
22131e1ddd6cScth 	if (*cp == '@' && *(cp + 1) == 'g')
22141e1ddd6cScth 		return (cp + 2);
22151e1ddd6cScth 	return (NULL);
22161e1ddd6cScth }
22171e1ddd6cScth 
22181e1ddd6cScth static int
vhci_bind_transport(struct scsi_address * ap,struct vhci_pkt * vpkt,int flags,int (* func)(caddr_t))22191e1ddd6cScth vhci_bind_transport(struct scsi_address *ap, struct vhci_pkt *vpkt, int flags,
22201e1ddd6cScth     int (*func)(caddr_t))
22211e1ddd6cScth {
22221e1ddd6cScth 	struct scsi_vhci	*vhci = ADDR2VHCI(ap);
22231e1ddd6cScth 	dev_info_t		*cdip = ADDR2DIP(ap);
22241e1ddd6cScth 	mdi_pathinfo_t		*pip = NULL;
22251e1ddd6cScth 	mdi_pathinfo_t		*npip = NULL;
22261e1ddd6cScth 	scsi_vhci_priv_t	*svp = NULL;
22271e1ddd6cScth 	struct scsi_device	*psd = NULL;
22281e1ddd6cScth 	struct scsi_address	*address = NULL;
22291e1ddd6cScth 	struct scsi_pkt		*pkt = NULL;
22301e1ddd6cScth 	int			rval = -1;
22311e1ddd6cScth 	int			pgr_sema_held = 0;
22321e1ddd6cScth 	int			held;
22331e1ddd6cScth 	int			mps_flag = MDI_SELECT_ONLINE_PATH;
22341e1ddd6cScth 	struct scsi_vhci_lun	*vlun;
2235b98fa0e9Srralphs 	int			path_instance = 0;
22361e1ddd6cScth 
22371e1ddd6cScth 	vlun = ADDR2VLUN(ap);
22381e1ddd6cScth 	ASSERT(vlun != 0);
22391e1ddd6cScth 
22401e1ddd6cScth 	if ((vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PROUT) &&
22411e1ddd6cScth 	    (((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
22421e1ddd6cScth 	    VHCI_PROUT_REGISTER) ||
22431e1ddd6cScth 	    ((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
22441e1ddd6cScth 	    VHCI_PROUT_R_AND_IGNORE))) {
22451e1ddd6cScth 		if (!sema_tryp(&vlun->svl_pgr_sema))
22461e1ddd6cScth 			return (TRAN_BUSY);
22471e1ddd6cScth 		pgr_sema_held = 1;
22481e1ddd6cScth 		if (vlun->svl_first_path != NULL) {
22491e1ddd6cScth 			rval = mdi_select_path(cdip, NULL,
22501e1ddd6cScth 			    MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH,
22511e1ddd6cScth 			    NULL, &pip);
22521e1ddd6cScth 			if ((rval != MDI_SUCCESS) || (pip == NULL)) {
22531e1ddd6cScth 				VHCI_DEBUG(4, (CE_NOTE, NULL,
22541e1ddd6cScth 				    "vhci_bind_transport: path select fail\n"));
22551e1ddd6cScth 			} else {
22561e1ddd6cScth 				npip = pip;
22571e1ddd6cScth 				do {
22581e1ddd6cScth 					if (npip == vlun->svl_first_path) {
22591e1ddd6cScth 						VHCI_DEBUG(4, (CE_NOTE, NULL,
22601e1ddd6cScth 						    "vhci_bind_transport: "
22611e1ddd6cScth 						    "valid first path 0x%p\n",
22621e1ddd6cScth 						    (void *)
22631e1ddd6cScth 						    vlun->svl_first_path));
22641e1ddd6cScth 						pip = vlun->svl_first_path;
22651e1ddd6cScth 						goto bind_path;
22661e1ddd6cScth 					}
22671e1ddd6cScth 					pip = npip;
22681e1ddd6cScth 					rval = mdi_select_path(cdip, NULL,
22691e1ddd6cScth 					    MDI_SELECT_ONLINE_PATH |
22701e1ddd6cScth 					    MDI_SELECT_STANDBY_PATH,
22711e1ddd6cScth 					    pip, &npip);
22721e1ddd6cScth 					mdi_rele_path(pip);
22731e1ddd6cScth 				} while ((rval == MDI_SUCCESS) &&
22741e1ddd6cScth 				    (npip != NULL));
22751e1ddd6cScth 			}
22761e1ddd6cScth 		}
22771e1ddd6cScth 
22781e1ddd6cScth 		if (vlun->svl_first_path) {
22791e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
22801e1ddd6cScth 			    "vhci_bind_transport: invalid first path 0x%p\n",
22811e1ddd6cScth 			    (void *)vlun->svl_first_path));
22821e1ddd6cScth 			vlun->svl_first_path = NULL;
22831e1ddd6cScth 		}
22841e1ddd6cScth 	} else if (vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PRIN) {
22851e1ddd6cScth 		if ((vpkt->vpkt_state & VHCI_PKT_THRU_TASKQ) == 0) {
22861e1ddd6cScth 			if (!sema_tryp(&vlun->svl_pgr_sema))
22871e1ddd6cScth 				return (TRAN_BUSY);
22881e1ddd6cScth 		}
22891e1ddd6cScth 		pgr_sema_held = 1;
22901e1ddd6cScth 	}
22911e1ddd6cScth 
22921e1ddd6cScth 	/*
22931e1ddd6cScth 	 * If the path is already bound for PKT_PARTIAL_DMA case,
22941e1ddd6cScth 	 * try to use the same path.
22951e1ddd6cScth 	 */
22961e1ddd6cScth 	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) && vpkt->vpkt_path) {
22971e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
22981e1ddd6cScth 		    "vhci_bind_transport: PKT_PARTIAL_DMA "
22991e1ddd6cScth 		    "vpkt 0x%p, path 0x%p\n",
23001e1ddd6cScth 		    (void *)vpkt, (void *)vpkt->vpkt_path));
23011e1ddd6cScth 		pip = vpkt->vpkt_path;
23021e1ddd6cScth 		goto bind_path;
23031e1ddd6cScth 	}
23041e1ddd6cScth 
23051e1ddd6cScth 	/*
23061cb30f30Sgap 	 * Get path_instance. Non-zero with FLAG_PKT_PATH_INSTANCE set
23071cb30f30Sgap 	 * indicates that mdi_select_path should be called to select a
23081cb30f30Sgap 	 * specific instance.
2309602ca9eaScth 	 *
2310602ca9eaScth 	 * NB: Condition pkt_path_instance reference on proper allocation.
2311602ca9eaScth 	 */
2312b98fa0e9Srralphs 	if ((vpkt->vpkt_tgt_pkt->pkt_flags & FLAG_PKT_PATH_INSTANCE) &&
2313b98fa0e9Srralphs 	    scsi_pkt_allocated_correctly(vpkt->vpkt_tgt_pkt)) {
2314602ca9eaScth 		path_instance = vpkt->vpkt_tgt_pkt->pkt_path_instance;
2315b98fa0e9Srralphs 	}
2316602ca9eaScth 
2317602ca9eaScth 	/*
23181e1ddd6cScth 	 * If reservation is active bind the transport directly to the pip
23191e1ddd6cScth 	 * with the reservation.
23201e1ddd6cScth 	 */
23211e1ddd6cScth 	if (vpkt->vpkt_hba_pkt == NULL) {
23221e1ddd6cScth 		if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
23231e1ddd6cScth 			if (MDI_PI_IS_ONLINE(vlun->svl_resrv_pip)) {
23241e1ddd6cScth 				pip = vlun->svl_resrv_pip;
23251e1ddd6cScth 				mdi_hold_path(pip);
23261e1ddd6cScth 				vlun->svl_waiting_for_activepath = 0;
23271e1ddd6cScth 				rval = MDI_SUCCESS;
23281e1ddd6cScth 				goto bind_path;
23291e1ddd6cScth 			} else {
23301e1ddd6cScth 				if (pgr_sema_held) {
23311e1ddd6cScth 					sema_v(&vlun->svl_pgr_sema);
23321e1ddd6cScth 				}
23331e1ddd6cScth 				return (TRAN_BUSY);
23341e1ddd6cScth 			}
23351e1ddd6cScth 		}
23361e1ddd6cScth try_again:
2337602ca9eaScth 		rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp,
2338602ca9eaScth 		    path_instance ? MDI_SELECT_PATH_INSTANCE : 0,
2339602ca9eaScth 		    (void *)(intptr_t)path_instance, &pip);
23401e1ddd6cScth 		if (rval == MDI_BUSY) {
23411e1ddd6cScth 			if (pgr_sema_held) {
23421e1ddd6cScth 				sema_v(&vlun->svl_pgr_sema);
23431e1ddd6cScth 			}
23441e1ddd6cScth 			return (TRAN_BUSY);
23451e1ddd6cScth 		} else if (rval == MDI_DEVI_ONLINING) {
23461e1ddd6cScth 			/*
23471e1ddd6cScth 			 * if we are here then we are in the midst of
23481e1ddd6cScth 			 * an attach/probe of the client device.
23491e1ddd6cScth 			 * We attempt to bind to ONLINE path if available,
23501e1ddd6cScth 			 * else it is OK to bind to a STANDBY path (instead
23511e1ddd6cScth 			 * of triggering a failover) because IO associated
23521e1ddd6cScth 			 * with attach/probe (eg. INQUIRY, block 0 read)
23531e1ddd6cScth 			 * are completed by targets even on passive paths
23541e1ddd6cScth 			 * If no ONLINE paths available, it is important
23551e1ddd6cScth 			 * to set svl_waiting_for_activepath for two
23561e1ddd6cScth 			 * reasons: (1) avoid sense analysis in the
23571e1ddd6cScth 			 * "external failure detection" codepath in
23581e1ddd6cScth 			 * vhci_intr().  Failure to do so will result in
23591e1ddd6cScth 			 * infinite loop (unless an ONLINE path becomes
23601e1ddd6cScth 			 * available at some point) (2) avoid
23611e1ddd6cScth 			 * unnecessary failover (see "---Waiting For Active
23621e1ddd6cScth 			 * Path---" comment below).
23631e1ddd6cScth 			 */
23641e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!%p in onlining "
23651e1ddd6cScth 			    "state\n", (void *)cdip));
23661e1ddd6cScth 			pip = NULL;
23671e1ddd6cScth 			rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp,
23681e1ddd6cScth 			    mps_flag, NULL, &pip);
23691e1ddd6cScth 			if ((rval != MDI_SUCCESS) || (pip == NULL)) {
23701e1ddd6cScth 				if (vlun->svl_waiting_for_activepath == 0) {
23711e1ddd6cScth 					vlun->svl_waiting_for_activepath = 1;
2372aa6577ebSJosef 'Jeff' Sipek 					vlun->svl_wfa_time = gethrtime();
23731e1ddd6cScth 				}
23741e1ddd6cScth 				mps_flag |= MDI_SELECT_STANDBY_PATH;
23751e1ddd6cScth 				rval = mdi_select_path(cdip,
23761e1ddd6cScth 				    vpkt->vpkt_tgt_init_bp,
23771e1ddd6cScth 				    mps_flag, NULL, &pip);
23781e1ddd6cScth 				if ((rval != MDI_SUCCESS) || (pip == NULL)) {
23791e1ddd6cScth 					if (pgr_sema_held) {
23801e1ddd6cScth 						sema_v(&vlun->svl_pgr_sema);
23811e1ddd6cScth 					}
23821e1ddd6cScth 					return (TRAN_FATAL_ERROR);
23831e1ddd6cScth 				}
23841e1ddd6cScth 				goto bind_path;
23851e1ddd6cScth 			}
23861cba8b6cSRandall Ralphs 		} else if ((rval == MDI_FAILURE) ||
23871cba8b6cSRandall Ralphs 		    ((rval == MDI_NOPATH) && (path_instance))) {
23881e1ddd6cScth 			if (pgr_sema_held) {
23891e1ddd6cScth 				sema_v(&vlun->svl_pgr_sema);
23901e1ddd6cScth 			}
23911e1ddd6cScth 			return (TRAN_FATAL_ERROR);
23921e1ddd6cScth 		}
23931e1ddd6cScth 
23941e1ddd6cScth 		if ((pip == NULL) || (rval == MDI_NOPATH)) {
23951e1ddd6cScth 			while (vlun->svl_waiting_for_activepath) {
23961e1ddd6cScth 				/*
23971e1ddd6cScth 				 * ---Waiting For Active Path---
23981e1ddd6cScth 				 * This device was discovered across a
23991e1ddd6cScth 				 * passive path; lets wait for a little
24001e1ddd6cScth 				 * bit, hopefully an active path will
24011e1ddd6cScth 				 * show up obviating the need for a
24021e1ddd6cScth 				 * failover
24031e1ddd6cScth 				 */
2404aa6577ebSJosef 'Jeff' Sipek 				if ((gethrtime() - vlun->svl_wfa_time) >=
2405aa6577ebSJosef 'Jeff' Sipek 				    (60 * NANOSEC)) {
24061e1ddd6cScth 					vlun->svl_waiting_for_activepath = 0;
24071e1ddd6cScth 				} else {
24081e1ddd6cScth 					drv_usecwait(1000);
24091e1ddd6cScth 					if (vlun->svl_waiting_for_activepath
24101e1ddd6cScth 					    == 0) {
24111e1ddd6cScth 						/*
24121e1ddd6cScth 						 * an active path has come
24131e1ddd6cScth 						 * online!
24141e1ddd6cScth 						 */
24151e1ddd6cScth 						goto try_again;
24161e1ddd6cScth 					}
24171e1ddd6cScth 				}
24181e1ddd6cScth 			}
24191e1ddd6cScth 			VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
24201e1ddd6cScth 			if (!held) {
24211e1ddd6cScth 				VHCI_DEBUG(4, (CE_NOTE, NULL,
24221e1ddd6cScth 				    "!Lun not held\n"));
24231e1ddd6cScth 				if (pgr_sema_held) {
24241e1ddd6cScth 					sema_v(&vlun->svl_pgr_sema);
24251e1ddd6cScth 				}
24261e1ddd6cScth 				return (TRAN_BUSY);
24271e1ddd6cScth 			}
24281e1ddd6cScth 			/*
24291e1ddd6cScth 			 * now that the LUN is stable, one last check
24301e1ddd6cScth 			 * to make sure no other changes sneaked in
24311e1ddd6cScth 			 * (like a path coming online or a
24321e1ddd6cScth 			 * failover initiated by another thread)
24331e1ddd6cScth 			 */
24341e1ddd6cScth 			pip = NULL;
24351e1ddd6cScth 			rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp,
24361e1ddd6cScth 			    0, NULL, &pip);
24371e1ddd6cScth 			if (pip != NULL) {
24381e1ddd6cScth 				VHCI_RELEASE_LUN(vlun);
24391e1ddd6cScth 				vlun->svl_waiting_for_activepath = 0;
24401e1ddd6cScth 				goto bind_path;
24411e1ddd6cScth 			}
24421e1ddd6cScth 
24431e1ddd6cScth 			/*
24441e1ddd6cScth 			 * Check if there is an ONLINE path OR a STANDBY path
24451e1ddd6cScth 			 * available. If none is available, do not attempt
24461e1ddd6cScth 			 * to do a failover, just return a fatal error at this
24471e1ddd6cScth 			 * point.
24481e1ddd6cScth 			 */
24491e1ddd6cScth 			npip = NULL;
24501e1ddd6cScth 			rval = mdi_select_path(cdip, NULL,
24511e1ddd6cScth 			    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
24521e1ddd6cScth 			    NULL, &npip);
24531e1ddd6cScth 			if ((npip == NULL) || (rval != MDI_SUCCESS)) {
24541e1ddd6cScth 				/*
24551e1ddd6cScth 				 * No paths available, jus return FATAL error.
24561e1ddd6cScth 				 */
24571e1ddd6cScth 				VHCI_RELEASE_LUN(vlun);
24581e1ddd6cScth 				if (pgr_sema_held) {
24591e1ddd6cScth 					sema_v(&vlun->svl_pgr_sema);
24601e1ddd6cScth 				}
24611e1ddd6cScth 				return (TRAN_FATAL_ERROR);
24621e1ddd6cScth 			}
24631e1ddd6cScth 			mdi_rele_path(npip);
246440764c95SVictor Li 			if (!(vpkt->vpkt_state & VHCI_PKT_IN_FAILOVER)) {
24651e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL, "!invoking "
24661e1ddd6cScth 				    "mdi_failover\n"));
24671e1ddd6cScth 				rval = mdi_failover(vhci->vhci_dip, cdip,
24681e1ddd6cScth 				    MDI_FAILOVER_ASYNC);
246940764c95SVictor Li 			} else {
247040764c95SVictor Li 				rval = vlun->svl_failover_status;
247140764c95SVictor Li 			}
24721e1ddd6cScth 			if (rval == MDI_FAILURE) {
24731e1ddd6cScth 				VHCI_RELEASE_LUN(vlun);
24741e1ddd6cScth 				if (pgr_sema_held) {
24751e1ddd6cScth 					sema_v(&vlun->svl_pgr_sema);
24761e1ddd6cScth 				}
24771e1ddd6cScth 				return (TRAN_FATAL_ERROR);
24781e1ddd6cScth 			} else if (rval == MDI_BUSY) {
24791e1ddd6cScth 				VHCI_RELEASE_LUN(vlun);
24801e1ddd6cScth 				if (pgr_sema_held) {
24811e1ddd6cScth 					sema_v(&vlun->svl_pgr_sema);
24821e1ddd6cScth 				}
24831e1ddd6cScth 				return (TRAN_BUSY);
24841e1ddd6cScth 			} else {
24851e1ddd6cScth 				if (pgr_sema_held) {
24861e1ddd6cScth 					sema_v(&vlun->svl_pgr_sema);
24871e1ddd6cScth 				}
248840764c95SVictor Li 				vpkt->vpkt_state |= VHCI_PKT_IN_FAILOVER;
24891e1ddd6cScth 				return (TRAN_BUSY);
24901e1ddd6cScth 			}
24911e1ddd6cScth 		}
24921e1ddd6cScth 		vlun->svl_waiting_for_activepath = 0;
24931e1ddd6cScth bind_path:
24941e1ddd6cScth 		vpkt->vpkt_path = pip;
24951e1ddd6cScth 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
24961e1ddd6cScth 		ASSERT(svp != NULL);
24971e1ddd6cScth 
24981e1ddd6cScth 		psd = svp->svp_psd;
24991e1ddd6cScth 		ASSERT(psd != NULL);
25001e1ddd6cScth 		address = &psd->sd_address;
25011e1ddd6cScth 	} else {
25021e1ddd6cScth 		pkt = vpkt->vpkt_hba_pkt;
25031e1ddd6cScth 		address = &pkt->pkt_address;
25041e1ddd6cScth 	}
25051e1ddd6cScth 
2506602ca9eaScth 	/* Verify match of specified path_instance and selected path_instance */
2507602ca9eaScth 	ASSERT((path_instance == 0) ||
2508602ca9eaScth 	    (path_instance == mdi_pi_get_path_instance(vpkt->vpkt_path)));
2509602ca9eaScth 
25101e1ddd6cScth 	/*
25111e1ddd6cScth 	 * For PKT_PARTIAL_DMA case, call pHCI's scsi_init_pkt whenever
25121e1ddd6cScth 	 * target driver calls vhci_scsi_init_pkt.
25131e1ddd6cScth 	 */
25141e1ddd6cScth 	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) &&
25151e1ddd6cScth 	    vpkt->vpkt_path && vpkt->vpkt_hba_pkt) {
25161e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
25171e1ddd6cScth 		    "vhci_bind_transport: PKT_PARTIAL_DMA "
25181e1ddd6cScth 		    "vpkt 0x%p, path 0x%p hba_pkt 0x%p\n",
25191e1ddd6cScth 		    (void *)vpkt, (void *)vpkt->vpkt_path, (void *)pkt));
25201e1ddd6cScth 		pkt = vpkt->vpkt_hba_pkt;
25211e1ddd6cScth 		address = &pkt->pkt_address;
25221e1ddd6cScth 	}
25231e1ddd6cScth 
25241e1ddd6cScth 	if (pkt == NULL || (vpkt->vpkt_flags & CFLAG_DMA_PARTIAL)) {
25251e1ddd6cScth 		pkt = scsi_init_pkt(address, pkt,
25261e1ddd6cScth 		    vpkt->vpkt_tgt_init_bp, vpkt->vpkt_tgt_init_cdblen,
25270205780bSrralphs 		    vpkt->vpkt_tgt_init_scblen, 0, flags, func, NULL);
25281e1ddd6cScth 
25291e1ddd6cScth 		if (pkt == NULL) {
25301e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
25311e1ddd6cScth 			    "!bind transport: 0x%p 0x%p 0x%p\n",
25321e1ddd6cScth 			    (void *)vhci, (void *)psd, (void *)vpkt));
25331e1ddd6cScth 			if ((vpkt->vpkt_hba_pkt == NULL) && vpkt->vpkt_path) {
25341e1ddd6cScth 				MDI_PI_ERRSTAT(vpkt->vpkt_path,
25351e1ddd6cScth 				    MDI_PI_TRANSERR);
25361e1ddd6cScth 				mdi_rele_path(vpkt->vpkt_path);
25371e1ddd6cScth 				vpkt->vpkt_path = NULL;
25381e1ddd6cScth 			}
25391e1ddd6cScth 			if (pgr_sema_held) {
25401e1ddd6cScth 				sema_v(&vlun->svl_pgr_sema);
25411e1ddd6cScth 			}
25421e1ddd6cScth 			/*
25434c06356bSdh142964 			 * Consider it a fatal error if b_error is
25444c06356bSdh142964 			 * set as a result of DMA binding failure
25454c06356bSdh142964 			 * vs. a condition of being temporarily out of
25464c06356bSdh142964 			 * some resource
25471e1ddd6cScth 			 */
2548a9bd6587SVictor Li 			if (vpkt->vpkt_tgt_init_bp == NULL ||
2549a9bd6587SVictor Li 			    geterror(vpkt->vpkt_tgt_init_bp))
25504c06356bSdh142964 				return (TRAN_FATAL_ERROR);
25514c06356bSdh142964 			else
25521e1ddd6cScth 				return (TRAN_BUSY);
25531e1ddd6cScth 		}
25541e1ddd6cScth 	}
25551e1ddd6cScth 
25561e1ddd6cScth 	pkt->pkt_private = vpkt;
25571e1ddd6cScth 	vpkt->vpkt_hba_pkt = pkt;
25581e1ddd6cScth 	return (TRAN_ACCEPT);
25591e1ddd6cScth }
25601e1ddd6cScth 
25611e1ddd6cScth 
25621e1ddd6cScth /*PRINTFLIKE3*/
25631e1ddd6cScth void
vhci_log(int level,dev_info_t * dip,const char * fmt,...)25641e1ddd6cScth vhci_log(int level, dev_info_t *dip, const char *fmt, ...)
25651e1ddd6cScth {
25661e1ddd6cScth 	char		buf[256];
25671e1ddd6cScth 	va_list		ap;
25681e1ddd6cScth 
25691e1ddd6cScth 	va_start(ap, fmt);
25701e1ddd6cScth 	(void) vsprintf(buf, fmt, ap);
25711e1ddd6cScth 	va_end(ap);
25721e1ddd6cScth 
25731e1ddd6cScth 	scsi_log(dip, "scsi_vhci", level, buf);
25741e1ddd6cScth }
25751e1ddd6cScth 
25761e1ddd6cScth /* do a PGR out with the information we've saved away */
25771e1ddd6cScth static int
vhci_do_prout(scsi_vhci_priv_t * svp)25781e1ddd6cScth vhci_do_prout(scsi_vhci_priv_t *svp)
25791e1ddd6cScth {
25801e1ddd6cScth 
25811e1ddd6cScth 	struct scsi_pkt			*new_pkt;
25821e1ddd6cScth 	struct buf			*bp;
25830205780bSrralphs 	scsi_vhci_lun_t			*vlun = svp->svp_svl;
25841e1ddd6cScth 	int				rval, retry, nr_retry, ua_retry;
25850c45178bSwl202157@icefox 	uint8_t				*sns, skey;
25861e1ddd6cScth 
25871e1ddd6cScth 	bp = getrbuf(KM_SLEEP);
25881e1ddd6cScth 	bp->b_flags = B_WRITE;
25891e1ddd6cScth 	bp->b_resid = 0;
25900205780bSrralphs 	bp->b_un.b_addr = (caddr_t)&vlun->svl_prout;
25910205780bSrralphs 	bp->b_bcount = vlun->svl_bcount;
25921e1ddd6cScth 
25931e1ddd6cScth 	VHCI_INCR_PATH_CMDCOUNT(svp);
25941e1ddd6cScth 
25951e1ddd6cScth 	new_pkt = scsi_init_pkt(&svp->svp_psd->sd_address, NULL, bp,
25961e1ddd6cScth 	    CDB_GROUP1, sizeof (struct scsi_arq_status), 0, 0,
25971e1ddd6cScth 	    SLEEP_FUNC, NULL);
25981e1ddd6cScth 	if (new_pkt == NULL) {
25991e1ddd6cScth 		VHCI_DECR_PATH_CMDCOUNT(svp);
26001e1ddd6cScth 		freerbuf(bp);
26011e1ddd6cScth 		cmn_err(CE_WARN, "!vhci_do_prout: scsi_init_pkt failed");
26021e1ddd6cScth 		return (0);
26031e1ddd6cScth 	}
26041e1ddd6cScth 	mutex_enter(&vlun->svl_mutex);
26051e1ddd6cScth 	bp->b_un.b_addr = (caddr_t)&vlun->svl_prout;
26061e1ddd6cScth 	bp->b_bcount = vlun->svl_bcount;
26071e1ddd6cScth 	bcopy(vlun->svl_cdb, new_pkt->pkt_cdbp,
26081e1ddd6cScth 	    sizeof (vlun->svl_cdb));
26091e1ddd6cScth 	new_pkt->pkt_time = vlun->svl_time;
26101e1ddd6cScth 	mutex_exit(&vlun->svl_mutex);
26111e1ddd6cScth 	new_pkt->pkt_flags = FLAG_NOINTR;
26121e1ddd6cScth 
26131e1ddd6cScth 	ua_retry = nr_retry = retry = 0;
26141e1ddd6cScth again:
26151e1ddd6cScth 	rval = vhci_do_scsi_cmd(new_pkt);
26161e1ddd6cScth 	if (rval != 1) {
26171e1ddd6cScth 		if ((new_pkt->pkt_reason == CMD_CMPLT) &&
26181e1ddd6cScth 		    (SCBP_C(new_pkt) == STATUS_CHECK) &&
26191e1ddd6cScth 		    (new_pkt->pkt_state & STATE_ARQ_DONE)) {
26200c45178bSwl202157@icefox 			sns = (uint8_t *)
26210c45178bSwl202157@icefox 			    &(((struct scsi_arq_status *)(uintptr_t)
26221e1ddd6cScth 			    (new_pkt->pkt_scbp))->sts_sensedata);
26230c45178bSwl202157@icefox 			skey = scsi_sense_key(sns);
26240c45178bSwl202157@icefox 			if ((skey == KEY_UNIT_ATTENTION) ||
26250c45178bSwl202157@icefox 			    (skey == KEY_NOT_READY)) {
26261e1ddd6cScth 				int max_retry;
26271e1ddd6cScth 				struct scsi_failover_ops *fops;
26281e1ddd6cScth 				fops = vlun->svl_fops;
26290205780bSrralphs 				rval = fops->sfo_analyze_sense(svp->svp_psd,
26300205780bSrralphs 				    sns, vlun->svl_fops_ctpriv);
26311e1ddd6cScth 				if (rval == SCSI_SENSE_NOT_READY) {
26321e1ddd6cScth 					max_retry = vhci_prout_not_ready_retry;
26331e1ddd6cScth 					retry = nr_retry++;
26341e1ddd6cScth 					delay(1 * drv_usectohz(1000000));
26351e1ddd6cScth 				} else {
26361e1ddd6cScth 					/* chk for state change and update */
26371e1ddd6cScth 					if (rval == SCSI_SENSE_STATE_CHANGED) {
26381e1ddd6cScth 						int held;
26391e1ddd6cScth 						VHCI_HOLD_LUN(vlun,
26401e1ddd6cScth 						    VH_NOSLEEP, held);
26411e1ddd6cScth 						if (!held) {
26421e1ddd6cScth 							rval = TRAN_BUSY;
26431e1ddd6cScth 						} else {
26441e1ddd6cScth 							/* chk for alua first */
26451e1ddd6cScth 							vhci_update_pathstates(
26461e1ddd6cScth 							    (void *)vlun);
26471e1ddd6cScth 						}
26481e1ddd6cScth 					}
26491e1ddd6cScth 					retry = ua_retry++;
26501e1ddd6cScth 					max_retry = VHCI_MAX_PGR_RETRIES;
26511e1ddd6cScth 				}
26521e1ddd6cScth 				if (retry < max_retry) {
26531e1ddd6cScth 					VHCI_DEBUG(4, (CE_WARN, NULL,
26541e1ddd6cScth 					    "!vhci_do_prout retry 0x%x "
26551e1ddd6cScth 					    "(0x%x 0x%x 0x%x)",
26561e1ddd6cScth 					    SCBP_C(new_pkt),
26571e1ddd6cScth 					    new_pkt->pkt_cdbp[0],
26581e1ddd6cScth 					    new_pkt->pkt_cdbp[1],
26591e1ddd6cScth 					    new_pkt->pkt_cdbp[2]));
26601e1ddd6cScth 					goto again;
26611e1ddd6cScth 				}
26621e1ddd6cScth 				rval = 0;
26631e1ddd6cScth 				VHCI_DEBUG(4, (CE_WARN, NULL,
26641e1ddd6cScth 				    "!vhci_do_prout 0x%x "
26651e1ddd6cScth 				    "(0x%x 0x%x 0x%x)",
26661e1ddd6cScth 				    SCBP_C(new_pkt),
26671e1ddd6cScth 				    new_pkt->pkt_cdbp[0],
26681e1ddd6cScth 				    new_pkt->pkt_cdbp[1],
26691e1ddd6cScth 				    new_pkt->pkt_cdbp[2]));
26700c45178bSwl202157@icefox 			} else if (skey == KEY_ILLEGAL_REQUEST)
26711e1ddd6cScth 				rval = VHCI_PGR_ILLEGALOP;
26721e1ddd6cScth 		}
26731e1ddd6cScth 	} else {
26741e1ddd6cScth 		rval = 1;
26751e1ddd6cScth 	}
26761e1ddd6cScth 	scsi_destroy_pkt(new_pkt);
26771e1ddd6cScth 	VHCI_DECR_PATH_CMDCOUNT(svp);
26781e1ddd6cScth 	freerbuf(bp);
26791e1ddd6cScth 	return (rval);
26801e1ddd6cScth }
26811e1ddd6cScth 
26821e1ddd6cScth static void
vhci_run_cmd(void * arg)26831e1ddd6cScth vhci_run_cmd(void *arg)
26841e1ddd6cScth {
26851e1ddd6cScth 	struct scsi_pkt		*pkt = (struct scsi_pkt *)arg;
26861e1ddd6cScth 	struct scsi_pkt		*tpkt;
26871e1ddd6cScth 	scsi_vhci_priv_t	*svp;
26881e1ddd6cScth 	mdi_pathinfo_t		*pip, *npip;
26891e1ddd6cScth 	scsi_vhci_lun_t		*vlun;
26901e1ddd6cScth 	dev_info_t		*cdip;
26911e1ddd6cScth 	scsi_vhci_priv_t	*nsvp;
26921e1ddd6cScth 	int			fail = 0;
26931e1ddd6cScth 	int			rval;
26941e1ddd6cScth 	struct vhci_pkt		*vpkt;
26951e1ddd6cScth 	uchar_t			cdb_1;
26961e1ddd6cScth 	vhci_prout_t		*prout;
26971e1ddd6cScth 
26981e1ddd6cScth 	vpkt = (struct vhci_pkt *)pkt->pkt_private;
26991e1ddd6cScth 	tpkt = vpkt->vpkt_tgt_pkt;
27001e1ddd6cScth 	pip = vpkt->vpkt_path;
27011e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
27021e1ddd6cScth 	if (svp == NULL) {
27031e1ddd6cScth 		tpkt->pkt_reason = CMD_TRAN_ERR;
27041e1ddd6cScth 		tpkt->pkt_statistics = STAT_ABORTED;
27051e1ddd6cScth 		goto done;
27061e1ddd6cScth 	}
27071e1ddd6cScth 	vlun = svp->svp_svl;
27081e1ddd6cScth 	prout = &vlun->svl_prout;
27091e1ddd6cScth 	if (SCBP_C(pkt) != STATUS_GOOD)
27101e1ddd6cScth 		fail++;
27111e1ddd6cScth 	cdip = vlun->svl_dip;
27121e1ddd6cScth 	pip = npip = NULL;
27131e1ddd6cScth 	rval = mdi_select_path(cdip, NULL,
27141e1ddd6cScth 	    MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH, NULL, &npip);
27151e1ddd6cScth 	if ((rval != MDI_SUCCESS) || (npip == NULL)) {
27161e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
27171e1ddd6cScth 		    "vhci_run_cmd: no path! 0x%p\n", (void *)svp));
27181e1ddd6cScth 		tpkt->pkt_reason = CMD_TRAN_ERR;
27191e1ddd6cScth 		tpkt->pkt_statistics = STAT_ABORTED;
27201e1ddd6cScth 		goto done;
27211e1ddd6cScth 	}
27221e1ddd6cScth 
27231e1ddd6cScth 	cdb_1 = vlun->svl_cdb[1];
27241e1ddd6cScth 	vlun->svl_cdb[1] &= 0xe0;
27251e1ddd6cScth 	vlun->svl_cdb[1] |= VHCI_PROUT_R_AND_IGNORE;
27261e1ddd6cScth 
27271e1ddd6cScth 	do {
27280205780bSrralphs 		nsvp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(npip);
27291e1ddd6cScth 		if (nsvp == NULL) {
27301e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
27311e1ddd6cScth 			    "vhci_run_cmd: no "
27321e1ddd6cScth 			    "client priv! 0x%p offlined?\n",
27331e1ddd6cScth 			    (void *)npip));
27341e1ddd6cScth 			goto next_path;
27351e1ddd6cScth 		}
27361e1ddd6cScth 		if (vlun->svl_first_path == npip) {
27371e1ddd6cScth 			goto next_path;
27381e1ddd6cScth 		} else {
27391e1ddd6cScth 			if (vhci_do_prout(nsvp) != 1)
27401e1ddd6cScth 				fail++;
27411e1ddd6cScth 		}
27421e1ddd6cScth next_path:
27431e1ddd6cScth 		pip = npip;
27441e1ddd6cScth 		rval = mdi_select_path(cdip, NULL,
27451e1ddd6cScth 		    MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH,
27461e1ddd6cScth 		    pip, &npip);
27471e1ddd6cScth 		mdi_rele_path(pip);
27481e1ddd6cScth 	} while ((rval == MDI_SUCCESS) && (npip != NULL));
27491e1ddd6cScth 
27501e1ddd6cScth 	vlun->svl_cdb[1] = cdb_1;
27511e1ddd6cScth 
27521e1ddd6cScth 	if (fail) {
27531e1ddd6cScth 		VHCI_DEBUG(4, (CE_WARN, NULL, "%s%d: key registration failed, "
27541e1ddd6cScth 		    "couldn't be replicated on all paths",
27551e1ddd6cScth 		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
27561e1ddd6cScth 		vhci_print_prout_keys(vlun, "vhci_run_cmd: ");
27571e1ddd6cScth 
27581e1ddd6cScth 		if (SCBP_C(pkt) != STATUS_GOOD) {
27591e1ddd6cScth 			tpkt->pkt_reason = CMD_TRAN_ERR;
27601e1ddd6cScth 			tpkt->pkt_statistics = STAT_ABORTED;
27611e1ddd6cScth 		}
27621e1ddd6cScth 	} else {
27631e1ddd6cScth 		vlun->svl_pgr_active = 1;
27641e1ddd6cScth 		vhci_print_prout_keys(vlun, "vhci_run_cmd: before bcopy:");
27651e1ddd6cScth 
27661e1ddd6cScth 		bcopy((const void *)prout->service_key,
27671e1ddd6cScth 		    (void *)prout->active_service_key, MHIOC_RESV_KEY_SIZE);
27681e1ddd6cScth 		bcopy((const void *)prout->res_key,
27691e1ddd6cScth 		    (void *)prout->active_res_key, MHIOC_RESV_KEY_SIZE);
27701e1ddd6cScth 
27711e1ddd6cScth 		vhci_print_prout_keys(vlun, "vhci_run_cmd: after bcopy:");
27721e1ddd6cScth 	}
27731e1ddd6cScth done:
27741e1ddd6cScth 	if (SCBP_C(pkt) == STATUS_GOOD)
27751e1ddd6cScth 		vlun->svl_first_path = NULL;
27761e1ddd6cScth 
27771e1ddd6cScth 	if (svp)
27781e1ddd6cScth 		VHCI_DECR_PATH_CMDCOUNT(svp);
27791e1ddd6cScth 
27801e1ddd6cScth 	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
27811e1ddd6cScth 		scsi_destroy_pkt(pkt);
27821e1ddd6cScth 		vpkt->vpkt_hba_pkt = NULL;
27831e1ddd6cScth 		if (vpkt->vpkt_path) {
27841e1ddd6cScth 			mdi_rele_path(vpkt->vpkt_path);
27851e1ddd6cScth 			vpkt->vpkt_path = NULL;
27861e1ddd6cScth 		}
27871e1ddd6cScth 	}
27881e1ddd6cScth 
27891e1ddd6cScth 	sema_v(&vlun->svl_pgr_sema);
27901e1ddd6cScth 	/*
27911e1ddd6cScth 	 * The PROUT commands are not included in the automatic retry
27921e1ddd6cScth 	 * mechanism, therefore, vpkt_org_vpkt should never be set here.
27931e1ddd6cScth 	 */
27941e1ddd6cScth 	ASSERT(vpkt->vpkt_org_vpkt == NULL);
27959c57abc8Ssrivijitha dugganapalli 	scsi_hba_pkt_comp(tpkt);
27961e1ddd6cScth }
27971e1ddd6cScth 
27981e1ddd6cScth /*
27991e1ddd6cScth  * Get the keys registered with this target.  Since we will have
28001e1ddd6cScth  * registered the same key with multiple initiators, strip out
28011e1ddd6cScth  * any duplicate keys.
28021e1ddd6cScth  *
28031e1ddd6cScth  * The pointers which will be used to filter the registered keys from
28041e1ddd6cScth  * the device will be stored in filter_prin and filter_pkt.  If the
28051e1ddd6cScth  * allocation length of the buffer was sufficient for the number of
28061e1ddd6cScth  * parameter data bytes available to be returned by the device then the
28071e1ddd6cScth  * key filtering will use the keylist returned from the original
28081e1ddd6cScth  * request.  If the allocation length of the buffer was not sufficient,
28091e1ddd6cScth  * then the filtering will use the keylist returned from the request
28101e1ddd6cScth  * that is resent below.
28111e1ddd6cScth  *
28121e1ddd6cScth  * If the device returns an additional length field that is greater than
28131e1ddd6cScth  * the allocation length of the buffer, then allocate a new buffer which
28141e1ddd6cScth  * can accommodate the number of parameter data bytes available to be
28151e1ddd6cScth  * returned.  Resend the scsi PRIN command, filter out the duplicate
28161e1ddd6cScth  * keys and return as many of the unique keys found that was originally
28171e1ddd6cScth  * requested and set the additional length field equal to the data bytes
28181e1ddd6cScth  * of unique reservation keys available to be returned.
28191e1ddd6cScth  *
28201e1ddd6cScth  * If the device returns an additional length field that is less than or
28211e1ddd6cScth  * equal to the allocation length of the buffer, then all the available
28221e1ddd6cScth  * keys registered were returned by the device.  Filter out the
28231e1ddd6cScth  * duplicate keys and return all of the unique keys found and set the
28241e1ddd6cScth  * additional length field equal to the data bytes of the reservation
28251e1ddd6cScth  * keys to be returned.
28261e1ddd6cScth  */
28273f51917eSrralphs 
28283f51917eSrralphs #define	VHCI_PRIN_HEADER_SZ (sizeof (prin->length) + sizeof (prin->generation))
28293f51917eSrralphs 
28301e1ddd6cScth static int
vhci_do_prin(struct vhci_pkt ** intr_vpkt)28313f51917eSrralphs vhci_do_prin(struct vhci_pkt **intr_vpkt)
28321e1ddd6cScth {
28333f51917eSrralphs 	scsi_vhci_priv_t *svp;
28343f51917eSrralphs 	struct vhci_pkt *vpkt = *intr_vpkt;
28351e1ddd6cScth 	vhci_prin_readkeys_t *prin;
28363f51917eSrralphs 	scsi_vhci_lun_t *vlun;
28373f51917eSrralphs 	struct scsi_vhci *vhci = ADDR2VHCI(&vpkt->vpkt_tgt_pkt->pkt_address);
28381e1ddd6cScth 
28391e1ddd6cScth 	struct buf		*new_bp = NULL;
28401e1ddd6cScth 	struct scsi_pkt		*new_pkt = NULL;
28411e1ddd6cScth 	struct vhci_pkt		*new_vpkt = NULL;
28423f51917eSrralphs 	uint32_t		needed_length;
28431e1ddd6cScth 	int			rval = VHCI_CMD_CMPLT;
28441e1ddd6cScth 	uint32_t		prin_length = 0;
28451e1ddd6cScth 	uint32_t		svl_prin_length = 0;
28461e1ddd6cScth 
28473f51917eSrralphs 	ASSERT(vpkt->vpkt_path);
28483f51917eSrralphs 	svp = mdi_pi_get_vhci_private(vpkt->vpkt_path);
28493f51917eSrralphs 	ASSERT(svp);
28503f51917eSrralphs 	vlun = svp->svp_svl;
28513f51917eSrralphs 	ASSERT(vlun);
28523f51917eSrralphs 
28533f51917eSrralphs 	/*
28543f51917eSrralphs 	 * If the caller only asked for an amount of data that would not
28553f51917eSrralphs 	 * be enough to include any key data it is likely that they will
28563f51917eSrralphs 	 * send the next command with a buffer size based on the information
28573f51917eSrralphs 	 * from this header. Doing recovery on this would be a duplication
28583f51917eSrralphs 	 * of efforts.
28593f51917eSrralphs 	 */
28603f51917eSrralphs 	if (vpkt->vpkt_tgt_init_bp->b_bcount <= VHCI_PRIN_HEADER_SZ) {
28613f51917eSrralphs 		rval = VHCI_CMD_CMPLT;
28623f51917eSrralphs 		goto exit;
28633f51917eSrralphs 	}
28643f51917eSrralphs 
28653f51917eSrralphs 	if (vpkt->vpkt_org_vpkt == NULL) {
28663f51917eSrralphs 		/*
28673f51917eSrralphs 		 * Can fail as sleep is not allowed.
28683f51917eSrralphs 		 */
28691e1ddd6cScth 		prin = (vhci_prin_readkeys_t *)
28700205780bSrralphs 		    bp_mapin_common(vpkt->vpkt_tgt_init_bp, VM_NOSLEEP);
28713f51917eSrralphs 	} else {
28723f51917eSrralphs 		/*
28733f51917eSrralphs 		 * The retry buf doesn't need to be mapped in.
28743f51917eSrralphs 		 */
28753f51917eSrralphs 		prin = (vhci_prin_readkeys_t *)
28763f51917eSrralphs 		    vpkt->vpkt_tgt_init_bp->b_un.b_daddr;
28771e1ddd6cScth 	}
28781e1ddd6cScth 
28791e1ddd6cScth 	if (prin == NULL) {
28801e1ddd6cScth 		VHCI_DEBUG(5, (CE_WARN, NULL,
28811e1ddd6cScth 		    "vhci_do_prin: bp_mapin_common failed."));
28821e1ddd6cScth 		rval = VHCI_CMD_ERROR;
28833f51917eSrralphs 		goto fail;
28843f51917eSrralphs 	}
28853f51917eSrralphs 
28863f51917eSrralphs 	prin_length = BE_32(prin->length);
28873f51917eSrralphs 
28881e1ddd6cScth 	/*
28891e1ddd6cScth 	 * According to SPC-3r22, sec 4.3.4.6: "If the amount of
28901e1ddd6cScth 	 * information to be transferred exceeds the maximum value
28911e1ddd6cScth 	 * that the ALLOCATION LENGTH field is capable of specifying,
28921e1ddd6cScth 	 * the device server shall...terminate the command with CHECK
28931e1ddd6cScth 	 * CONDITION status".  The ALLOCATION LENGTH field of the
28941e1ddd6cScth 	 * PERSISTENT RESERVE IN command is 2 bytes. We should never
28951e1ddd6cScth 	 * get here with an ADDITIONAL LENGTH greater than 0xFFFF
28961e1ddd6cScth 	 * so if we do, then it is an error!
28971e1ddd6cScth 	 */
28981e1ddd6cScth 
28991e1ddd6cScth 
29003f51917eSrralphs 	if ((prin_length + VHCI_PRIN_HEADER_SZ) > 0xFFFF) {
29011e1ddd6cScth 		VHCI_DEBUG(5, (CE_NOTE, NULL,
29021e1ddd6cScth 		    "vhci_do_prin: Device returned invalid "
29031e1ddd6cScth 		    "length 0x%x\n", prin_length));
29041e1ddd6cScth 		rval = VHCI_CMD_ERROR;
29053f51917eSrralphs 		goto fail;
29061e1ddd6cScth 	}
29073f51917eSrralphs 	needed_length = prin_length + VHCI_PRIN_HEADER_SZ;
29081e1ddd6cScth 
29091e1ddd6cScth 	/*
29101e1ddd6cScth 	 * If prin->length is greater than the byte count allocated in the
29111e1ddd6cScth 	 * original buffer, then resend the request with enough buffer
29121e1ddd6cScth 	 * allocated to get all of the available registered keys.
29131e1ddd6cScth 	 */
29143f51917eSrralphs 	if ((vpkt->vpkt_tgt_init_bp->b_bcount < needed_length) &&
29153f51917eSrralphs 	    (vpkt->vpkt_org_vpkt == NULL)) {
29163f51917eSrralphs 
29170205780bSrralphs 		new_pkt = vhci_create_retry_pkt(vpkt);
29183f51917eSrralphs 		if (new_pkt == NULL) {
29193f51917eSrralphs 			rval = VHCI_CMD_ERROR;
29203f51917eSrralphs 			goto fail;
29213f51917eSrralphs 		}
29221e1ddd6cScth 		new_vpkt = TGTPKT2VHCIPKT(new_pkt);
29231e1ddd6cScth 
29241e1ddd6cScth 		/*
29251e1ddd6cScth 		 * This is the buf with buffer pointer
29261e1ddd6cScth 		 * where the prin readkeys will be
29271e1ddd6cScth 		 * returned from the device
29281e1ddd6cScth 		 */
29293f51917eSrralphs 		new_bp = scsi_alloc_consistent_buf(&svp->svp_psd->sd_address,
29303f51917eSrralphs 		    NULL, needed_length, B_READ, NULL_FUNC, NULL);
29313f51917eSrralphs 		if ((new_bp == NULL) || (new_bp->b_un.b_addr == NULL)) {
29323f51917eSrralphs 			if (new_bp) {
29333f51917eSrralphs 				scsi_free_consistent_buf(new_bp);
29343f51917eSrralphs 			}
29353f51917eSrralphs 			vhci_scsi_destroy_pkt(&new_pkt->pkt_address, new_pkt);
29363f51917eSrralphs 			rval = VHCI_CMD_ERROR;
29373f51917eSrralphs 			goto fail;
29383f51917eSrralphs 		}
29393f51917eSrralphs 		new_bp->b_bcount = needed_length;
29403f51917eSrralphs 		new_pkt->pkt_cdbp[7] = (uchar_t)(needed_length >> 8);
29413f51917eSrralphs 		new_pkt->pkt_cdbp[8] = (uchar_t)needed_length;
29421e1ddd6cScth 
29431e1ddd6cScth 		rval = VHCI_CMD_RETRY;
29443f51917eSrralphs 
29453f51917eSrralphs 		new_vpkt->vpkt_tgt_init_bp = new_bp;
29461e1ddd6cScth 	}
29471e1ddd6cScth 
29481e1ddd6cScth 	if (rval == VHCI_CMD_RETRY) {
29493f51917eSrralphs 
29503f51917eSrralphs 		/*
29513f51917eSrralphs 		 * There were more keys then the original request asked for.
29523f51917eSrralphs 		 */
29533f51917eSrralphs 		mdi_pathinfo_t *path_holder = vpkt->vpkt_path;
29541e1ddd6cScth 
29551e1ddd6cScth 		/*
29561e1ddd6cScth 		 * Release the old path because it does not matter which path
29571e1ddd6cScth 		 * this command is sent down.  This allows the normal bind
29581e1ddd6cScth 		 * transport mechanism to be used.
29591e1ddd6cScth 		 */
29600205780bSrralphs 		if (vpkt->vpkt_path != NULL) {
29610205780bSrralphs 			mdi_rele_path(vpkt->vpkt_path);
29620205780bSrralphs 			vpkt->vpkt_path = NULL;
29631e1ddd6cScth 		}
29641e1ddd6cScth 
29651e1ddd6cScth 		/*
29661e1ddd6cScth 		 * Dispatch the retry command
29671e1ddd6cScth 		 */
29681e1ddd6cScth 		if (taskq_dispatch(vhci->vhci_taskq, vhci_dispatch_scsi_start,
2969fc8ae2ecSToomas Soome 		    (void *) new_vpkt, KM_NOSLEEP) == TASKQID_INVALID) {
29703f51917eSrralphs 			if (path_holder) {
29713f51917eSrralphs 				vpkt->vpkt_path = path_holder;
29723f51917eSrralphs 				mdi_hold_path(path_holder);
29733f51917eSrralphs 			}
29743f51917eSrralphs 			scsi_free_consistent_buf(new_bp);
29753f51917eSrralphs 			vhci_scsi_destroy_pkt(&new_pkt->pkt_address, new_pkt);
29761e1ddd6cScth 			rval = VHCI_CMD_ERROR;
29773f51917eSrralphs 			goto fail;
29783f51917eSrralphs 		}
29793f51917eSrralphs 
29801e1ddd6cScth 		/*
29811e1ddd6cScth 		 * If we return VHCI_CMD_RETRY, that means the caller
29821e1ddd6cScth 		 * is going to bail and wait for the reissued command
29831e1ddd6cScth 		 * to complete.  In that case, we need to decrement
29841e1ddd6cScth 		 * the path command count right now.  In any other
29851e1ddd6cScth 		 * case, it'll be decremented by the caller.
29861e1ddd6cScth 		 */
29871e1ddd6cScth 		VHCI_DECR_PATH_CMDCOUNT(svp);
29883f51917eSrralphs 		goto exit;
29893f51917eSrralphs 
29901e1ddd6cScth 	}
29911e1ddd6cScth 
29923f51917eSrralphs 	if (rval == VHCI_CMD_CMPLT) {
29933f51917eSrralphs 		/*
29943f51917eSrralphs 		 * The original request got all of the keys or the recovery
29953f51917eSrralphs 		 * packet returns.
29963f51917eSrralphs 		 */
29973f51917eSrralphs 		int new;
29983f51917eSrralphs 		int old;
29993f51917eSrralphs 		int num_keys = prin_length / MHIOC_RESV_KEY_SIZE;
30001e1ddd6cScth 
30011e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_do_prin: %d keys read\n",
30023f51917eSrralphs 		    num_keys));
30031e1ddd6cScth 
30041e1ddd6cScth #ifdef DEBUG
30051e1ddd6cScth 		VHCI_DEBUG(5, (CE_NOTE, NULL, "vhci_do_prin: from storage\n"));
30061e1ddd6cScth 		if (vhci_debug == 5)
30073f51917eSrralphs 			vhci_print_prin_keys(prin, num_keys);
30081e1ddd6cScth 		VHCI_DEBUG(5, (CE_NOTE, NULL,
30091e1ddd6cScth 		    "vhci_do_prin: MPxIO old keys:\n"));
30101e1ddd6cScth 		if (vhci_debug == 5)
30113f51917eSrralphs 			vhci_print_prin_keys(&vlun->svl_prin, num_keys);
30121e1ddd6cScth #endif
30131e1ddd6cScth 
30141e1ddd6cScth 		/*
30151e1ddd6cScth 		 * Filter out all duplicate keys returned from the device
30161e1ddd6cScth 		 * We know that we use a different key for every host, so we
30171e1ddd6cScth 		 * can simply strip out duplicates. Otherwise we would need to
30181e1ddd6cScth 		 * do more bookkeeping to figure out which keys to strip out.
30191e1ddd6cScth 		 */
30201e1ddd6cScth 
30211e1ddd6cScth 		new = 0;
30221e1ddd6cScth 
30233f51917eSrralphs 		/*
30243f51917eSrralphs 		 * If we got at least 1 key copy it.
30253f51917eSrralphs 		 */
30263f51917eSrralphs 		if (num_keys > 0) {
30271e1ddd6cScth 			vlun->svl_prin.keylist[0] = prin->keylist[0];
30281e1ddd6cScth 			new++;
30291e1ddd6cScth 		}
30301e1ddd6cScth 
30313f51917eSrralphs 		/*
30323f51917eSrralphs 		 * find next unique key.
30333f51917eSrralphs 		 */
30343f51917eSrralphs 		for (old = 1; old < num_keys; old++) {
30351e1ddd6cScth 			int j;
30361e1ddd6cScth 			int match = 0;
30373f51917eSrralphs 
30383f51917eSrralphs 			if (new >= VHCI_NUM_RESV_KEYS)
30393f51917eSrralphs 				break;
30401e1ddd6cScth 			for (j = 0; j < new; j++) {
30411e1ddd6cScth 				if (bcmp(&prin->keylist[old],
30421e1ddd6cScth 				    &vlun->svl_prin.keylist[j],
30431e1ddd6cScth 				    sizeof (mhioc_resv_key_t)) == 0) {
30441e1ddd6cScth 					match = 1;
30451e1ddd6cScth 					break;
30461e1ddd6cScth 				}
30471e1ddd6cScth 			}
30481e1ddd6cScth 			if (!match) {
30491e1ddd6cScth 				vlun->svl_prin.keylist[new] =
30501e1ddd6cScth 				    prin->keylist[old];
30511e1ddd6cScth 				new++;
30521e1ddd6cScth 			}
30531e1ddd6cScth 		}
30541e1ddd6cScth 
30553f51917eSrralphs 		/* Stored Big Endian */
30561e1ddd6cScth 		vlun->svl_prin.generation = prin->generation;
30573f51917eSrralphs 		svl_prin_length = new * sizeof (mhioc_resv_key_t);
30583f51917eSrralphs 		/* Stored Big Endian */
30591e1ddd6cScth 		vlun->svl_prin.length = BE_32(svl_prin_length);
30603f51917eSrralphs 		svl_prin_length += VHCI_PRIN_HEADER_SZ;
30611e1ddd6cScth 
30621e1ddd6cScth 		/*
30631e1ddd6cScth 		 * If we arrived at this point after issuing a retry, make sure
30641e1ddd6cScth 		 * that we put everything back the way it originally was so
30651e1ddd6cScth 		 * that the target driver can complete the command correctly.
30661e1ddd6cScth 		 */
30670205780bSrralphs 		if (vpkt->vpkt_org_vpkt != NULL) {
30680205780bSrralphs 			new_bp = vpkt->vpkt_tgt_init_bp;
30691e1ddd6cScth 
30701e1ddd6cScth 			scsi_free_consistent_buf(new_bp);
30711e1ddd6cScth 
30720205780bSrralphs 			vpkt = vhci_sync_retry_pkt(vpkt);
30733f51917eSrralphs 			*intr_vpkt = vpkt;
30741e1ddd6cScth 
30751e1ddd6cScth 			/*
30761e1ddd6cScth 			 * Make sure the original buffer is mapped into kernel
30771e1ddd6cScth 			 * space before we try to copy the filtered keys into
30781e1ddd6cScth 			 * it.
30791e1ddd6cScth 			 */
30801e1ddd6cScth 			prin = (vhci_prin_readkeys_t *)bp_mapin_common(
30810205780bSrralphs 			    vpkt->vpkt_tgt_init_bp, VM_NOSLEEP);
30821e1ddd6cScth 		}
30831e1ddd6cScth 
30841e1ddd6cScth 		/*
30851e1ddd6cScth 		 * Now copy the desired number of prin keys into the original
30861e1ddd6cScth 		 * target buffer.
30871e1ddd6cScth 		 */
30883f51917eSrralphs 		if (svl_prin_length <= vpkt->vpkt_tgt_init_bp->b_bcount) {
30891e1ddd6cScth 			/*
30901e1ddd6cScth 			 * It is safe to return all of the available unique
30911e1ddd6cScth 			 * keys
30921e1ddd6cScth 			 */
30933f51917eSrralphs 			bcopy(&vlun->svl_prin, prin, svl_prin_length);
30941e1ddd6cScth 		} else {
30951e1ddd6cScth 			/*
30961e1ddd6cScth 			 * Not all of the available keys were requested by the
30971e1ddd6cScth 			 * original command.
30981e1ddd6cScth 			 */
30991e1ddd6cScth 			bcopy(&vlun->svl_prin, prin,
31000205780bSrralphs 			    vpkt->vpkt_tgt_init_bp->b_bcount);
31011e1ddd6cScth 		}
31021e1ddd6cScth #ifdef DEBUG
31031e1ddd6cScth 		VHCI_DEBUG(5, (CE_NOTE, NULL,
31041e1ddd6cScth 		    "vhci_do_prin: To Application:\n"));
31051e1ddd6cScth 		if (vhci_debug == 5)
31061e1ddd6cScth 			vhci_print_prin_keys(prin, new);
31071e1ddd6cScth 		VHCI_DEBUG(5, (CE_NOTE, NULL,
31081e1ddd6cScth 		    "vhci_do_prin: MPxIO new keys:\n"));
31091e1ddd6cScth 		if (vhci_debug == 5)
31101e1ddd6cScth 			vhci_print_prin_keys(&vlun->svl_prin, new);
31111e1ddd6cScth #endif
31121e1ddd6cScth 	}
31133f51917eSrralphs fail:
31141e1ddd6cScth 	if (rval == VHCI_CMD_ERROR) {
31151e1ddd6cScth 		/*
31161e1ddd6cScth 		 * If we arrived at this point after issuing a
31171e1ddd6cScth 		 * retry, make sure that we put everything back
31181e1ddd6cScth 		 * the way it originally was so that ssd can
31191e1ddd6cScth 		 * complete the command correctly.
31201e1ddd6cScth 		 */
31211e1ddd6cScth 
31220205780bSrralphs 		if (vpkt->vpkt_org_vpkt != NULL) {
31230205780bSrralphs 			new_bp = vpkt->vpkt_tgt_init_bp;
31241e1ddd6cScth 			if (new_bp != NULL) {
31251e1ddd6cScth 				scsi_free_consistent_buf(new_bp);
31261e1ddd6cScth 			}
31271e1ddd6cScth 
31280205780bSrralphs 			new_vpkt = vpkt;
31290205780bSrralphs 			vpkt = vpkt->vpkt_org_vpkt;
31301e1ddd6cScth 
31311e1ddd6cScth 			vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
31321e1ddd6cScth 			    new_vpkt->vpkt_tgt_pkt);
31331e1ddd6cScth 		}
31341e1ddd6cScth 
31351e1ddd6cScth 		/*
31361e1ddd6cScth 		 * Mark this command completion as having an error so that
31371e1ddd6cScth 		 * ssd will retry the command.
31381e1ddd6cScth 		 */
31391e1ddd6cScth 
31400205780bSrralphs 		vpkt->vpkt_tgt_pkt->pkt_reason = CMD_ABORTED;
31410205780bSrralphs 		vpkt->vpkt_tgt_pkt->pkt_statistics |= STAT_ABORTED;
31421e1ddd6cScth 
31431e1ddd6cScth 		rval = VHCI_CMD_CMPLT;
31441e1ddd6cScth 	}
31453f51917eSrralphs exit:
31461e1ddd6cScth 	/*
31471e1ddd6cScth 	 * Make sure that the semaphore is only released once.
31481e1ddd6cScth 	 */
31491e1ddd6cScth 	if (rval == VHCI_CMD_CMPLT) {
31501e1ddd6cScth 		sema_v(&vlun->svl_pgr_sema);
31511e1ddd6cScth 	}
31521e1ddd6cScth 
31531e1ddd6cScth 	return (rval);
31541e1ddd6cScth }
31551e1ddd6cScth 
31561e1ddd6cScth static void
vhci_intr(struct scsi_pkt * pkt)31571e1ddd6cScth vhci_intr(struct scsi_pkt *pkt)
31581e1ddd6cScth {
31591e1ddd6cScth 	struct vhci_pkt		*vpkt = (struct vhci_pkt *)pkt->pkt_private;
31601e1ddd6cScth 	struct scsi_pkt		*tpkt;
31611e1ddd6cScth 	scsi_vhci_priv_t	*svp;
31621e1ddd6cScth 	scsi_vhci_lun_t		*vlun;
31631e1ddd6cScth 	int			rval, held;
31641e1ddd6cScth 	struct scsi_failover_ops	*fops;
31650c45178bSwl202157@icefox 	uint8_t			*sns, skey, asc, ascq;
31661e1ddd6cScth 	mdi_pathinfo_t		*lpath;
31671e1ddd6cScth 	static char		*timeout_err = "Command Timeout";
31681e1ddd6cScth 	static char		*parity_err = "Parity Error";
31691e1ddd6cScth 	char			*err_str = NULL;
31704c06356bSdh142964 	dev_info_t		*vdip, *cdip;
31714c06356bSdh142964 	char			*cpath;
31721e1ddd6cScth 
31731e1ddd6cScth 	ASSERT(vpkt != NULL);
31741e1ddd6cScth 	tpkt = vpkt->vpkt_tgt_pkt;
31751e1ddd6cScth 	ASSERT(tpkt != NULL);
31761e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(vpkt->vpkt_path);
31771e1ddd6cScth 	ASSERT(svp != NULL);
31781e1ddd6cScth 	vlun = svp->svp_svl;
31791e1ddd6cScth 	ASSERT(vlun != NULL);
31801e1ddd6cScth 	lpath = vpkt->vpkt_path;
31811e1ddd6cScth 
31821e1ddd6cScth 	/*
31831e1ddd6cScth 	 * sync up the target driver's pkt with the pkt that
31841e1ddd6cScth 	 * we actually used
31851e1ddd6cScth 	 */
31861e1ddd6cScth 	*(tpkt->pkt_scbp) = *(pkt->pkt_scbp);
31871e1ddd6cScth 	tpkt->pkt_resid = pkt->pkt_resid;
31881e1ddd6cScth 	tpkt->pkt_state = pkt->pkt_state;
31891e1ddd6cScth 	tpkt->pkt_statistics = pkt->pkt_statistics;
31901e1ddd6cScth 	tpkt->pkt_reason = pkt->pkt_reason;
31911e1ddd6cScth 
3192602ca9eaScth 	/* Return path_instance information back to the target driver. */
3193602ca9eaScth 	if (scsi_pkt_allocated_correctly(tpkt)) {
3194602ca9eaScth 		if (scsi_pkt_allocated_correctly(pkt)) {
3195602ca9eaScth 			/*
3196602ca9eaScth 			 * If both packets were correctly allocated,
3197602ca9eaScth 			 * return path returned by pHCI.
3198602ca9eaScth 			 */
3199602ca9eaScth 			tpkt->pkt_path_instance = pkt->pkt_path_instance;
3200602ca9eaScth 		} else {
3201602ca9eaScth 			/* Otherwise return path of pHCI we used */
3202602ca9eaScth 			tpkt->pkt_path_instance =
3203602ca9eaScth 			    mdi_pi_get_path_instance(lpath);
3204602ca9eaScth 		}
3205602ca9eaScth 	}
3206602ca9eaScth 
32071e1ddd6cScth 	if (pkt->pkt_cdbp[0] == SCMD_PROUT &&
32081e1ddd6cScth 	    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
32091e1ddd6cScth 	    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE)) {
32101e1ddd6cScth 		if ((SCBP_C(pkt) != STATUS_GOOD) ||
32111e1ddd6cScth 		    (pkt->pkt_reason != CMD_CMPLT)) {
32121e1ddd6cScth 			sema_v(&vlun->svl_pgr_sema);
32131e1ddd6cScth 		}
32141e1ddd6cScth 	} else if (pkt->pkt_cdbp[0] == SCMD_PRIN) {
32151e1ddd6cScth 		if (pkt->pkt_reason != CMD_CMPLT ||
32161e1ddd6cScth 		    (SCBP_C(pkt) != STATUS_GOOD)) {
32171e1ddd6cScth 			sema_v(&vlun->svl_pgr_sema);
32181e1ddd6cScth 		}
32191e1ddd6cScth 	}
32201e1ddd6cScth 
32211e1ddd6cScth 	switch (pkt->pkt_reason) {
32221e1ddd6cScth 	case CMD_CMPLT:
32231e1ddd6cScth 		/*
32241e1ddd6cScth 		 * cmd completed successfully, check for scsi errors
32251e1ddd6cScth 		 */
32261e1ddd6cScth 		switch (*(pkt->pkt_scbp)) {
32271e1ddd6cScth 		case STATUS_CHECK:
32281e1ddd6cScth 			if (pkt->pkt_state & STATE_ARQ_DONE) {
32290c45178bSwl202157@icefox 				sns = (uint8_t *)
32300c45178bSwl202157@icefox 				    &(((struct scsi_arq_status *)(uintptr_t)
32311e1ddd6cScth 				    (pkt->pkt_scbp))->sts_sensedata);
32320c45178bSwl202157@icefox 				skey = scsi_sense_key(sns);
32330c45178bSwl202157@icefox 				asc = scsi_sense_asc(sns);
32340c45178bSwl202157@icefox 				ascq = scsi_sense_ascq(sns);
32351e1ddd6cScth 				fops = vlun->svl_fops;
32361e1ddd6cScth 				ASSERT(fops != NULL);
32371e1ddd6cScth 				VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_intr: "
32381e1ddd6cScth 				    "Received sns key %x  esc %x  escq %x\n",
32390c45178bSwl202157@icefox 				    skey, asc, ascq));
32401e1ddd6cScth 
32411e1ddd6cScth 				if (vlun->svl_waiting_for_activepath == 1) {
32421e1ddd6cScth 					/*
32431e1ddd6cScth 					 * if we are here it means we are
32441e1ddd6cScth 					 * in the midst of a probe/attach
32451e1ddd6cScth 					 * through a passive path; this
32461e1ddd6cScth 					 * case is exempt from sense analysis
32471e1ddd6cScth 					 * for detection of ext. failover
32481e1ddd6cScth 					 * because that would unnecessarily
32491e1ddd6cScth 					 * increase attach time.
32501e1ddd6cScth 					 */
32511e1ddd6cScth 					bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
32521e1ddd6cScth 					    vpkt->vpkt_tgt_init_scblen);
32531e1ddd6cScth 					break;
32541e1ddd6cScth 				}
32550c45178bSwl202157@icefox 				if (asc == VHCI_SCSI_PERR) {
32561e1ddd6cScth 					/*
32571e1ddd6cScth 					 * parity error
32581e1ddd6cScth 					 */
32591e1ddd6cScth 					err_str = parity_err;
32601e1ddd6cScth 					bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
32611e1ddd6cScth 					    vpkt->vpkt_tgt_init_scblen);
32621e1ddd6cScth 					break;
32631e1ddd6cScth 				}
32640205780bSrralphs 				rval = fops->sfo_analyze_sense(svp->svp_psd,
32650205780bSrralphs 				    sns, vlun->svl_fops_ctpriv);
32661e1ddd6cScth 				if ((rval == SCSI_SENSE_NOFAILOVER) ||
32671e1ddd6cScth 				    (rval == SCSI_SENSE_UNKNOWN) ||
32681e1ddd6cScth 				    (rval == SCSI_SENSE_NOT_READY)) {
32691e1ddd6cScth 					bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
32701e1ddd6cScth 					    vpkt->vpkt_tgt_init_scblen);
32711e1ddd6cScth 					break;
32721e1ddd6cScth 				} else if (rval == SCSI_SENSE_STATE_CHANGED) {
32731e1ddd6cScth 					struct scsi_vhci	*vhci;
32741e1ddd6cScth 					vhci = ADDR2VHCI(&tpkt->pkt_address);
32751e1ddd6cScth 					VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
32761e1ddd6cScth 					if (!held) {
32771e1ddd6cScth 						/*
32781e1ddd6cScth 						 * looks like some other thread
32791e1ddd6cScth 						 * has already detected this
32801e1ddd6cScth 						 * condition
32811e1ddd6cScth 						 */
32821e1ddd6cScth 						tpkt->pkt_state &=
32831e1ddd6cScth 						    ~STATE_ARQ_DONE;
32841e1ddd6cScth 						*(tpkt->pkt_scbp) =
32851e1ddd6cScth 						    STATUS_BUSY;
32861e1ddd6cScth 						break;
32871e1ddd6cScth 					}
32881e1ddd6cScth 					(void) taskq_dispatch(
32891e1ddd6cScth 					    vhci->vhci_update_pathstates_taskq,
32901e1ddd6cScth 					    vhci_update_pathstates,
32911e1ddd6cScth 					    (void *)vlun, KM_SLEEP);
32921e1ddd6cScth 				} else {
32931e1ddd6cScth 					/*
32941e1ddd6cScth 					 * externally initiated failover
32951e1ddd6cScth 					 * has occurred or is in progress
32961e1ddd6cScth 					 */
32971e1ddd6cScth 					VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
32981e1ddd6cScth 					if (!held) {
32991e1ddd6cScth 						/*
33001e1ddd6cScth 						 * looks like some other thread
33011e1ddd6cScth 						 * has already detected this
33021e1ddd6cScth 						 * condition
33031e1ddd6cScth 						 */
33041e1ddd6cScth 						tpkt->pkt_state &=
33051e1ddd6cScth 						    ~STATE_ARQ_DONE;
33061e1ddd6cScth 						*(tpkt->pkt_scbp) =
33071e1ddd6cScth 						    STATUS_BUSY;
33081e1ddd6cScth 						break;
33091e1ddd6cScth 					} else {
33101e1ddd6cScth 						rval = vhci_handle_ext_fo
33111e1ddd6cScth 						    (pkt, rval);
33121e1ddd6cScth 						if (rval == BUSY_RETURN) {
33131e1ddd6cScth 							tpkt->pkt_state &=
33141e1ddd6cScth 							    ~STATE_ARQ_DONE;
33151e1ddd6cScth 							*(tpkt->pkt_scbp) =
33161e1ddd6cScth 							    STATUS_BUSY;
33171e1ddd6cScth 							break;
33181e1ddd6cScth 						}
33191e1ddd6cScth 						bcopy(pkt->pkt_scbp,
33201e1ddd6cScth 						    tpkt->pkt_scbp,
33211e1ddd6cScth 						    vpkt->vpkt_tgt_init_scblen);
33221e1ddd6cScth 						break;
33231e1ddd6cScth 					}
33241e1ddd6cScth 				}
33251e1ddd6cScth 			}
33261e1ddd6cScth 			break;
33271e1ddd6cScth 
33281e1ddd6cScth 		/*
33291e1ddd6cScth 		 * If this is a good SCSI-II RELEASE cmd completion then restore
33301e1ddd6cScth 		 * the load balancing policy and reset VLUN_RESERVE_ACTIVE_FLG.
33311e1ddd6cScth 		 * If this is a good SCSI-II RESERVE cmd completion then set
33321e1ddd6cScth 		 * VLUN_RESERVE_ACTIVE_FLG.
33331e1ddd6cScth 		 */
33341e1ddd6cScth 		case STATUS_GOOD:
33351e1ddd6cScth 			if ((pkt->pkt_cdbp[0] == SCMD_RELEASE) ||
33361e1ddd6cScth 			    (pkt->pkt_cdbp[0] == SCMD_RELEASE_G1)) {
33371e1ddd6cScth 				(void) mdi_set_lb_policy(vlun->svl_dip,
33381e1ddd6cScth 				    vlun->svl_lb_policy_save);
33391e1ddd6cScth 				vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
33401e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, NULL,
33411e1ddd6cScth 				    "!vhci_intr: vlun 0x%p release path 0x%p",
33421e1ddd6cScth 				    (void *)vlun, (void *)vpkt->vpkt_path));
33431e1ddd6cScth 			}
33441e1ddd6cScth 
33451e1ddd6cScth 			if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
33461e1ddd6cScth 			    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
33471e1ddd6cScth 				vlun->svl_flags |= VLUN_RESERVE_ACTIVE_FLG;
33481e1ddd6cScth 				vlun->svl_resrv_pip = vpkt->vpkt_path;
33491e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, NULL,
33501e1ddd6cScth 				    "!vhci_intr: vlun 0x%p reserved path 0x%p",
33511e1ddd6cScth 				    (void *)vlun, (void *)vpkt->vpkt_path));
33521e1ddd6cScth 			}
33531e1ddd6cScth 			break;
33541e1ddd6cScth 
33551e1ddd6cScth 		case STATUS_RESERVATION_CONFLICT:
33561e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL,
33571e1ddd6cScth 			    "!vhci_intr: vlun 0x%p "
33581e1ddd6cScth 			    "reserve conflict on path 0x%p",
33591e1ddd6cScth 			    (void *)vlun, (void *)vpkt->vpkt_path));
33601e1ddd6cScth 			/* FALLTHROUGH */
33611e1ddd6cScth 		default:
33621e1ddd6cScth 			break;
33631e1ddd6cScth 		}
33641e1ddd6cScth 
33651e1ddd6cScth 		/*
33661e1ddd6cScth 		 * Update I/O completion statistics for the path
33671e1ddd6cScth 		 */
33681e1ddd6cScth 		mdi_pi_kstat_iosupdate(vpkt->vpkt_path, vpkt->vpkt_tgt_init_bp);
33691e1ddd6cScth 
33701e1ddd6cScth 		/*
33711e1ddd6cScth 		 * Command completed successfully, release the dma binding and
33721e1ddd6cScth 		 * destroy the transport side of the packet.
33731e1ddd6cScth 		 */
33741e1ddd6cScth 		if ((pkt->pkt_cdbp[0] == SCMD_PROUT) &&
33751e1ddd6cScth 		    (((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
33760205780bSrralphs 		    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE))) {
33771e1ddd6cScth 			if (SCBP_C(pkt) == STATUS_GOOD) {
33781e1ddd6cScth 				ASSERT(vlun->svl_taskq);
33791e1ddd6cScth 				svp->svp_last_pkt_reason = pkt->pkt_reason;
33801e1ddd6cScth 				(void) taskq_dispatch(vlun->svl_taskq,
33811e1ddd6cScth 				    vhci_run_cmd, pkt, KM_SLEEP);
33821e1ddd6cScth 				return;
33831e1ddd6cScth 			}
33841e1ddd6cScth 		}
33851e1ddd6cScth 		if ((SCBP_C(pkt) == STATUS_GOOD) &&
33860205780bSrralphs 		    (pkt->pkt_cdbp[0] == SCMD_PRIN) && vpkt->vpkt_tgt_init_bp) {
33871e1ddd6cScth 			/*
33881e1ddd6cScth 			 * If the action (value in byte 1 of the cdb) is zero,
33891e1ddd6cScth 			 * we're reading keys, and that's the only condition
33901e1ddd6cScth 			 * where we need to be concerned with filtering keys
33911e1ddd6cScth 			 * and potential retries.  Otherwise, we simply signal
33921e1ddd6cScth 			 * the semaphore and move on.
33931e1ddd6cScth 			 */
33941e1ddd6cScth 			if (pkt->pkt_cdbp[1] == 0) {
33951e1ddd6cScth 				/*
33961e1ddd6cScth 				 * If this is the completion of an internal
33971e1ddd6cScth 				 * retry then we need to make sure that the
33981e1ddd6cScth 				 * pkt and tpkt pointers are readjusted so
33991e1ddd6cScth 				 * the calls to scsi_destroy_pkt and pkt_comp
34001e1ddd6cScth 				 * below work * correctly.
34011e1ddd6cScth 				 */
34021e1ddd6cScth 				if (vpkt->vpkt_org_vpkt != NULL) {
34031e1ddd6cScth 					pkt = vpkt->vpkt_org_vpkt->vpkt_hba_pkt;
34041e1ddd6cScth 					tpkt = vpkt->vpkt_org_vpkt->
34051e1ddd6cScth 					    vpkt_tgt_pkt;
34061e1ddd6cScth 
34071e1ddd6cScth 					/*
34081e1ddd6cScth 					 * If this command was issued through
34091e1ddd6cScth 					 * the taskq then we need to clear
34101e1ddd6cScth 					 * this flag for proper processing in
34111e1ddd6cScth 					 * the case of a retry from the target
34121e1ddd6cScth 					 * driver.
34131e1ddd6cScth 					 */
34141e1ddd6cScth 					vpkt->vpkt_state &=
34151e1ddd6cScth 					    ~VHCI_PKT_THRU_TASKQ;
34161e1ddd6cScth 				}
34171e1ddd6cScth 
34181e1ddd6cScth 				/*
34191e1ddd6cScth 				 * if vhci_do_prin returns VHCI_CMD_CMPLT then
34201e1ddd6cScth 				 * vpkt will contain the address of the
34211e1ddd6cScth 				 * original vpkt
34221e1ddd6cScth 				 */
34233f51917eSrralphs 				if (vhci_do_prin(&vpkt) == VHCI_CMD_RETRY) {
34241e1ddd6cScth 					/*
34251e1ddd6cScth 					 * The command has been resent to get
34261e1ddd6cScth 					 * all the keys from the device.  Don't
34271e1ddd6cScth 					 * complete the command with ssd until
34281e1ddd6cScth 					 * the retry completes.
34291e1ddd6cScth 					 */
34301e1ddd6cScth 					return;
34311e1ddd6cScth 				}
34321e1ddd6cScth 			} else {
34331e1ddd6cScth 				sema_v(&vlun->svl_pgr_sema);
34341e1ddd6cScth 			}
34351e1ddd6cScth 		}
34361e1ddd6cScth 
34371e1ddd6cScth 		break;
34381e1ddd6cScth 
34391e1ddd6cScth 	case CMD_TIMEOUT:
34401e1ddd6cScth 		if ((pkt->pkt_statistics &
34411e1ddd6cScth 		    (STAT_BUS_RESET | STAT_DEV_RESET | STAT_ABORTED)) == 0) {
34421e1ddd6cScth 
34431e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
34441e1ddd6cScth 			    "!scsi vhci timeout invoked\n"));
34451e1ddd6cScth 
34461e1ddd6cScth 			(void) vhci_recovery_reset(vlun, &pkt->pkt_address,
34471e1ddd6cScth 			    FALSE, VHCI_DEPTH_ALL);
34481e1ddd6cScth 		}
34491e1ddd6cScth 		MDI_PI_ERRSTAT(lpath, MDI_PI_TRANSERR);
34501e1ddd6cScth 		tpkt->pkt_statistics |= STAT_ABORTED;
34511e1ddd6cScth 		err_str = timeout_err;
34521e1ddd6cScth 		break;
34531e1ddd6cScth 
34541e1ddd6cScth 	case CMD_TRAN_ERR:
34551e1ddd6cScth 		/*
34561e1ddd6cScth 		 * This status is returned if the transport has sent the cmd
34571e1ddd6cScth 		 * down the link to the target and then some error occurs.
34581e1ddd6cScth 		 * In case of SCSI-II RESERVE cmd, we don't know if the
34591e1ddd6cScth 		 * reservation been accepted by the target or not, so we need
34601e1ddd6cScth 		 * to clear the reservation.
34611e1ddd6cScth 		 */
34621e1ddd6cScth 		if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
34631e1ddd6cScth 		    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
34641e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_intr received"
34651e1ddd6cScth 			    " cmd_tran_err for scsi-2 reserve cmd\n"));
34661e1ddd6cScth 			if (!vhci_recovery_reset(vlun, &pkt->pkt_address,
34671e1ddd6cScth 			    TRUE, VHCI_DEPTH_TARGET)) {
34681e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, NULL,
34691e1ddd6cScth 				    "!vhci_intr cmd_tran_err reset failed!"));
34701e1ddd6cScth 			}
34711e1ddd6cScth 		}
34721e1ddd6cScth 		break;
34731e1ddd6cScth 
34741e1ddd6cScth 	case CMD_DEV_GONE:
34754c06356bSdh142964 		/*
34764c06356bSdh142964 		 * If this is the last path then report CMD_DEV_GONE to the
34774c06356bSdh142964 		 * target driver, otherwise report BUSY to triggger retry.
34784c06356bSdh142964 		 */
34794c06356bSdh142964 		if (vlun->svl_dip &&
34804c06356bSdh142964 		    (mdi_client_get_path_count(vlun->svl_dip) <= 1)) {
34814c06356bSdh142964 			struct scsi_vhci	*vhci;
34824c06356bSdh142964 			vhci = ADDR2VHCI(&tpkt->pkt_address);
34834c06356bSdh142964 			VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_intr received "
34844c06356bSdh142964 			    "cmd_dev_gone on last path\n"));
34854c06356bSdh142964 			(void) vhci_invalidate_mpapi_lu(vhci, vlun);
34864c06356bSdh142964 			break;
34874c06356bSdh142964 		}
34884c06356bSdh142964 
34894c06356bSdh142964 		/* Report CMD_CMPLT-with-BUSY to cause retry. */
34900205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_intr received "
34910205780bSrralphs 		    "cmd_dev_gone\n"));
34921e1ddd6cScth 		tpkt->pkt_reason = CMD_CMPLT;
34931e1ddd6cScth 		tpkt->pkt_state = STATE_GOT_BUS |
34941e1ddd6cScth 		    STATE_GOT_TARGET | STATE_SENT_CMD |
34951e1ddd6cScth 		    STATE_GOT_STATUS;
34961e1ddd6cScth 		*(tpkt->pkt_scbp) = STATUS_BUSY;
34971e1ddd6cScth 		break;
34981e1ddd6cScth 
34991e1ddd6cScth 	default:
35001e1ddd6cScth 		break;
35011e1ddd6cScth 	}
35021e1ddd6cScth 
35031e1ddd6cScth 	/*
35041e1ddd6cScth 	 * SCSI-II RESERVE cmd has been serviced by the lower layers clear
35051e1ddd6cScth 	 * the flag so the lun is not QUIESCED any longer.
35061e1ddd6cScth 	 * Also clear the VHCI_PKT_THRU_TASKQ flag, to ensure that if this pkt
35071e1ddd6cScth 	 * is retried, a taskq shall again be dispatched to service it.  Else
35081e1ddd6cScth 	 * it may lead to a system hang if the retry is within interrupt
35091e1ddd6cScth 	 * context.
35101e1ddd6cScth 	 */
35111e1ddd6cScth 	if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
35121e1ddd6cScth 	    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
35131e1ddd6cScth 		vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
35141e1ddd6cScth 		vpkt->vpkt_state &= ~VHCI_PKT_THRU_TASKQ;
35151e1ddd6cScth 	}
35161e1ddd6cScth 
35171e1ddd6cScth 	/*
35181e1ddd6cScth 	 * vpkt_org_vpkt should always be NULL here if the retry command
35191e1ddd6cScth 	 * has been successfully processed.  If vpkt_org_vpkt != NULL at
35201e1ddd6cScth 	 * this point, it is an error so restore the original vpkt and
35211e1ddd6cScth 	 * return an error to the target driver so it can retry the
35221e1ddd6cScth 	 * command as appropriate.
35231e1ddd6cScth 	 */
35241e1ddd6cScth 	if (vpkt->vpkt_org_vpkt != NULL) {
35251e1ddd6cScth 		struct vhci_pkt *new_vpkt = vpkt;
35261e1ddd6cScth 		vpkt = vpkt->vpkt_org_vpkt;
35271e1ddd6cScth 
35281e1ddd6cScth 		vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
35291e1ddd6cScth 		    new_vpkt->vpkt_tgt_pkt);
35301e1ddd6cScth 
35311e1ddd6cScth 		/*
35321e1ddd6cScth 		 * Mark this command completion as having an error so that
35331e1ddd6cScth 		 * ssd will retry the command.
35341e1ddd6cScth 		 */
35351e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_reason = CMD_ABORTED;
35361e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_statistics |= STAT_ABORTED;
35371e1ddd6cScth 
35381e1ddd6cScth 		pkt = vpkt->vpkt_hba_pkt;
35391e1ddd6cScth 		tpkt = vpkt->vpkt_tgt_pkt;
35401e1ddd6cScth 	}
35411e1ddd6cScth 
35421e1ddd6cScth 	if ((err_str != NULL) && (pkt->pkt_reason !=
35431e1ddd6cScth 	    svp->svp_last_pkt_reason)) {
35441e1ddd6cScth 		cdip = vlun->svl_dip;
35451e1ddd6cScth 		vdip = ddi_get_parent(cdip);
35461e1ddd6cScth 		cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
35474c06356bSdh142964 		vhci_log(CE_WARN, vdip, "!%s (%s%d): %s on path %s",
35481e1ddd6cScth 		    ddi_pathname(cdip, cpath), ddi_driver_name(cdip),
35491e1ddd6cScth 		    ddi_get_instance(cdip), err_str,
35504c06356bSdh142964 		    mdi_pi_spathname(vpkt->vpkt_path));
35511e1ddd6cScth 		kmem_free(cpath, MAXPATHLEN);
35521e1ddd6cScth 	}
35531e1ddd6cScth 	svp->svp_last_pkt_reason = pkt->pkt_reason;
35541e1ddd6cScth 	VHCI_DECR_PATH_CMDCOUNT(svp);
35551e1ddd6cScth 
35561e1ddd6cScth 	/*
35571e1ddd6cScth 	 * For PARTIAL_DMA, vhci should not free the path.
35581e1ddd6cScth 	 * Target driver will call into vhci_scsi_dmafree or
35591e1ddd6cScth 	 * destroy pkt to release this path.
35601e1ddd6cScth 	 */
35611e1ddd6cScth 	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
35621e1ddd6cScth 		scsi_destroy_pkt(pkt);
35631e1ddd6cScth 		vpkt->vpkt_hba_pkt = NULL;
35641e1ddd6cScth 		if (vpkt->vpkt_path) {
35651e1ddd6cScth 			mdi_rele_path(vpkt->vpkt_path);
35661e1ddd6cScth 			vpkt->vpkt_path = NULL;
35671e1ddd6cScth 		}
35681e1ddd6cScth 	}
35691e1ddd6cScth 
35709c57abc8Ssrivijitha dugganapalli 	scsi_hba_pkt_comp(tpkt);
35711e1ddd6cScth }
35721e1ddd6cScth 
35731e1ddd6cScth /*
35741e1ddd6cScth  * two possibilities: (1) failover has completed
35751e1ddd6cScth  * or (2) is in progress; update our path states for
35761e1ddd6cScth  * the former case; for the latter case,
35771e1ddd6cScth  * initiate a scsi_watch request to
35781e1ddd6cScth  * determine when failover completes - vlun is HELD
35791e1ddd6cScth  * until failover completes; BUSY is returned to upper
35801e1ddd6cScth  * layer in both the cases
35811e1ddd6cScth  */
35821e1ddd6cScth static int
vhci_handle_ext_fo(struct scsi_pkt * pkt,int fostat)35831e1ddd6cScth vhci_handle_ext_fo(struct scsi_pkt *pkt, int fostat)
35841e1ddd6cScth {
35851e1ddd6cScth 	struct vhci_pkt		*vpkt = (struct vhci_pkt *)pkt->pkt_private;
35861e1ddd6cScth 	struct scsi_pkt		*tpkt;
35871e1ddd6cScth 	scsi_vhci_priv_t	*svp;
35881e1ddd6cScth 	scsi_vhci_lun_t		*vlun;
35891e1ddd6cScth 	struct scsi_vhci	*vhci;
35901e1ddd6cScth 	scsi_vhci_swarg_t	*swarg;
35911e1ddd6cScth 	char			*path;
35921e1ddd6cScth 
35931e1ddd6cScth 	ASSERT(vpkt != NULL);
35941e1ddd6cScth 	tpkt = vpkt->vpkt_tgt_pkt;
35951e1ddd6cScth 	ASSERT(tpkt != NULL);
35961e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(vpkt->vpkt_path);
35971e1ddd6cScth 	ASSERT(svp != NULL);
35981e1ddd6cScth 	vlun = svp->svp_svl;
35991e1ddd6cScth 	ASSERT(vlun != NULL);
36001e1ddd6cScth 	ASSERT(VHCI_LUN_IS_HELD(vlun));
36011e1ddd6cScth 
36021e1ddd6cScth 	vhci = ADDR2VHCI(&tpkt->pkt_address);
36031e1ddd6cScth 
36041e1ddd6cScth 	if (fostat == SCSI_SENSE_INACTIVE) {
36051e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Failover "
36061e1ddd6cScth 		    "detected for %s; updating path states...\n",
36071e1ddd6cScth 		    vlun->svl_lun_wwn));
36081e1ddd6cScth 		/*
36091e1ddd6cScth 		 * set the vlun flag to indicate to the task that the target
36101e1ddd6cScth 		 * port group needs updating
36111e1ddd6cScth 		 */
36121e1ddd6cScth 		vlun->svl_flags |= VLUN_UPDATE_TPG;
36131e1ddd6cScth 		(void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
36141e1ddd6cScth 		    vhci_update_pathstates, (void *)vlun, KM_SLEEP);
36151e1ddd6cScth 	} else {
36161e1ddd6cScth 		path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
36171e1ddd6cScth 		vhci_log(CE_NOTE, ddi_get_parent(vlun->svl_dip),
36181e1ddd6cScth 		    "!%s (%s%d): Waiting for externally initiated failover "
36191e1ddd6cScth 		    "to complete", ddi_pathname(vlun->svl_dip, path),
36201e1ddd6cScth 		    ddi_driver_name(vlun->svl_dip),
36211e1ddd6cScth 		    ddi_get_instance(vlun->svl_dip));
36221e1ddd6cScth 		kmem_free(path, MAXPATHLEN);
36231e1ddd6cScth 		swarg = kmem_alloc(sizeof (*swarg), KM_NOSLEEP);
36241e1ddd6cScth 		if (swarg == NULL) {
36251e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_handle_ext_fo: "
36261e1ddd6cScth 			    "request packet allocation for %s failed....\n",
36271e1ddd6cScth 			    vlun->svl_lun_wwn));
36281e1ddd6cScth 			VHCI_RELEASE_LUN(vlun);
36291e1ddd6cScth 			return (PKT_RETURN);
36301e1ddd6cScth 		}
36311e1ddd6cScth 		swarg->svs_svp = svp;
3632aa6577ebSJosef 'Jeff' Sipek 		swarg->svs_tos = gethrtime();
36331e1ddd6cScth 		swarg->svs_pi = vpkt->vpkt_path;
36341e1ddd6cScth 		swarg->svs_release_lun = 0;
36351e1ddd6cScth 		swarg->svs_done = 0;
36361e1ddd6cScth 		/*
36371e1ddd6cScth 		 * place a hold on the path...we don't want it to
36381e1ddd6cScth 		 * vanish while scsi_watch is in progress
36391e1ddd6cScth 		 */
36401e1ddd6cScth 		mdi_hold_path(vpkt->vpkt_path);
36411e1ddd6cScth 		svp->svp_sw_token = scsi_watch_request_submit(svp->svp_psd,
36421e1ddd6cScth 		    VHCI_FOWATCH_INTERVAL, SENSE_LENGTH, vhci_efo_watch_cb,
36431e1ddd6cScth 		    (caddr_t)swarg);
36441e1ddd6cScth 	}
36451e1ddd6cScth 	return (BUSY_RETURN);
36461e1ddd6cScth }
36471e1ddd6cScth 
36481e1ddd6cScth /*
36491e1ddd6cScth  * vhci_efo_watch_cb:
36501e1ddd6cScth  *	Callback from scsi_watch request to check the failover status.
36511e1ddd6cScth  *	Completion is either due to successful failover or timeout.
36521e1ddd6cScth  *	Upon successful completion, vhci_update_path_states is called.
36531e1ddd6cScth  *	For timeout condition, vhci_efo_done is called.
36541e1ddd6cScth  *	Always returns 0 to scsi_watch to keep retrying till vhci_efo_done
36551e1ddd6cScth  *	terminates this request properly in a separate thread.
36561e1ddd6cScth  */
36571e1ddd6cScth 
36581e1ddd6cScth static int
vhci_efo_watch_cb(caddr_t arg,struct scsi_watch_result * resultp)36591e1ddd6cScth vhci_efo_watch_cb(caddr_t arg, struct scsi_watch_result *resultp)
36601e1ddd6cScth {
36611e1ddd6cScth 	struct scsi_status		*statusp = resultp->statusp;
36620c45178bSwl202157@icefox 	uint8_t				*sensep = (uint8_t *)resultp->sensep;
36631e1ddd6cScth 	struct scsi_pkt			*pkt = resultp->pkt;
36641e1ddd6cScth 	scsi_vhci_swarg_t		*swarg;
36651e1ddd6cScth 	scsi_vhci_priv_t		*svp;
36661e1ddd6cScth 	scsi_vhci_lun_t			*vlun;
36671e1ddd6cScth 	struct scsi_vhci		*vhci;
36681e1ddd6cScth 	dev_info_t			*vdip;
36691e1ddd6cScth 	int				rval, updt_paths;
36701e1ddd6cScth 
36711e1ddd6cScth 	swarg = (scsi_vhci_swarg_t *)(uintptr_t)arg;
36721e1ddd6cScth 	svp = swarg->svs_svp;
36731e1ddd6cScth 	if (swarg->svs_done) {
36741e1ddd6cScth 		/*
36751e1ddd6cScth 		 * Already completed failover or timedout.
36761e1ddd6cScth 		 * Waiting for vhci_efo_done to terminate this scsi_watch.
36771e1ddd6cScth 		 */
36781e1ddd6cScth 		return (0);
36791e1ddd6cScth 	}
36801e1ddd6cScth 
36811e1ddd6cScth 	ASSERT(svp != NULL);
36821e1ddd6cScth 	vlun = svp->svp_svl;
36831e1ddd6cScth 	ASSERT(vlun != NULL);
36841e1ddd6cScth 	ASSERT(VHCI_LUN_IS_HELD(vlun));
36851e1ddd6cScth 	vlun->svl_efo_update_path = 0;
36861e1ddd6cScth 	vdip = ddi_get_parent(vlun->svl_dip);
36871e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate,
36881e1ddd6cScth 	    ddi_get_instance(vdip));
36891e1ddd6cScth 
36901e1ddd6cScth 	updt_paths = 0;
36911e1ddd6cScth 
36921e1ddd6cScth 	if (pkt->pkt_reason != CMD_CMPLT) {
3693aa6577ebSJosef 'Jeff' Sipek 		if ((gethrtime() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
36941e1ddd6cScth 			swarg->svs_release_lun = 1;
36951e1ddd6cScth 			goto done;
36961e1ddd6cScth 		}
36971e1ddd6cScth 		return (0);
36981e1ddd6cScth 	}
36991e1ddd6cScth 	if (*((unsigned char *)statusp) == STATUS_CHECK) {
37000205780bSrralphs 		rval = vlun->svl_fops->sfo_analyze_sense(svp->svp_psd, sensep,
37010205780bSrralphs 		    vlun->svl_fops_ctpriv);
37021e1ddd6cScth 		switch (rval) {
37031e1ddd6cScth 			/*
37041e1ddd6cScth 			 * Only update path states in case path is definitely
37051e1ddd6cScth 			 * inactive, or no failover occurred.  For all other
37061e1ddd6cScth 			 * check conditions continue pinging.  A unexpected
37071e1ddd6cScth 			 * check condition shouldn't cause pinging to complete
37081e1ddd6cScth 			 * prematurely.
37091e1ddd6cScth 			 */
37101e1ddd6cScth 			case SCSI_SENSE_INACTIVE:
37111e1ddd6cScth 			case SCSI_SENSE_NOFAILOVER:
37121e1ddd6cScth 				updt_paths = 1;
37131e1ddd6cScth 				break;
37141e1ddd6cScth 			default:
3715aa6577ebSJosef 'Jeff' Sipek 				if ((gethrtime() - swarg->svs_tos)
37161e1ddd6cScth 				    >= VHCI_EXTFO_TIMEOUT) {
37171e1ddd6cScth 					swarg->svs_release_lun = 1;
37181e1ddd6cScth 					goto done;
37191e1ddd6cScth 				}
37201e1ddd6cScth 				return (0);
37211e1ddd6cScth 		}
37221e1ddd6cScth 	} else if (*((unsigned char *)statusp) ==
37231e1ddd6cScth 	    STATUS_RESERVATION_CONFLICT) {
37241e1ddd6cScth 		updt_paths = 1;
37251e1ddd6cScth 	} else if ((*((unsigned char *)statusp)) &
37261e1ddd6cScth 	    (STATUS_BUSY | STATUS_QFULL)) {
37271e1ddd6cScth 		return (0);
37281e1ddd6cScth 	}
37291e1ddd6cScth 	if ((*((unsigned char *)statusp) == STATUS_GOOD) ||
37301e1ddd6cScth 	    (updt_paths == 1)) {
37311e1ddd6cScth 		/*
37321e1ddd6cScth 		 * we got here because we had detected an
37331e1ddd6cScth 		 * externally initiated failover; things
37341e1ddd6cScth 		 * have settled down now, so let's
37351e1ddd6cScth 		 * start up a task to update the
37361e1ddd6cScth 		 * path states and target port group
37371e1ddd6cScth 		 */
37381e1ddd6cScth 		vlun->svl_efo_update_path = 1;
37391e1ddd6cScth 		swarg->svs_done = 1;
37401e1ddd6cScth 		vlun->svl_swarg = swarg;
37411e1ddd6cScth 		vlun->svl_flags |= VLUN_UPDATE_TPG;
37421e1ddd6cScth 		(void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
37431e1ddd6cScth 		    vhci_update_pathstates, (void *)vlun,
37441e1ddd6cScth 		    KM_SLEEP);
37451e1ddd6cScth 		return (0);
37461e1ddd6cScth 	}
3747aa6577ebSJosef 'Jeff' Sipek 	if ((gethrtime() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
37481e1ddd6cScth 		swarg->svs_release_lun = 1;
37491e1ddd6cScth 		goto done;
37501e1ddd6cScth 	}
37511e1ddd6cScth 	return (0);
37521e1ddd6cScth done:
37531e1ddd6cScth 	swarg->svs_done = 1;
37541e1ddd6cScth 	(void) taskq_dispatch(vhci->vhci_taskq,
37551e1ddd6cScth 	    vhci_efo_done, (void *)swarg, KM_SLEEP);
37561e1ddd6cScth 	return (0);
37571e1ddd6cScth }
37581e1ddd6cScth 
37591e1ddd6cScth /*
37601e1ddd6cScth  * vhci_efo_done:
37611e1ddd6cScth  *	cleanly terminates scsi_watch and free up resources.
37621e1ddd6cScth  *	Called as taskq function in vhci_efo_watch_cb for EFO timeout condition
37631e1ddd6cScth  *	or by vhci_update_path_states invoked during external initiated
37641e1ddd6cScth  *	failover completion.
37651e1ddd6cScth  */
37661e1ddd6cScth static void
vhci_efo_done(void * arg)37671e1ddd6cScth vhci_efo_done(void *arg)
37681e1ddd6cScth {
37691e1ddd6cScth 	scsi_vhci_lun_t			*vlun;
37701e1ddd6cScth 	scsi_vhci_swarg_t		*swarg = (scsi_vhci_swarg_t *)arg;
37711e1ddd6cScth 	scsi_vhci_priv_t		*svp = swarg->svs_svp;
37721e1ddd6cScth 	ASSERT(svp);
37731e1ddd6cScth 
37741e1ddd6cScth 	vlun = svp->svp_svl;
37751e1ddd6cScth 	ASSERT(vlun);
37761e1ddd6cScth 
37771e1ddd6cScth 	/* Wait for clean termination of scsi_watch */
37781e1ddd6cScth 	(void) scsi_watch_request_terminate(svp->svp_sw_token,
3779b50431b8Stl223370 	    SCSI_WATCH_TERMINATE_ALL_WAIT);
37801e1ddd6cScth 	svp->svp_sw_token = NULL;
37811e1ddd6cScth 
37821e1ddd6cScth 	/* release path and freeup resources to indicate failover completion */
37831e1ddd6cScth 	mdi_rele_path(swarg->svs_pi);
37841e1ddd6cScth 	if (swarg->svs_release_lun) {
37851e1ddd6cScth 		VHCI_RELEASE_LUN(vlun);
37861e1ddd6cScth 	}
37871e1ddd6cScth 	kmem_free((void *)swarg, sizeof (*swarg));
37881e1ddd6cScth }
37891e1ddd6cScth 
37901e1ddd6cScth /*
37911e1ddd6cScth  * Update the path states
37921e1ddd6cScth  * vlun should be HELD when this is invoked.
37931e1ddd6cScth  * Calls vhci_efo_done to cleanup resources allocated for EFO.
37941e1ddd6cScth  */
37951e1ddd6cScth void
vhci_update_pathstates(void * arg)37961e1ddd6cScth vhci_update_pathstates(void *arg)
37971e1ddd6cScth {
37981e1ddd6cScth 	mdi_pathinfo_t			*pip, *npip;
37994c06356bSdh142964 	dev_info_t			*dip;
38001e1ddd6cScth 	struct scsi_failover_ops	*fo;
38011e1ddd6cScth 	struct scsi_vhci_priv		*svp;
38021e1ddd6cScth 	struct scsi_device		*psd;
38031e1ddd6cScth 	struct scsi_path_opinfo		opinfo;
38041e1ddd6cScth 	char				*pclass, *tptr;
38051e1ddd6cScth 	struct scsi_vhci_lun		*vlun = (struct scsi_vhci_lun *)arg;
38061e1ddd6cScth 	int				sps; /* mdi_select_path() status */
38074c06356bSdh142964 	char				*cpath;
38081e1ddd6cScth 	struct scsi_vhci		*vhci;
38091e1ddd6cScth 	struct scsi_pkt			*pkt;
38101e1ddd6cScth 	struct buf			*bp;
38111c8dbe37SMilos Muzik 	struct scsi_vhci_priv		*svp_conflict = NULL;
3812*28c5054dSJason King 	size_t				blksize;
38131e1ddd6cScth 
38141e1ddd6cScth 	ASSERT(VHCI_LUN_IS_HELD(vlun));
38151e1ddd6cScth 	dip  = vlun->svl_dip;
38161e1ddd6cScth 	pip = npip = NULL;
38171e1ddd6cScth 
38181e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate,
38191e1ddd6cScth 	    ddi_get_instance(ddi_get_parent(dip)));
38201e1ddd6cScth 
38211e1ddd6cScth 	sps = mdi_select_path(dip, NULL, (MDI_SELECT_ONLINE_PATH |
3822b08fdaf7SSheshadri Vasudevan 	    MDI_SELECT_STANDBY_PATH | MDI_SELECT_NO_PREFERRED), NULL, &npip);
38231e1ddd6cScth 	if ((npip == NULL) || (sps != MDI_SUCCESS)) {
38241e1ddd6cScth 		goto done;
38251e1ddd6cScth 	}
38261e1ddd6cScth 
3827*28c5054dSJason King 	blksize = vhci_get_blocksize(dip);
3828*28c5054dSJason King 
38291e1ddd6cScth 	fo = vlun->svl_fops;
38301e1ddd6cScth 	do {
38311e1ddd6cScth 		pip = npip;
38321e1ddd6cScth 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
38331e1ddd6cScth 		psd = svp->svp_psd;
38340205780bSrralphs 		if (fo->sfo_path_get_opinfo(psd, &opinfo,
38351e1ddd6cScth 		    vlun->svl_fops_ctpriv) != 0) {
38361e1ddd6cScth 			sps = mdi_select_path(dip, NULL,
3837b08fdaf7SSheshadri Vasudevan 			    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH |
3838b08fdaf7SSheshadri Vasudevan 			    MDI_SELECT_NO_PREFERRED), pip, &npip);
38391e1ddd6cScth 			mdi_rele_path(pip);
38401e1ddd6cScth 			continue;
38411e1ddd6cScth 		}
38421e1ddd6cScth 
38431e1ddd6cScth 		if (mdi_prop_lookup_string(pip, "path-class", &pclass) !=
38441e1ddd6cScth 		    MDI_SUCCESS) {
38451e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
38461e1ddd6cScth 			    "!vhci_update_pathstates: prop lookup failed for "
38471e1ddd6cScth 			    "path 0x%p\n", (void *)pip));
38481e1ddd6cScth 			sps = mdi_select_path(dip, NULL,
3849b08fdaf7SSheshadri Vasudevan 			    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH |
3850b08fdaf7SSheshadri Vasudevan 			    MDI_SELECT_NO_PREFERRED), pip, &npip);
38511e1ddd6cScth 			mdi_rele_path(pip);
38521e1ddd6cScth 			continue;
38531e1ddd6cScth 		}
38541e1ddd6cScth 
38551e1ddd6cScth 		/*
38561e1ddd6cScth 		 * Need to update the "path-class" property
38571e1ddd6cScth 		 * value in the device tree if different
38581e1ddd6cScth 		 * from the existing value.
38591e1ddd6cScth 		 */
38601e1ddd6cScth 		if (strcmp(pclass, opinfo.opinfo_path_attr) != 0) {
38611e1ddd6cScth 			(void) mdi_prop_update_string(pip, "path-class",
38621e1ddd6cScth 			    opinfo.opinfo_path_attr);
38631e1ddd6cScth 		}
38641e1ddd6cScth 
38651e1ddd6cScth 		/*
38661e1ddd6cScth 		 * Only change the state if needed. i.e. Don't call
38671e1ddd6cScth 		 * mdi_pi_set_state to ONLINE a path if its already
38681e1ddd6cScth 		 * ONLINE. Same for STANDBY paths.
38691e1ddd6cScth 		 */
38701e1ddd6cScth 
38711e1ddd6cScth 		if ((opinfo.opinfo_path_state == SCSI_PATH_ACTIVE ||
38721e1ddd6cScth 		    opinfo.opinfo_path_state == SCSI_PATH_ACTIVE_NONOPT)) {
38731e1ddd6cScth 			if (!(MDI_PI_IS_ONLINE(pip))) {
38741e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
38751e1ddd6cScth 				    "!vhci_update_pathstates: marking path"
38761e1ddd6cScth 				    " 0x%p as ONLINE\n", (void *)pip));
38771e1ddd6cScth 				cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
38781e1ddd6cScth 				vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s "
38794c06356bSdh142964 				    "(%s%d): path %s "
38801e1ddd6cScth 				    "is now ONLINE because of "
38811e1ddd6cScth 				    "an externally initiated failover",
38821e1ddd6cScth 				    ddi_pathname(dip, cpath),
38831e1ddd6cScth 				    ddi_driver_name(dip),
38841e1ddd6cScth 				    ddi_get_instance(dip),
38854c06356bSdh142964 				    mdi_pi_spathname(pip));
38861e1ddd6cScth 				kmem_free(cpath, MAXPATHLEN);
38871e1ddd6cScth 				mdi_pi_set_state(pip,
38881e1ddd6cScth 				    MDI_PATHINFO_STATE_ONLINE);
38891e1ddd6cScth 				mdi_pi_set_preferred(pip,
38901e1ddd6cScth 				    opinfo.opinfo_preferred);
38911e1ddd6cScth 				tptr = kmem_alloc(strlen
38921e1ddd6cScth 				    (opinfo.opinfo_path_attr) + 1, KM_SLEEP);
38931e1ddd6cScth 				(void) strlcpy(tptr, opinfo.opinfo_path_attr,
38941e1ddd6cScth 				    (strlen(opinfo.opinfo_path_attr) + 1));
38951e1ddd6cScth 				mutex_enter(&vlun->svl_mutex);
38961e1ddd6cScth 				if (vlun->svl_active_pclass != NULL) {
38971e1ddd6cScth 					kmem_free(vlun->svl_active_pclass,
38984e40636aSToomas Soome 					    strlen(vlun->svl_active_pclass) +
38994e40636aSToomas Soome 					    1);
39001e1ddd6cScth 				}
39011e1ddd6cScth 				vlun->svl_active_pclass = tptr;
39021e1ddd6cScth 				if (vlun->svl_waiting_for_activepath) {
39031e1ddd6cScth 					vlun->svl_waiting_for_activepath = 0;
39041e1ddd6cScth 				}
39051e1ddd6cScth 				mutex_exit(&vlun->svl_mutex);
39061e1ddd6cScth 			} else if (MDI_PI_IS_ONLINE(pip)) {
39071e1ddd6cScth 				if (strcmp(pclass, opinfo.opinfo_path_attr)
39081e1ddd6cScth 				    != 0) {
39091e1ddd6cScth 					mdi_pi_set_preferred(pip,
39101e1ddd6cScth 					    opinfo.opinfo_preferred);
39111e1ddd6cScth 					mutex_enter(&vlun->svl_mutex);
39121e1ddd6cScth 					if (vlun->svl_active_pclass == NULL ||
39131e1ddd6cScth 					    strcmp(opinfo.opinfo_path_attr,
39141e1ddd6cScth 					    vlun->svl_active_pclass) != 0) {
39151e1ddd6cScth 						mutex_exit(&vlun->svl_mutex);
39161e1ddd6cScth 						tptr = kmem_alloc(strlen
39174e40636aSToomas Soome 						    (opinfo.opinfo_path_attr) +
39184e40636aSToomas Soome 						    1, KM_SLEEP);
39191e1ddd6cScth 						(void) strlcpy(tptr,
39201e1ddd6cScth 						    opinfo.opinfo_path_attr,
39211e1ddd6cScth 						    (strlen
39221e1ddd6cScth 						    (opinfo.opinfo_path_attr)
39231e1ddd6cScth 						    + 1));
39241e1ddd6cScth 						mutex_enter(&vlun->svl_mutex);
39251e1ddd6cScth 					} else {
39261e1ddd6cScth 						/*
39271e1ddd6cScth 						 * No need to update
39281e1ddd6cScth 						 * svl_active_pclass
39291e1ddd6cScth 						 */
39301e1ddd6cScth 						tptr = NULL;
39311e1ddd6cScth 						mutex_exit(&vlun->svl_mutex);
39321e1ddd6cScth 					}
39331e1ddd6cScth 					if (tptr) {
39341e1ddd6cScth 						if (vlun->svl_active_pclass
39351e1ddd6cScth 						    != NULL) {
39361e1ddd6cScth 							kmem_free(vlun->
39371e1ddd6cScth 							    svl_active_pclass,
39381e1ddd6cScth 							    strlen(vlun->
39391e1ddd6cScth 							    svl_active_pclass)
39401e1ddd6cScth 							    + 1);
39411e1ddd6cScth 						}
39421e1ddd6cScth 						vlun->svl_active_pclass = tptr;
39431e1ddd6cScth 						mutex_exit(&vlun->svl_mutex);
39441e1ddd6cScth 					}
39451e1ddd6cScth 				}
39461e1ddd6cScth 			}
39471c8dbe37SMilos Muzik 
39481c8dbe37SMilos Muzik 			/* Check for Reservation Conflict */
39491c8dbe37SMilos Muzik 			bp = scsi_alloc_consistent_buf(
39501c8dbe37SMilos Muzik 			    &svp->svp_psd->sd_address, (struct buf *)NULL,
3951*28c5054dSJason King 			    blksize, B_READ, NULL, NULL);
39521c8dbe37SMilos Muzik 			if (!bp) {
39531c8dbe37SMilos Muzik 				VHCI_DEBUG(1, (CE_NOTE, NULL,
39541c8dbe37SMilos Muzik 				    "!vhci_update_pathstates: No resources "
39551c8dbe37SMilos Muzik 				    "(buf)\n"));
39561c8dbe37SMilos Muzik 				mdi_rele_path(pip);
39571c8dbe37SMilos Muzik 				goto done;
39581c8dbe37SMilos Muzik 			}
39591c8dbe37SMilos Muzik 			pkt = scsi_init_pkt(&svp->svp_psd->sd_address, NULL, bp,
39601c8dbe37SMilos Muzik 			    CDB_GROUP1, sizeof (struct scsi_arq_status), 0,
39611c8dbe37SMilos Muzik 			    PKT_CONSISTENT, NULL, NULL);
39621c8dbe37SMilos Muzik 			if (pkt) {
39631c8dbe37SMilos Muzik 				(void) scsi_setup_cdb((union scsi_cdb *)
3964*28c5054dSJason King 				    (uintptr_t)pkt->pkt_cdbp, SCMD_READ_G1, 1,
3965*28c5054dSJason King 				    1, 0);
39661c8dbe37SMilos Muzik 				pkt->pkt_time = 3 * 30;
39671c8dbe37SMilos Muzik 				pkt->pkt_flags = FLAG_NOINTR;
39681c8dbe37SMilos Muzik 				pkt->pkt_path_instance =
39691c8dbe37SMilos Muzik 				    mdi_pi_get_path_instance(pip);
39701c8dbe37SMilos Muzik 
39711c8dbe37SMilos Muzik 				if ((scsi_transport(pkt) == TRAN_ACCEPT) &&
39721c8dbe37SMilos Muzik 				    (pkt->pkt_reason == CMD_CMPLT) &&
39731c8dbe37SMilos Muzik 				    (SCBP_C(pkt) ==
39741c8dbe37SMilos Muzik 				    STATUS_RESERVATION_CONFLICT)) {
39751c8dbe37SMilos Muzik 					VHCI_DEBUG(1, (CE_NOTE, NULL,
39761c8dbe37SMilos Muzik 					    "!vhci_update_pathstates: reserv. "
39771c8dbe37SMilos Muzik 					    "conflict to be resolved on 0x%p\n",
39781c8dbe37SMilos Muzik 					    (void *)pip));
39791c8dbe37SMilos Muzik 					svp_conflict = svp;
39801c8dbe37SMilos Muzik 				}
39811c8dbe37SMilos Muzik 				scsi_destroy_pkt(pkt);
39821c8dbe37SMilos Muzik 			}
39831c8dbe37SMilos Muzik 			scsi_free_consistent_buf(bp);
39841e1ddd6cScth 		} else if ((opinfo.opinfo_path_state == SCSI_PATH_INACTIVE) &&
39851e1ddd6cScth 		    !(MDI_PI_IS_STANDBY(pip))) {
39861e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
39871e1ddd6cScth 			    "!vhci_update_pathstates: marking path"
39881e1ddd6cScth 			    " 0x%p as STANDBY\n", (void *)pip));
39891e1ddd6cScth 			cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
39901e1ddd6cScth 			vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s "
39914c06356bSdh142964 			    "(%s%d): path %s "
39921e1ddd6cScth 			    "is now STANDBY because of "
39931e1ddd6cScth 			    "an externally initiated failover",
39941e1ddd6cScth 			    ddi_pathname(dip, cpath),
39951e1ddd6cScth 			    ddi_driver_name(dip),
39961e1ddd6cScth 			    ddi_get_instance(dip),
39974c06356bSdh142964 			    mdi_pi_spathname(pip));
39981e1ddd6cScth 			kmem_free(cpath, MAXPATHLEN);
39991e1ddd6cScth 			mdi_pi_set_state(pip,
40001e1ddd6cScth 			    MDI_PATHINFO_STATE_STANDBY);
40011e1ddd6cScth 			mdi_pi_set_preferred(pip,
40021e1ddd6cScth 			    opinfo.opinfo_preferred);
40031e1ddd6cScth 			mutex_enter(&vlun->svl_mutex);
40041e1ddd6cScth 			if (vlun->svl_active_pclass != NULL) {
40051e1ddd6cScth 				if (strcmp(vlun->svl_active_pclass,
40061e1ddd6cScth 				    opinfo.opinfo_path_attr) == 0) {
40071e1ddd6cScth 					kmem_free(vlun->
40081e1ddd6cScth 					    svl_active_pclass,
40091e1ddd6cScth 					    strlen(vlun->
40101e1ddd6cScth 					    svl_active_pclass) + 1);
40111e1ddd6cScth 					vlun->svl_active_pclass = NULL;
40121e1ddd6cScth 				}
40131e1ddd6cScth 			}
40141e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
40151e1ddd6cScth 		}
40161e1ddd6cScth 		(void) mdi_prop_free(pclass);
40171e1ddd6cScth 		sps = mdi_select_path(dip, NULL,
4018b08fdaf7SSheshadri Vasudevan 		    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH |
4019b08fdaf7SSheshadri Vasudevan 		    MDI_SELECT_NO_PREFERRED), pip, &npip);
40201e1ddd6cScth 		mdi_rele_path(pip);
40211e1ddd6cScth 
40221e1ddd6cScth 	} while ((npip != NULL) && (sps == MDI_SUCCESS));
40231e1ddd6cScth 
40241e1ddd6cScth 	/*
40251e1ddd6cScth 	 * Check to see if this vlun has an active SCSI-II RESERVE.  If so
40261e1ddd6cScth 	 * clear the reservation by sending a reset, so the host doesn't
40271c8dbe37SMilos Muzik 	 * receive a reservation conflict.  The reset has to be sent via a
40281c8dbe37SMilos Muzik 	 * working path.  Let's use a path referred to by svp_conflict as it
40291c8dbe37SMilos Muzik 	 * should be working.
40301e1ddd6cScth 	 * Reset VLUN_RESERVE_ACTIVE_FLG for this vlun.  Also notify ssd
40311e1ddd6cScth 	 * of the reset, explicitly.
40321e1ddd6cScth 	 */
40331e1ddd6cScth 	if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
40341c8dbe37SMilos Muzik 		if (svp_conflict && (vlun->svl_xlf_capable == 0)) {
40351c8dbe37SMilos Muzik 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_update_pathstates:"
40361c8dbe37SMilos Muzik 			    " sending recovery reset on 0x%p, path_state: %x",
40371c8dbe37SMilos Muzik 			    svp_conflict->svp_psd->sd_private,
40381c8dbe37SMilos Muzik 			    mdi_pi_get_state((mdi_pathinfo_t *)
40391c8dbe37SMilos Muzik 			    svp_conflict->svp_psd->sd_private)));
40401c8dbe37SMilos Muzik 
40411e1ddd6cScth 			(void) vhci_recovery_reset(vlun,
40421c8dbe37SMilos Muzik 			    &svp_conflict->svp_psd->sd_address, FALSE,
40431e1ddd6cScth 			    VHCI_DEPTH_TARGET);
40441e1ddd6cScth 		}
40451e1ddd6cScth 		vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
40461e1ddd6cScth 		mutex_enter(&vhci->vhci_mutex);
40471e1ddd6cScth 		scsi_hba_reset_notify_callback(&vhci->vhci_mutex,
40481e1ddd6cScth 		    &vhci->vhci_reset_notify_listf);
40491e1ddd6cScth 		mutex_exit(&vhci->vhci_mutex);
40501e1ddd6cScth 	}
40511e1ddd6cScth 	if (vlun->svl_flags & VLUN_UPDATE_TPG) {
40521e1ddd6cScth 		/*
40531e1ddd6cScth 		 * Update the AccessState of related MP-API TPGs
40541e1ddd6cScth 		 */
40551e1ddd6cScth 		(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci, vlun);
40561e1ddd6cScth 		vlun->svl_flags &= ~VLUN_UPDATE_TPG;
40571e1ddd6cScth 	}
40581e1ddd6cScth done:
40591e1ddd6cScth 	if (vlun->svl_efo_update_path) {
40601e1ddd6cScth 		vlun->svl_efo_update_path = 0;
40611e1ddd6cScth 		vhci_efo_done(vlun->svl_swarg);
40621e1ddd6cScth 		vlun->svl_swarg = 0;
40631e1ddd6cScth 	}
40641e1ddd6cScth 	VHCI_RELEASE_LUN(vlun);
40651e1ddd6cScth }
40661e1ddd6cScth 
40671e1ddd6cScth /* ARGSUSED */
40681e1ddd6cScth static int
vhci_pathinfo_init(dev_info_t * vdip,mdi_pathinfo_t * pip,int flags)40691e1ddd6cScth vhci_pathinfo_init(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
40701e1ddd6cScth {
40711e1ddd6cScth 	scsi_hba_tran_t		*hba = NULL;
40721e1ddd6cScth 	struct scsi_device	*psd = NULL;
40731e1ddd6cScth 	scsi_vhci_lun_t		*vlun = NULL;
40741e1ddd6cScth 	dev_info_t		*pdip = NULL;
40751e1ddd6cScth 	dev_info_t		*tgt_dip;
40761e1ddd6cScth 	struct scsi_vhci	*vhci;
40771e1ddd6cScth 	char			*guid;
40781e1ddd6cScth 	scsi_vhci_priv_t	*svp = NULL;
40791e1ddd6cScth 	int			rval = MDI_FAILURE;
40801e1ddd6cScth 	int			vlun_alloced = 0;
40811e1ddd6cScth 
40821e1ddd6cScth 	ASSERT(vdip != NULL);
40831e1ddd6cScth 	ASSERT(pip != NULL);
40841e1ddd6cScth 
40851e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
40861e1ddd6cScth 	ASSERT(vhci != NULL);
40871e1ddd6cScth 
40881e1ddd6cScth 	pdip = mdi_pi_get_phci(pip);
40891e1ddd6cScth 	ASSERT(pdip != NULL);
40901e1ddd6cScth 
40911e1ddd6cScth 	hba = ddi_get_driver_private(pdip);
40921e1ddd6cScth 	ASSERT(hba != NULL);
40931e1ddd6cScth 
40941e1ddd6cScth 	tgt_dip = mdi_pi_get_client(pip);
40951e1ddd6cScth 	ASSERT(tgt_dip != NULL);
40961e1ddd6cScth 
40971e1ddd6cScth 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip, PROPFLAGS,
40981e1ddd6cScth 	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
40991e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, NULL,
41001e1ddd6cScth 		    "vhci_pathinfo_init: lun guid property failed"));
41011e1ddd6cScth 		goto failure;
41021e1ddd6cScth 	}
41031e1ddd6cScth 
41041e1ddd6cScth 	vlun = vhci_lun_lookup_alloc(tgt_dip, guid, &vlun_alloced);
41051e1ddd6cScth 	ddi_prop_free(guid);
41061e1ddd6cScth 
41071e1ddd6cScth 	vlun->svl_dip = tgt_dip;
41081e1ddd6cScth 
41091e1ddd6cScth 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
41101e1ddd6cScth 	svp->svp_svl = vlun;
41111e1ddd6cScth 
41121c8dbe37SMilos Muzik 	/*
41131c8dbe37SMilos Muzik 	 * Initialize svl_lb_policy_save only for newly allocated vlun. Writing
41141c8dbe37SMilos Muzik 	 * to svl_lb_policy_save later could accidentally overwrite saved lb
41151c8dbe37SMilos Muzik 	 * policy.
41161c8dbe37SMilos Muzik 	 */
41171c8dbe37SMilos Muzik 	if (vlun_alloced) {
41181e1ddd6cScth 		vlun->svl_lb_policy_save = mdi_get_lb_policy(tgt_dip);
41191c8dbe37SMilos Muzik 	}
41201c8dbe37SMilos Muzik 
41211e1ddd6cScth 	mutex_init(&svp->svp_mutex, NULL, MUTEX_DRIVER, NULL);
41221e1ddd6cScth 	cv_init(&svp->svp_cv, NULL, CV_DRIVER, NULL);
41231e1ddd6cScth 
41241e1ddd6cScth 	psd = kmem_zalloc(sizeof (*psd), KM_SLEEP);
41251e1ddd6cScth 	mutex_init(&psd->sd_mutex, NULL, MUTEX_DRIVER, NULL);
41261e1ddd6cScth 
412753a7b6b6SChris Horne 	if (hba->tran_hba_flags & SCSI_HBA_ADDR_COMPLEX) {
412853a7b6b6SChris Horne 		/*
412953a7b6b6SChris Horne 		 * For a SCSI_HBA_ADDR_COMPLEX transport we store a pointer to
413053a7b6b6SChris Horne 		 * scsi_device in the scsi_address structure.  This allows an
413153a7b6b6SChris Horne 		 * an HBA driver to find its scsi_device(9S) and
413253a7b6b6SChris Horne 		 * per-scsi_device(9S) HBA private data given a
413353a7b6b6SChris Horne 		 * scsi_address(9S) by using scsi_address_device(9F) and
413453a7b6b6SChris Horne 		 * scsi_device_hba_private_get(9F)).
413553a7b6b6SChris Horne 		 */
413653a7b6b6SChris Horne 		psd->sd_address.a.a_sd = psd;
413753a7b6b6SChris Horne 	} else if (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE) {
41381e1ddd6cScth 		/*
41391e1ddd6cScth 		 * Clone transport structure if requested, so
41401e1ddd6cScth 		 * Self enumerating HBAs always need to use cloning
41411e1ddd6cScth 		 */
41421e1ddd6cScth 		scsi_hba_tran_t	*clone =
41431e1ddd6cScth 		    kmem_alloc(sizeof (scsi_hba_tran_t), KM_SLEEP);
41441e1ddd6cScth 		bcopy(hba, clone, sizeof (scsi_hba_tran_t));
41451e1ddd6cScth 		hba = clone;
41461e1ddd6cScth 		hba->tran_sd = psd;
41471e1ddd6cScth 	} else {
414853a7b6b6SChris Horne 		/*
414953a7b6b6SChris Horne 		 * SPI pHCI unit-address. If we ever need to support this
415053a7b6b6SChris Horne 		 * we could set a.spi.a_target/a.spi.a_lun based on pathinfo
415153a7b6b6SChris Horne 		 * node unit-address properties.  For now we fail...
415253a7b6b6SChris Horne 		 */
415353a7b6b6SChris Horne 		goto failure;
41541e1ddd6cScth 	}
415553a7b6b6SChris Horne 
41561e1ddd6cScth 	psd->sd_dev = tgt_dip;
41571e1ddd6cScth 	psd->sd_address.a_hba_tran = hba;
415853a7b6b6SChris Horne 
415953a7b6b6SChris Horne 	/*
416053a7b6b6SChris Horne 	 * Mark scsi_device as being associated with a pathinfo node. For
416153a7b6b6SChris Horne 	 * a scsi_device structure associated with a devinfo node,
416253a7b6b6SChris Horne 	 * scsi_ctlops_initchild sets this field to NULL.
416353a7b6b6SChris Horne 	 */
416453a7b6b6SChris Horne 	psd->sd_pathinfo = pip;
416553a7b6b6SChris Horne 
416653a7b6b6SChris Horne 	/*
416753a7b6b6SChris Horne 	 * LEGACY: sd_private: set for older mpxio-capable pHCI drivers with
416853a7b6b6SChris Horne 	 * too much scsi_vhci/mdi/ndi knowledge. Remove this code when all
416953a7b6b6SChris Horne 	 * mpxio-capable pHCI drivers use SCSA enumeration services (or at
417053a7b6b6SChris Horne 	 * least have been changed to use sd_pathinfo instead).
417153a7b6b6SChris Horne 	 */
41721e1ddd6cScth 	psd->sd_private = (caddr_t)pip;
417353a7b6b6SChris Horne 
417453a7b6b6SChris Horne 	/* See scsi_hba.c for info on sd_tran_safe kludge */
417553a7b6b6SChris Horne 	psd->sd_tran_safe = hba;
417653a7b6b6SChris Horne 
41771e1ddd6cScth 	svp->svp_psd = psd;
41781e1ddd6cScth 	mdi_pi_set_vhci_private(pip, (caddr_t)svp);
41791e1ddd6cScth 
41801e1ddd6cScth 	/*
41811e1ddd6cScth 	 * call hba's target init entry point if it exists
41821e1ddd6cScth 	 */
41831e1ddd6cScth 	if (hba->tran_tgt_init != NULL) {
4184c40ba10dSReed 		psd->sd_tran_tgt_free_done = 0;
41851e1ddd6cScth 		if ((rval = (*hba->tran_tgt_init)(pdip, tgt_dip,
41861e1ddd6cScth 		    hba, psd)) != DDI_SUCCESS) {
41871e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, pdip,
41881e1ddd6cScth 			    "!vhci_pathinfo_init: tran_tgt_init failed for "
41891e1ddd6cScth 			    "path=0x%p rval=%x", (void *)pip, rval));
41901e1ddd6cScth 			goto failure;
41911e1ddd6cScth 		}
41921e1ddd6cScth 	}
41931e1ddd6cScth 
41941e1ddd6cScth 	svp->svp_new_path = 1;
41951e1ddd6cScth 
41961e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_pathinfo_init: path:%p\n",
41971e1ddd6cScth 	    (void *)pip));
41981e1ddd6cScth 	return (MDI_SUCCESS);
41991e1ddd6cScth 
42001e1ddd6cScth failure:
42011e1ddd6cScth 	if (psd) {
42021e1ddd6cScth 		mutex_destroy(&psd->sd_mutex);
42031e1ddd6cScth 		kmem_free(psd, sizeof (*psd));
42041e1ddd6cScth 	}
42051e1ddd6cScth 	if (svp) {
42061e1ddd6cScth 		mdi_pi_set_vhci_private(pip, NULL);
42071e1ddd6cScth 		mutex_destroy(&svp->svp_mutex);
42081e1ddd6cScth 		cv_destroy(&svp->svp_cv);
42091e1ddd6cScth 		kmem_free(svp, sizeof (*svp));
42101e1ddd6cScth 	}
421153a7b6b6SChris Horne 	if (hba && (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE))
42121e1ddd6cScth 		kmem_free(hba, sizeof (scsi_hba_tran_t));
42131e1ddd6cScth 
42141e1ddd6cScth 	if (vlun_alloced)
421529c56acbSRaghuram Prahlada 		vhci_lun_free(vlun, NULL);
42161e1ddd6cScth 
42171e1ddd6cScth 	return (rval);
42181e1ddd6cScth }
42191e1ddd6cScth 
42201e1ddd6cScth /* ARGSUSED */
42211e1ddd6cScth static int
vhci_pathinfo_uninit(dev_info_t * vdip,mdi_pathinfo_t * pip,int flags)42221e1ddd6cScth vhci_pathinfo_uninit(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
42231e1ddd6cScth {
42241e1ddd6cScth 	scsi_hba_tran_t		*hba = NULL;
42251e1ddd6cScth 	struct scsi_device	*psd = NULL;
42261e1ddd6cScth 	dev_info_t		*pdip = NULL;
42271e1ddd6cScth 	dev_info_t		*cdip = NULL;
42281e1ddd6cScth 	scsi_vhci_priv_t	*svp = NULL;
42291e1ddd6cScth 
42301e1ddd6cScth 	ASSERT(vdip != NULL);
42311e1ddd6cScth 	ASSERT(pip != NULL);
42321e1ddd6cScth 
42331e1ddd6cScth 	pdip = mdi_pi_get_phci(pip);
42341e1ddd6cScth 	ASSERT(pdip != NULL);
42351e1ddd6cScth 
42361e1ddd6cScth 	cdip = mdi_pi_get_client(pip);
42371e1ddd6cScth 	ASSERT(cdip != NULL);
42381e1ddd6cScth 
42391e1ddd6cScth 	hba = ddi_get_driver_private(pdip);
42401e1ddd6cScth 	ASSERT(hba != NULL);
42411e1ddd6cScth 
42424c06356bSdh142964 	vhci_mpapi_set_path_state(vdip, pip, MP_DRVR_PATH_STATE_UNINIT);
42431e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
42441e1ddd6cScth 	if (svp == NULL) {
42451e1ddd6cScth 		/* path already freed. Nothing to do. */
42461e1ddd6cScth 		return (MDI_SUCCESS);
42471e1ddd6cScth 	}
42481e1ddd6cScth 
42491e1ddd6cScth 	psd = svp->svp_psd;
42501e1ddd6cScth 	ASSERT(psd != NULL);
42511e1ddd6cScth 
425253a7b6b6SChris Horne 	if (hba->tran_hba_flags & SCSI_HBA_ADDR_COMPLEX) {
425353a7b6b6SChris Horne 		/* Verify plumbing */
425453a7b6b6SChris Horne 		ASSERT(psd->sd_address.a_hba_tran == hba);
425553a7b6b6SChris Horne 		ASSERT(psd->sd_address.a.a_sd == psd);
425653a7b6b6SChris Horne 	} else if (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE) {
425753a7b6b6SChris Horne 		/* Switch to cloned scsi_hba_tran(9S) structure */
42581e1ddd6cScth 		hba = psd->sd_address.a_hba_tran;
42591e1ddd6cScth 		ASSERT(hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE);
42601e1ddd6cScth 		ASSERT(hba->tran_sd == psd);
42611e1ddd6cScth 	}
42621e1ddd6cScth 
4263c40ba10dSReed 	if ((hba->tran_tgt_free != NULL) && !psd->sd_tran_tgt_free_done) {
42641e1ddd6cScth 		(*hba->tran_tgt_free) (pdip, cdip, hba, psd);
4265c40ba10dSReed 		psd->sd_tran_tgt_free_done = 1;
42661e1ddd6cScth 	}
42671e1ddd6cScth 	mutex_destroy(&psd->sd_mutex);
42681e1ddd6cScth 	if (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE) {
42691e1ddd6cScth 		kmem_free(hba, sizeof (*hba));
42701e1ddd6cScth 	}
42711e1ddd6cScth 
42721e1ddd6cScth 	mdi_pi_set_vhci_private(pip, NULL);
427355e592a2SRandall Ralphs 
427455e592a2SRandall Ralphs 	/*
427555e592a2SRandall Ralphs 	 * Free the pathinfo related scsi_device inquiry data. Note that this
427655e592a2SRandall Ralphs 	 * matches what happens for scsi_hba.c devinfo case at uninitchild time.
427755e592a2SRandall Ralphs 	 */
427855e592a2SRandall Ralphs 	if (psd->sd_inq)
427955e592a2SRandall Ralphs 		kmem_free((caddr_t)psd->sd_inq, sizeof (struct scsi_inquiry));
42801e1ddd6cScth 	kmem_free((caddr_t)psd, sizeof (*psd));
42811e1ddd6cScth 
42821e1ddd6cScth 	mutex_destroy(&svp->svp_mutex);
42831e1ddd6cScth 	cv_destroy(&svp->svp_cv);
42841e1ddd6cScth 	kmem_free((caddr_t)svp, sizeof (*svp));
42851e1ddd6cScth 
42861e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_pathinfo_uninit: path=0x%p\n",
42871e1ddd6cScth 	    (void *)pip));
42881e1ddd6cScth 	return (MDI_SUCCESS);
42891e1ddd6cScth }
42901e1ddd6cScth 
42911e1ddd6cScth /* ARGSUSED */
42921e1ddd6cScth static int
vhci_pathinfo_state_change(dev_info_t * vdip,mdi_pathinfo_t * pip,mdi_pathinfo_state_t state,uint32_t ext_state,int flags)42931e1ddd6cScth vhci_pathinfo_state_change(dev_info_t *vdip, mdi_pathinfo_t *pip,
42941e1ddd6cScth     mdi_pathinfo_state_t state, uint32_t ext_state, int flags)
42951e1ddd6cScth {
42961e1ddd6cScth 	int			rval = MDI_SUCCESS;
42971e1ddd6cScth 	scsi_vhci_priv_t	*svp;
42981e1ddd6cScth 	scsi_vhci_lun_t		*vlun;
42991e1ddd6cScth 	int			held;
43001e1ddd6cScth 	int			op = (flags & 0xf00) >> 8;
43011e1ddd6cScth 	struct scsi_vhci	*vhci;
43021e1ddd6cScth 
43031e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
43041e1ddd6cScth 
43051e1ddd6cScth 	if (flags & MDI_EXT_STATE_CHANGE) {
43061e1ddd6cScth 		/*
43071e1ddd6cScth 		 * We do not want to issue any commands down the path in case
43081e1ddd6cScth 		 * sync flag is set. Lower layers might not be ready to accept
43091e1ddd6cScth 		 * any I/O commands.
43101e1ddd6cScth 		 */
43111e1ddd6cScth 		if (op == DRIVER_DISABLE)
43121e1ddd6cScth 			return (MDI_SUCCESS);
43131e1ddd6cScth 
43141e1ddd6cScth 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
43151e1ddd6cScth 		if (svp == NULL) {
43161e1ddd6cScth 			return (MDI_FAILURE);
43171e1ddd6cScth 		}
43181e1ddd6cScth 		vlun = svp->svp_svl;
43191e1ddd6cScth 
43201e1ddd6cScth 		if (flags & MDI_BEFORE_STATE_CHANGE) {
43211e1ddd6cScth 			/*
43221e1ddd6cScth 			 * Hold the LUN.
43231e1ddd6cScth 			 */
43241e1ddd6cScth 			VHCI_HOLD_LUN(vlun, VH_SLEEP, held);
43251e1ddd6cScth 			if (flags & MDI_DISABLE_OP)  {
43261e1ddd6cScth 				/*
43271e1ddd6cScth 				 * Issue scsi reset if it happens to be
43281e1ddd6cScth 				 * reserved path.
43291e1ddd6cScth 				 */
43301e1ddd6cScth 				if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
43311e1ddd6cScth 					/*
43321e1ddd6cScth 					 * if reservation pending on
43331e1ddd6cScth 					 * this path, dont' mark the
43341e1ddd6cScth 					 * path busy
43351e1ddd6cScth 					 */
43361e1ddd6cScth 					if (op == DRIVER_DISABLE_TRANSIENT) {
43371e1ddd6cScth 						VHCI_DEBUG(1, (CE_NOTE, NULL,
43381e1ddd6cScth 						    "!vhci_pathinfo"
43391e1ddd6cScth 						    "_state_change (pip:%p): "
43401e1ddd6cScth 						    " reservation: fail busy\n",
43411e1ddd6cScth 						    (void *)pip));
43421e1ddd6cScth 						return (MDI_FAILURE);
43431e1ddd6cScth 					}
43441e1ddd6cScth 					if (pip == vlun->svl_resrv_pip) {
43451e1ddd6cScth 						if (vhci_recovery_reset(
43461e1ddd6cScth 						    svp->svp_svl,
43471e1ddd6cScth 						    &svp->svp_psd->sd_address,
43481e1ddd6cScth 						    TRUE,
43491e1ddd6cScth 						    VHCI_DEPTH_TARGET) == 0) {
43501e1ddd6cScth 							VHCI_DEBUG(1,
43511e1ddd6cScth 							    (CE_NOTE, NULL,
43521e1ddd6cScth 							    "!vhci_pathinfo"
43531e1ddd6cScth 							    "_state_change "
43541e1ddd6cScth 							    " (pip:%p): "
43551e1ddd6cScth 							    "reset failed, "
43561e1ddd6cScth 							    "give up!\n",
43571e1ddd6cScth 							    (void *)pip));
43581e1ddd6cScth 						}
43591e1ddd6cScth 						vlun->svl_flags &=
43601e1ddd6cScth 						    ~VLUN_RESERVE_ACTIVE_FLG;
43611e1ddd6cScth 					}
43621e1ddd6cScth 				}
43631e1ddd6cScth 			} else if (flags & MDI_ENABLE_OP)  {
43641e1ddd6cScth 				if (((vhci->vhci_conf_flags &
43651e1ddd6cScth 				    VHCI_CONF_FLAGS_AUTO_FAILBACK) ==
43661e1ddd6cScth 				    VHCI_CONF_FLAGS_AUTO_FAILBACK) &&
43671e1ddd6cScth 				    MDI_PI_IS_USER_DISABLE(pip) &&
43681e1ddd6cScth 				    MDI_PI_IS_STANDBY(pip)) {
43691e1ddd6cScth 					struct scsi_failover_ops	*fo;
43701e1ddd6cScth 					char *best_pclass, *pclass = NULL;
43711e1ddd6cScth 					int  best_class, rv;
43721e1ddd6cScth 					/*
43731e1ddd6cScth 					 * Failback if enabling a standby path
43741e1ddd6cScth 					 * and it is the primary class or
43751e1ddd6cScth 					 * preferred class
43761e1ddd6cScth 					 */
43771e1ddd6cScth 					best_class = mdi_pi_get_preferred(pip);
43781e1ddd6cScth 					if (best_class == 0) {
43791e1ddd6cScth 						/*
43801e1ddd6cScth 						 * if not preferred - compare
43811e1ddd6cScth 						 * path-class with class
43821e1ddd6cScth 						 */
43831e1ddd6cScth 						fo = vlun->svl_fops;
43840205780bSrralphs 						(void) fo->sfo_pathclass_next(
43850205780bSrralphs 						    NULL, &best_pclass,
43861e1ddd6cScth 						    vlun->svl_fops_ctpriv);
43871e1ddd6cScth 						pclass = NULL;
43881e1ddd6cScth 						rv = mdi_prop_lookup_string(pip,
43891e1ddd6cScth 						    "path-class", &pclass);
43901e1ddd6cScth 						if (rv != MDI_SUCCESS ||
43911e1ddd6cScth 						    pclass == NULL) {
43921e1ddd6cScth 							vhci_log(CE_NOTE, vdip,
43931e1ddd6cScth 							    "!path-class "
43941e1ddd6cScth 							    " lookup "
43951e1ddd6cScth 							    "failed. rv: %d"
43961e1ddd6cScth 							    "class: %p", rv,
43971e1ddd6cScth 							    (void *)pclass);
43981e1ddd6cScth 						} else if (strncmp(pclass,
43991e1ddd6cScth 						    best_pclass,
44001e1ddd6cScth 						    strlen(best_pclass)) == 0) {
44011e1ddd6cScth 							best_class = 1;
44021e1ddd6cScth 						}
44031e1ddd6cScth 						if (rv == MDI_SUCCESS &&
44041e1ddd6cScth 						    pclass != NULL) {
44051e1ddd6cScth 							rv = mdi_prop_free(
44061e1ddd6cScth 							    pclass);
44071e1ddd6cScth 							if (rv !=
44081e1ddd6cScth 							    DDI_PROP_SUCCESS) {
44091e1ddd6cScth 								vhci_log(
44101e1ddd6cScth 								    CE_NOTE,
44111e1ddd6cScth 								    vdip,
44121e1ddd6cScth 								    "!path-"
44131e1ddd6cScth 								    "class"
44141e1ddd6cScth 								    " free"
44151e1ddd6cScth 								    " failed"
44161e1ddd6cScth 								    " rv: %d"
44171e1ddd6cScth 								    " class: "
44181e1ddd6cScth 								    "%p",
44191e1ddd6cScth 								    rv,
44201e1ddd6cScth 								    (void *)
44211e1ddd6cScth 								    pclass);
44221e1ddd6cScth 							}
44231e1ddd6cScth 						}
44241e1ddd6cScth 					}
44251e1ddd6cScth 					if (best_class == 1) {
44261e1ddd6cScth 						VHCI_DEBUG(1, (CE_NOTE, NULL,
44271e1ddd6cScth 						    "preferred path: %p "
44281e1ddd6cScth 						    "USER_DISABLE->USER_ENABLE "
44291e1ddd6cScth 						    "transition for lun %s\n",
44301e1ddd6cScth 						    (void *)pip,
44311e1ddd6cScth 						    vlun->svl_lun_wwn));
44321e1ddd6cScth 						(void) taskq_dispatch(
44331e1ddd6cScth 						    vhci->vhci_taskq,
44341e1ddd6cScth 						    vhci_initiate_auto_failback,
44351e1ddd6cScth 						    (void *) vlun, KM_SLEEP);
44361e1ddd6cScth 					}
44371e1ddd6cScth 				}
44381e1ddd6cScth 				/*
44391e1ddd6cScth 				 * if PGR is active, revalidate key and
44401e1ddd6cScth 				 * register on this path also, if key is
44411e1ddd6cScth 				 * still valid
44421e1ddd6cScth 				 */
44431e1ddd6cScth 				sema_p(&vlun->svl_pgr_sema);
44441e1ddd6cScth 				if (vlun->svl_pgr_active)
44451e1ddd6cScth 					(void)
44461e1ddd6cScth 					    vhci_pgr_validate_and_register(svp);
44471e1ddd6cScth 				sema_v(&vlun->svl_pgr_sema);
44481e1ddd6cScth 				/*
44491e1ddd6cScth 				 * Inform target driver about any
44501e1ddd6cScth 				 * reservations to be reinstated if target
44511e1ddd6cScth 				 * has dropped reservation during the busy
44521e1ddd6cScth 				 * period.
44531e1ddd6cScth 				 */
44541e1ddd6cScth 				mutex_enter(&vhci->vhci_mutex);
44551e1ddd6cScth 				scsi_hba_reset_notify_callback(
44561e1ddd6cScth 				    &vhci->vhci_mutex,
44571e1ddd6cScth 				    &vhci->vhci_reset_notify_listf);
44581e1ddd6cScth 				mutex_exit(&vhci->vhci_mutex);
44591e1ddd6cScth 			}
44601e1ddd6cScth 		}
44611e1ddd6cScth 		if (flags & MDI_AFTER_STATE_CHANGE) {
44621e1ddd6cScth 			if (flags & MDI_ENABLE_OP)  {
44631e1ddd6cScth 				mutex_enter(&vhci_global_mutex);
44641e1ddd6cScth 				cv_broadcast(&vhci_cv);
44651e1ddd6cScth 				mutex_exit(&vhci_global_mutex);
44661e1ddd6cScth 			}
44671e1ddd6cScth 			if (vlun->svl_setcap_done) {
44681e1ddd6cScth 				(void) vhci_pHCI_cap(&svp->svp_psd->sd_address,
44691e1ddd6cScth 				    "sector-size", vlun->svl_sector_size,
44701e1ddd6cScth 				    1, pip);
44711e1ddd6cScth 			}
44721e1ddd6cScth 
44731e1ddd6cScth 			/*
44741e1ddd6cScth 			 * Release the LUN
44751e1ddd6cScth 			 */
44761e1ddd6cScth 			VHCI_RELEASE_LUN(vlun);
44771e1ddd6cScth 
44781e1ddd6cScth 			/*
44791e1ddd6cScth 			 * Path transition is complete.
44801e1ddd6cScth 			 * Run callback to indicate target driver to
44811e1ddd6cScth 			 * retry to prevent IO starvation.
44821e1ddd6cScth 			 */
44831e1ddd6cScth 			if (scsi_callback_id != 0) {
44841e1ddd6cScth 				ddi_run_callback(&scsi_callback_id);
44851e1ddd6cScth 			}
44861e1ddd6cScth 		}
44871e1ddd6cScth 	} else {
44881e1ddd6cScth 		switch (state) {
44891e1ddd6cScth 		case MDI_PATHINFO_STATE_ONLINE:
44901e1ddd6cScth 			rval = vhci_pathinfo_online(vdip, pip, flags);
44911e1ddd6cScth 			break;
44921e1ddd6cScth 
44931e1ddd6cScth 		case MDI_PATHINFO_STATE_OFFLINE:
44941e1ddd6cScth 			rval = vhci_pathinfo_offline(vdip, pip, flags);
44951e1ddd6cScth 			break;
44961e1ddd6cScth 
44971e1ddd6cScth 		default:
44981e1ddd6cScth 			break;
44991e1ddd6cScth 		}
45001e1ddd6cScth 		/*
45011e1ddd6cScth 		 * Path transition is complete.
45021e1ddd6cScth 		 * Run callback to indicate target driver to
45031e1ddd6cScth 		 * retry to prevent IO starvation.
45041e1ddd6cScth 		 */
45051e1ddd6cScth 		if ((rval == MDI_SUCCESS) && (scsi_callback_id != 0)) {
45061e1ddd6cScth 			ddi_run_callback(&scsi_callback_id);
45071e1ddd6cScth 		}
45081e1ddd6cScth 		return (rval);
45091e1ddd6cScth 	}
45101e1ddd6cScth 
45111e1ddd6cScth 	return (MDI_SUCCESS);
45121e1ddd6cScth }
45131e1ddd6cScth 
45141e1ddd6cScth /*
45151e1ddd6cScth  * Parse the mpxio load balancing options. The datanameptr
45161e1ddd6cScth  * will point to a string containing the load-balance-options value.
45171e1ddd6cScth  * The load-balance-options value will be a property that
45181e1ddd6cScth  * defines the load-balance algorithm and any arguments to that
45191e1ddd6cScth  * algorithm.
45201e1ddd6cScth  * For example:
45211e1ddd6cScth  * device-type-mpxio-options-list=
45221e1ddd6cScth  * "device-type=SUN    SENA", "load-balance-options=logical-block-options"
45231e1ddd6cScth  * "device-type=SUN     SE6920", "round-robin-options";
45241e1ddd6cScth  * logical-block-options="load-balance=logical-block", "region-size=15";
45251e1ddd6cScth  * round-robin-options="load-balance=round-robin";
45261e1ddd6cScth  *
45271e1ddd6cScth  * If the load-balance is not defined the load balance algorithm will
45281e1ddd6cScth  * default to the global setting. There will be default values assigned
45291e1ddd6cScth  * to the arguments (region-size=18) and if an argument is one
45301e1ddd6cScth  * that is not known, it will be ignored.
45311e1ddd6cScth  */
45321e1ddd6cScth static void
vhci_parse_mpxio_lb_options(dev_info_t * dip,dev_info_t * cdip,caddr_t datanameptr)45331e1ddd6cScth vhci_parse_mpxio_lb_options(dev_info_t *dip, dev_info_t *cdip,
45341e1ddd6cScth     caddr_t datanameptr)
45351e1ddd6cScth {
45361e1ddd6cScth 	char			*dataptr, *next_entry;
45371e1ddd6cScth 	caddr_t			config_list	= NULL;
45381e1ddd6cScth 	int			config_list_len = 0, list_len = 0;
45391e1ddd6cScth 	int			region_size = -1;
45401e1ddd6cScth 	client_lb_t		load_balance;
45411e1ddd6cScth 
45421e1ddd6cScth 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, datanameptr,
45431e1ddd6cScth 	    (caddr_t)&config_list, &config_list_len) != DDI_PROP_SUCCESS) {
45441e1ddd6cScth 		return;
45451e1ddd6cScth 	}
45461e1ddd6cScth 
45471e1ddd6cScth 	list_len = config_list_len;
45481e1ddd6cScth 	next_entry = config_list;
45491e1ddd6cScth 	while (config_list_len > 0) {
45501e1ddd6cScth 		dataptr = next_entry;
45511e1ddd6cScth 
45521e1ddd6cScth 		if (strncmp(mdi_load_balance, dataptr,
45531e1ddd6cScth 		    strlen(mdi_load_balance)) == 0) {
45541e1ddd6cScth 			/* get the load-balance scheme */
45551e1ddd6cScth 			dataptr += strlen(mdi_load_balance) + 1;
45561e1ddd6cScth 			if (strcmp(dataptr, LOAD_BALANCE_PROP_RR) == 0) {
45571e1ddd6cScth 				(void) mdi_set_lb_policy(cdip, LOAD_BALANCE_RR);
45581e1ddd6cScth 				load_balance = LOAD_BALANCE_RR;
45591e1ddd6cScth 			} else if (strcmp(dataptr,
45601e1ddd6cScth 			    LOAD_BALANCE_PROP_LBA) == 0) {
45611e1ddd6cScth 				(void) mdi_set_lb_policy(cdip,
45621e1ddd6cScth 				    LOAD_BALANCE_LBA);
45631e1ddd6cScth 				load_balance = LOAD_BALANCE_LBA;
45641e1ddd6cScth 			} else if (strcmp(dataptr,
45651e1ddd6cScth 			    LOAD_BALANCE_PROP_NONE) == 0) {
45661e1ddd6cScth 				(void) mdi_set_lb_policy(cdip,
45671e1ddd6cScth 				    LOAD_BALANCE_NONE);
45681e1ddd6cScth 				load_balance = LOAD_BALANCE_NONE;
45691e1ddd6cScth 			}
45701e1ddd6cScth 		} else if (strncmp(dataptr, LOGICAL_BLOCK_REGION_SIZE,
45711e1ddd6cScth 		    strlen(LOGICAL_BLOCK_REGION_SIZE)) == 0) {
45721e1ddd6cScth 			int	i = 0;
45731e1ddd6cScth 			char	*ptr;
45741e1ddd6cScth 			char	*tmp;
45751e1ddd6cScth 
45761e1ddd6cScth 			tmp = dataptr + (strlen(LOGICAL_BLOCK_REGION_SIZE) + 1);
45771e1ddd6cScth 			/* check for numeric value */
45781e1ddd6cScth 			for (ptr = tmp; i < strlen(tmp); i++, ptr++) {
45791e1ddd6cScth 				if (!isdigit(*ptr)) {
45801e1ddd6cScth 					cmn_err(CE_WARN,
45811e1ddd6cScth 					    "Illegal region size: %s."
45821e1ddd6cScth 					    " Setting to default value: %d",
45831e1ddd6cScth 					    tmp,
45841e1ddd6cScth 					    LOAD_BALANCE_DEFAULT_REGION_SIZE);
45851e1ddd6cScth 					region_size =
45861e1ddd6cScth 					    LOAD_BALANCE_DEFAULT_REGION_SIZE;
45871e1ddd6cScth 					break;
45881e1ddd6cScth 				}
45891e1ddd6cScth 			}
45901e1ddd6cScth 			if (i >= strlen(tmp)) {
45911e1ddd6cScth 				region_size = stoi(&tmp);
45921e1ddd6cScth 			}
45931e1ddd6cScth 			(void) mdi_set_lb_region_size(cdip, region_size);
45941e1ddd6cScth 		}
45951e1ddd6cScth 		config_list_len -= (strlen(next_entry) + 1);
45961e1ddd6cScth 		next_entry += strlen(next_entry) + 1;
45971e1ddd6cScth 	}
45981e1ddd6cScth #ifdef DEBUG
45991e1ddd6cScth 	if ((region_size >= 0) && (load_balance != LOAD_BALANCE_LBA)) {
46001e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, dip,
46011e1ddd6cScth 		    "!vhci_parse_mpxio_lb_options: region-size: %d"
46021e1ddd6cScth 		    "only valid for load-balance=logical-block\n",
46031e1ddd6cScth 		    region_size));
46041e1ddd6cScth 	}
46051e1ddd6cScth #endif
46061e1ddd6cScth 	if ((region_size == -1) && (load_balance == LOAD_BALANCE_LBA)) {
46071e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, dip,
46081e1ddd6cScth 		    "!vhci_parse_mpxio_lb_options: No region-size"
46091e1ddd6cScth 		    " defined load-balance=logical-block."
46101e1ddd6cScth 		    " Default to: %d\n", LOAD_BALANCE_DEFAULT_REGION_SIZE));
46111e1ddd6cScth 		(void) mdi_set_lb_region_size(cdip,
46121e1ddd6cScth 		    LOAD_BALANCE_DEFAULT_REGION_SIZE);
46131e1ddd6cScth 	}
46141e1ddd6cScth 	if (list_len > 0) {
46151e1ddd6cScth 		kmem_free(config_list, list_len);
46161e1ddd6cScth 	}
46171e1ddd6cScth }
46181e1ddd6cScth 
46191e1ddd6cScth /*
46201e1ddd6cScth  * Parse the device-type-mpxio-options-list looking for the key of
46211e1ddd6cScth  * "load-balance-options". If found, parse the load balancing options.
46221e1ddd6cScth  * Check the comment of the vhci_get_device_type_mpxio_options()
46231e1ddd6cScth  * for the device-type-mpxio-options-list.
46241e1ddd6cScth  */
46251e1ddd6cScth static void
vhci_parse_mpxio_options(dev_info_t * dip,dev_info_t * cdip,caddr_t datanameptr,int list_len)46261e1ddd6cScth vhci_parse_mpxio_options(dev_info_t *dip, dev_info_t *cdip,
46271e1ddd6cScth     caddr_t datanameptr, int list_len)
46281e1ddd6cScth {
46291e1ddd6cScth 	char		*dataptr;
46301e1ddd6cScth 	int		len;
46311e1ddd6cScth 
46321e1ddd6cScth 	/*
46331e1ddd6cScth 	 * get the data list
46341e1ddd6cScth 	 */
46351e1ddd6cScth 	dataptr = datanameptr;
46361e1ddd6cScth 	len = 0;
46371e1ddd6cScth 	while (len < list_len &&
46381e1ddd6cScth 	    strncmp(dataptr, DEVICE_TYPE_STR, strlen(DEVICE_TYPE_STR))
46391e1ddd6cScth 	    != 0) {
46401e1ddd6cScth 		if (strncmp(dataptr, LOAD_BALANCE_OPTIONS,
46411e1ddd6cScth 		    strlen(LOAD_BALANCE_OPTIONS)) == 0) {
46421e1ddd6cScth 			len += strlen(LOAD_BALANCE_OPTIONS) + 1;
46431e1ddd6cScth 			dataptr += strlen(LOAD_BALANCE_OPTIONS) + 1;
46441e1ddd6cScth 			vhci_parse_mpxio_lb_options(dip, cdip, dataptr);
46451e1ddd6cScth 		}
46461e1ddd6cScth 		len += strlen(dataptr) + 1;
46471e1ddd6cScth 		dataptr += strlen(dataptr) + 1;
46481e1ddd6cScth 	}
46491e1ddd6cScth }
46501e1ddd6cScth 
46511e1ddd6cScth /*
465255e592a2SRandall Ralphs  * Check the inquriy string returned from the device with the device-type
46531e1ddd6cScth  * Check for the existence of the device-type-mpxio-options-list and
46541e1ddd6cScth  * if found parse the list checking for a match with the device-type
46551e1ddd6cScth  * value and the inquiry string returned from the device. If a match
46561e1ddd6cScth  * is found, parse the mpxio options list. The format of the
46571e1ddd6cScth  * device-type-mpxio-options-list is:
46581e1ddd6cScth  * device-type-mpxio-options-list=
46591e1ddd6cScth  * "device-type=SUN    SENA", "load-balance-options=logical-block-options"
46601e1ddd6cScth  * "device-type=SUN     SE6920", "round-robin-options";
46611e1ddd6cScth  * logical-block-options="load-balance=logical-block", "region-size=15";
46621e1ddd6cScth  * round-robin-options="load-balance=round-robin";
46631e1ddd6cScth  */
46641e1ddd6cScth void
vhci_get_device_type_mpxio_options(dev_info_t * dip,dev_info_t * cdip,struct scsi_device * devp)46651e1ddd6cScth vhci_get_device_type_mpxio_options(dev_info_t *dip, dev_info_t *cdip,
46661e1ddd6cScth     struct scsi_device *devp)
46671e1ddd6cScth {
46681e1ddd6cScth 
46691e1ddd6cScth 	caddr_t			config_list	= NULL;
46701e1ddd6cScth 	caddr_t			vidptr, datanameptr;
46711e1ddd6cScth 	int			vidlen, dupletlen = 0;
46721e1ddd6cScth 	int			config_list_len = 0, len;
46731e1ddd6cScth 	struct scsi_inquiry	*inq = devp->sd_inq;
46741e1ddd6cScth 
46751e1ddd6cScth 	/*
46761e1ddd6cScth 	 * look up the device-type-mpxio-options-list and walk thru
46771e1ddd6cScth 	 * the list compare the vendor ids of the earlier inquiry command and
46781e1ddd6cScth 	 * with those vids in the list if there is a match, lookup
46791e1ddd6cScth 	 * the mpxio-options value
46801e1ddd6cScth 	 */
46811e1ddd6cScth 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
46821e1ddd6cScth 	    MPXIO_OPTIONS_LIST,
46831e1ddd6cScth 	    (caddr_t)&config_list, &config_list_len) == DDI_PROP_SUCCESS) {
46841e1ddd6cScth 
46851e1ddd6cScth 		/*
46861e1ddd6cScth 		 * Compare vids in each duplet - if it matches,
46871e1ddd6cScth 		 * parse the mpxio options list.
46881e1ddd6cScth 		 */
46891e1ddd6cScth 		for (len = config_list_len, vidptr = config_list; len > 0;
46901e1ddd6cScth 		    len -= dupletlen) {
46911e1ddd6cScth 
46921e1ddd6cScth 			dupletlen = 0;
46931e1ddd6cScth 
46941e1ddd6cScth 			if (strlen(vidptr) != 0 &&
46951e1ddd6cScth 			    strncmp(vidptr, DEVICE_TYPE_STR,
46961e1ddd6cScth 			    strlen(DEVICE_TYPE_STR)) == 0) {
46971e1ddd6cScth 				/* point to next duplet */
46981e1ddd6cScth 				datanameptr = vidptr + strlen(vidptr) + 1;
46991e1ddd6cScth 				/* add len of this duplet */
47001e1ddd6cScth 				dupletlen += strlen(vidptr) + 1;
47011e1ddd6cScth 				/* get to device type */
47021e1ddd6cScth 				vidptr += strlen(DEVICE_TYPE_STR) + 1;
47031e1ddd6cScth 				vidlen = strlen(vidptr);
47041e1ddd6cScth 				if ((vidlen != 0) &&
47051e1ddd6cScth 				    bcmp(inq->inq_vid, vidptr, vidlen) == 0) {
47061e1ddd6cScth 					vhci_parse_mpxio_options(dip, cdip,
47071e1ddd6cScth 					    datanameptr, len - dupletlen);
47081e1ddd6cScth 					break;
47091e1ddd6cScth 				}
47101e1ddd6cScth 				/* get to next duplet */
47111e1ddd6cScth 				vidptr += strlen(vidptr) + 1;
47121e1ddd6cScth 			}
47131e1ddd6cScth 			/* get to the next device-type */
47141e1ddd6cScth 			while (len - dupletlen > 0 &&
47151e1ddd6cScth 			    strlen(vidptr) != 0 &&
47161e1ddd6cScth 			    strncmp(vidptr, DEVICE_TYPE_STR,
47171e1ddd6cScth 			    strlen(DEVICE_TYPE_STR)) != 0) {
47181e1ddd6cScth 				dupletlen += strlen(vidptr) + 1;
47191e1ddd6cScth 				vidptr += strlen(vidptr) + 1;
47201e1ddd6cScth 			}
47211e1ddd6cScth 		}
47221e1ddd6cScth 		if (config_list_len > 0) {
47231e1ddd6cScth 			kmem_free(config_list, config_list_len);
47241e1ddd6cScth 		}
47251e1ddd6cScth 	}
47261e1ddd6cScth }
47271e1ddd6cScth 
47281e1ddd6cScth static int
vhci_update_pathinfo(struct scsi_device * psd,mdi_pathinfo_t * pip,struct scsi_failover_ops * fo,scsi_vhci_lun_t * vlun,struct scsi_vhci * vhci)47291e1ddd6cScth vhci_update_pathinfo(struct scsi_device *psd,  mdi_pathinfo_t *pip,
47304e40636aSToomas Soome     struct scsi_failover_ops *fo, scsi_vhci_lun_t *vlun,
47311e1ddd6cScth     struct scsi_vhci *vhci)
47321e1ddd6cScth {
47331e1ddd6cScth 	struct scsi_path_opinfo		opinfo;
47341e1ddd6cScth 	char				*pclass, *best_pclass;
47351c8dbe37SMilos Muzik 	char				*resrv_pclass = NULL;
47361c8dbe37SMilos Muzik 	int				force_rereserve = 0;
47371c8dbe37SMilos Muzik 	int				update_pathinfo_done = 0;
47381e1ddd6cScth 
47390205780bSrralphs 	if (fo->sfo_path_get_opinfo(psd, &opinfo, vlun->svl_fops_ctpriv) != 0) {
47401e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_update_pathinfo: "
47411e1ddd6cScth 		    "Failed to get operation info for path:%p\n", (void *)pip));
47421e1ddd6cScth 		return (MDI_FAILURE);
47431e1ddd6cScth 	}
47441e1ddd6cScth 	/* set the xlf capable flag in the vlun for future use */
47451e1ddd6cScth 	vlun->svl_xlf_capable = opinfo.opinfo_xlf_capable;
47461e1ddd6cScth 	(void) mdi_prop_update_string(pip, "path-class",
47471e1ddd6cScth 	    opinfo.opinfo_path_attr);
47481e1ddd6cScth 
47491e1ddd6cScth 	pclass = opinfo.opinfo_path_attr;
47501e1ddd6cScth 	if (opinfo.opinfo_path_state == SCSI_PATH_ACTIVE) {
47511e1ddd6cScth 		mutex_enter(&vlun->svl_mutex);
47521e1ddd6cScth 		if (vlun->svl_active_pclass != NULL) {
47531e1ddd6cScth 			if (strcmp(vlun->svl_active_pclass, pclass) != 0) {
47541e1ddd6cScth 				mutex_exit(&vlun->svl_mutex);
47551e1ddd6cScth 				/*
47561e1ddd6cScth 				 * Externally initiated failover has happened;
47571e1ddd6cScth 				 * force the path state to be STANDBY/ONLINE,
47581e1ddd6cScth 				 * next IO will trigger failover and thus
47591e1ddd6cScth 				 * sync-up the pathstates.  Reason we don't
47601e1ddd6cScth 				 * sync-up immediately by invoking
47611e1ddd6cScth 				 * vhci_update_pathstates() is because it
47621e1ddd6cScth 				 * needs a VHCI_HOLD_LUN() and we don't
47631e1ddd6cScth 				 * want to block here.
47641e1ddd6cScth 				 *
47651e1ddd6cScth 				 * Further, if the device is an ALUA device,
47661e1ddd6cScth 				 * then failure to exactly match 'pclass' and
47671e1ddd6cScth 				 * 'svl_active_pclass'(as is the case here)
47681e1ddd6cScth 				 * indicates that the currently active path
47691e1ddd6cScth 				 * is a 'non-optimized' path - which means
47701e1ddd6cScth 				 * that 'svl_active_pclass' needs to be
47711e1ddd6cScth 				 * replaced with opinfo.opinfo_path_state
47721e1ddd6cScth 				 * value.
47731e1ddd6cScth 				 */
47741e1ddd6cScth 
47751e1ddd6cScth 				if (SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
47761e1ddd6cScth 					char	*tptr;
47771e1ddd6cScth 
47781e1ddd6cScth 					/*
47791e1ddd6cScth 					 * The device is ALUA compliant. The
47801e1ddd6cScth 					 * state need to be changed to online
47811e1ddd6cScth 					 * rather than standby state which is
47821e1ddd6cScth 					 * done typically for a asymmetric
47831e1ddd6cScth 					 * device that is non ALUA compliant.
47841e1ddd6cScth 					 */
47851e1ddd6cScth 					mdi_pi_set_state(pip,
47861e1ddd6cScth 					    MDI_PATHINFO_STATE_ONLINE);
47871e1ddd6cScth 					tptr = kmem_alloc(strlen
47881e1ddd6cScth 					    (opinfo.opinfo_path_attr) + 1,
47891e1ddd6cScth 					    KM_SLEEP);
47901e1ddd6cScth 					(void) strlcpy(tptr,
47911e1ddd6cScth 					    opinfo.opinfo_path_attr,
47921e1ddd6cScth 					    (strlen(opinfo.opinfo_path_attr)
47931e1ddd6cScth 					    + 1));
47941e1ddd6cScth 					mutex_enter(&vlun->svl_mutex);
47951e1ddd6cScth 					kmem_free(vlun->svl_active_pclass,
47964e40636aSToomas Soome 					    strlen(vlun->svl_active_pclass) +
47974e40636aSToomas Soome 					    1);
47981e1ddd6cScth 					vlun->svl_active_pclass = tptr;
47991e1ddd6cScth 					mutex_exit(&vlun->svl_mutex);
48001e1ddd6cScth 				} else {
48011e1ddd6cScth 					/*
48021e1ddd6cScth 					 * Non ALUA device case.
48031e1ddd6cScth 					 */
48041e1ddd6cScth 					mdi_pi_set_state(pip,
48051e1ddd6cScth 					    MDI_PATHINFO_STATE_STANDBY);
48061e1ddd6cScth 				}
48071e1ddd6cScth 				vlun->svl_fo_support = opinfo.opinfo_mode;
48081e1ddd6cScth 				mdi_pi_set_preferred(pip,
48091e1ddd6cScth 				    opinfo.opinfo_preferred);
48101c8dbe37SMilos Muzik 				update_pathinfo_done = 1;
48111c8dbe37SMilos Muzik 			}
48121c8dbe37SMilos Muzik 
48131c8dbe37SMilos Muzik 			/*
48141c8dbe37SMilos Muzik 			 * Find out a class of currently reserved path if there
48151c8dbe37SMilos Muzik 			 * is any.
48161c8dbe37SMilos Muzik 			 */
48171c8dbe37SMilos Muzik 			if ((vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) &&
48181c8dbe37SMilos Muzik 			    mdi_prop_lookup_string(vlun->svl_resrv_pip,
48191c8dbe37SMilos Muzik 			    "path-class", &resrv_pclass) != MDI_SUCCESS) {
48201c8dbe37SMilos Muzik 				VHCI_DEBUG(1, (CE_NOTE, NULL,
48211c8dbe37SMilos Muzik 				    "!vhci_update_pathinfo: prop lookup "
48221c8dbe37SMilos Muzik 				    "failed for path 0x%p\n",
48231c8dbe37SMilos Muzik 				    (void *)vlun->svl_resrv_pip));
48241c8dbe37SMilos Muzik 				/*
48251c8dbe37SMilos Muzik 				 * Something is wrong with the reserved path.
48261c8dbe37SMilos Muzik 				 * We can't do much with that right here. Just
48271c8dbe37SMilos Muzik 				 * force re-reservation to another path.
48281c8dbe37SMilos Muzik 				 */
48291c8dbe37SMilos Muzik 				force_rereserve = 1;
48301c8dbe37SMilos Muzik 			}
48311c8dbe37SMilos Muzik 
48321c8dbe37SMilos Muzik 			(void) fo->sfo_pathclass_next(NULL, &best_pclass,
48331c8dbe37SMilos Muzik 			    vlun->svl_fops_ctpriv);
48341c8dbe37SMilos Muzik 			if ((force_rereserve == 1) || ((resrv_pclass != NULL) &&
48351c8dbe37SMilos Muzik 			    (strcmp(pclass, best_pclass) == 0) &&
48361c8dbe37SMilos Muzik 			    (strcmp(resrv_pclass, best_pclass) != 0))) {
48371c8dbe37SMilos Muzik 				/*
48381c8dbe37SMilos Muzik 				 * Inform target driver that a reservation
48391c8dbe37SMilos Muzik 				 * should be reinstated because the reserved
48401c8dbe37SMilos Muzik 				 * path is not the most preferred one.
48411c8dbe37SMilos Muzik 				 */
48421c8dbe37SMilos Muzik 				mutex_enter(&vhci->vhci_mutex);
48431c8dbe37SMilos Muzik 				scsi_hba_reset_notify_callback(
48441c8dbe37SMilos Muzik 				    &vhci->vhci_mutex,
48451c8dbe37SMilos Muzik 				    &vhci->vhci_reset_notify_listf);
48461c8dbe37SMilos Muzik 				mutex_exit(&vhci->vhci_mutex);
48471c8dbe37SMilos Muzik 			}
48481c8dbe37SMilos Muzik 
48491c8dbe37SMilos Muzik 			if (update_pathinfo_done == 1) {
48501e1ddd6cScth 				return (MDI_SUCCESS);
48511e1ddd6cScth 			}
48521e1ddd6cScth 		} else {
48531e1ddd6cScth 			char	*tptr;
48541e1ddd6cScth 
48551e1ddd6cScth 			/*
48561e1ddd6cScth 			 * lets release the mutex before we try to
48571e1ddd6cScth 			 * allocate since the potential to sleep is
48581e1ddd6cScth 			 * possible.
48591e1ddd6cScth 			 */
48601e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
48611e1ddd6cScth 			tptr = kmem_alloc(strlen(pclass) + 1, KM_SLEEP);
48621e1ddd6cScth 			(void) strlcpy(tptr, pclass, (strlen(pclass) + 1));
48631e1ddd6cScth 			mutex_enter(&vlun->svl_mutex);
48641e1ddd6cScth 			vlun->svl_active_pclass = tptr;
48651e1ddd6cScth 		}
48661e1ddd6cScth 		mutex_exit(&vlun->svl_mutex);
48671e1ddd6cScth 		mdi_pi_set_state(pip, MDI_PATHINFO_STATE_ONLINE);
48681e1ddd6cScth 		vlun->svl_waiting_for_activepath = 0;
48691e1ddd6cScth 	} else if (opinfo.opinfo_path_state == SCSI_PATH_ACTIVE_NONOPT) {
48701e1ddd6cScth 		mutex_enter(&vlun->svl_mutex);
48711e1ddd6cScth 		if (vlun->svl_active_pclass == NULL) {
48721e1ddd6cScth 			char	*tptr;
48731e1ddd6cScth 
48741e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
48751e1ddd6cScth 			tptr = kmem_alloc(strlen(pclass) + 1, KM_SLEEP);
48761e1ddd6cScth 			(void) strlcpy(tptr, pclass, (strlen(pclass) + 1));
48771e1ddd6cScth 			mutex_enter(&vlun->svl_mutex);
48781e1ddd6cScth 			vlun->svl_active_pclass = tptr;
48791e1ddd6cScth 		}
48801e1ddd6cScth 		mutex_exit(&vlun->svl_mutex);
48811e1ddd6cScth 		mdi_pi_set_state(pip, MDI_PATHINFO_STATE_ONLINE);
48821e1ddd6cScth 		vlun->svl_waiting_for_activepath = 0;
48831e1ddd6cScth 	} else if (opinfo.opinfo_path_state == SCSI_PATH_INACTIVE) {
48841e1ddd6cScth 		mutex_enter(&vlun->svl_mutex);
48851e1ddd6cScth 		if (vlun->svl_active_pclass != NULL) {
48861e1ddd6cScth 			if (strcmp(vlun->svl_active_pclass, pclass) == 0) {
48871e1ddd6cScth 				mutex_exit(&vlun->svl_mutex);
48881e1ddd6cScth 				/*
48891e1ddd6cScth 				 * externally initiated failover has happened;
48901e1ddd6cScth 				 * force state to ONLINE (see comment above)
48911e1ddd6cScth 				 */
48921e1ddd6cScth 				mdi_pi_set_state(pip,
48931e1ddd6cScth 				    MDI_PATHINFO_STATE_ONLINE);
48941e1ddd6cScth 				vlun->svl_fo_support = opinfo.opinfo_mode;
48951e1ddd6cScth 				mdi_pi_set_preferred(pip,
48961e1ddd6cScth 				    opinfo.opinfo_preferred);
48971e1ddd6cScth 				return (MDI_SUCCESS);
48981e1ddd6cScth 			}
48991e1ddd6cScth 		}
49001e1ddd6cScth 		mutex_exit(&vlun->svl_mutex);
49011e1ddd6cScth 		mdi_pi_set_state(pip, MDI_PATHINFO_STATE_STANDBY);
49021e1ddd6cScth 
49031e1ddd6cScth 		/*
49041e1ddd6cScth 		 * Initiate auto-failback, if enabled, for path if path-state
49051e1ddd6cScth 		 * is transitioning from OFFLINE->STANDBY and pathclass is the
490655e592a2SRandall Ralphs 		 * preferred pathclass for this storage.
49071e1ddd6cScth 		 * NOTE: In case where opinfo_path_state is SCSI_PATH_ACTIVE
49081e1ddd6cScth 		 * (above), where the pi state is set to STANDBY, we don't
49091e1ddd6cScth 		 * initiate auto-failback as the next IO shall take care of.
49101e1ddd6cScth 		 * this. See comment above.
49111e1ddd6cScth 		 */
49120205780bSrralphs 		(void) fo->sfo_pathclass_next(NULL, &best_pclass,
49131e1ddd6cScth 		    vlun->svl_fops_ctpriv);
49141e1ddd6cScth 		if (((vhci->vhci_conf_flags & VHCI_CONF_FLAGS_AUTO_FAILBACK) ==
49151e1ddd6cScth 		    VHCI_CONF_FLAGS_AUTO_FAILBACK) &&
4916b0de01caSChris Liu 		    (strcmp(pclass, best_pclass) == 0) &&
49171e1ddd6cScth 		    ((MDI_PI_OLD_STATE(pip) == MDI_PATHINFO_STATE_OFFLINE) ||
49181e1ddd6cScth 		    (MDI_PI_OLD_STATE(pip) == MDI_PATHINFO_STATE_INIT))) {
49191e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "%s pathclass path: %p"
49201e1ddd6cScth 			    " OFFLINE->STANDBY transition for lun %s\n",
49211e1ddd6cScth 			    best_pclass, (void *)pip, vlun->svl_lun_wwn));
49221e1ddd6cScth 			(void) taskq_dispatch(vhci->vhci_taskq,
49231e1ddd6cScth 			    vhci_initiate_auto_failback, (void *) vlun,
49241e1ddd6cScth 			    KM_SLEEP);
49251e1ddd6cScth 		}
49261e1ddd6cScth 	}
49271e1ddd6cScth 	vlun->svl_fo_support = opinfo.opinfo_mode;
49281e1ddd6cScth 	mdi_pi_set_preferred(pip, opinfo.opinfo_preferred);
49291e1ddd6cScth 
49301e1ddd6cScth 	VHCI_DEBUG(8, (CE_NOTE, NULL, "vhci_update_pathinfo: opinfo_rev = %x,"
49311e1ddd6cScth 	    " opinfo_path_state = %x opinfo_preferred = %x, opinfo_mode = %x\n",
49321e1ddd6cScth 	    opinfo.opinfo_rev, opinfo.opinfo_path_state,
49331e1ddd6cScth 	    opinfo.opinfo_preferred, opinfo.opinfo_mode));
49341e1ddd6cScth 
49351e1ddd6cScth 	return (MDI_SUCCESS);
49361e1ddd6cScth }
49371e1ddd6cScth 
49381e1ddd6cScth /*
49391e1ddd6cScth  * Form the kstat name and and call mdi_pi_kstat_create()
49401e1ddd6cScth  */
49411e1ddd6cScth void
vhci_kstat_create_pathinfo(mdi_pathinfo_t * pip)49421e1ddd6cScth vhci_kstat_create_pathinfo(mdi_pathinfo_t *pip)
49431e1ddd6cScth {
49441e1ddd6cScth 	dev_info_t	*tgt_dip;
49451e1ddd6cScth 	dev_info_t	*pdip;
49461e1ddd6cScth 	char		*guid;
49471e1ddd6cScth 	char		*target_port, *target_port_dup;
49481e1ddd6cScth 	char		ks_name[KSTAT_STRLEN];
49491e1ddd6cScth 	uint_t		pid;
49501e1ddd6cScth 	int		by_id;
49511e1ddd6cScth 	mod_hash_val_t	hv;
49521e1ddd6cScth 
49531e1ddd6cScth 
49541e1ddd6cScth 	/* return if we have already allocated kstats */
49551e1ddd6cScth 	if (mdi_pi_kstat_exists(pip))
49561e1ddd6cScth 		return;
49571e1ddd6cScth 
49581e1ddd6cScth 	/*
49591e1ddd6cScth 	 * We need instance numbers to create a kstat name, return if we don't
49601e1ddd6cScth 	 * have instance numbers assigned yet.
49611e1ddd6cScth 	 */
49621e1ddd6cScth 	tgt_dip = mdi_pi_get_client(pip);
49631e1ddd6cScth 	pdip = mdi_pi_get_phci(pip);
49641e1ddd6cScth 	if ((ddi_get_instance(tgt_dip) == -1) || (ddi_get_instance(pdip) == -1))
49651e1ddd6cScth 		return;
49661e1ddd6cScth 
49671e1ddd6cScth 	/*
49681e1ddd6cScth 	 * A path oriented kstat has a ks_name of the form:
49691e1ddd6cScth 	 *
49701e1ddd6cScth 	 * <client-driver><instance>.t<pid>.<pHCI-driver><instance>
49711e1ddd6cScth 	 *
49721e1ddd6cScth 	 * We maintain a bidirectional 'target-port' to <pid> map,
49731e1ddd6cScth 	 * called targetmap. All pathinfo nodes with the same
4974bbf21555SRichard Lowe 	 * 'target-port' map to the same <pid>. The iostat(8) code,
49751e1ddd6cScth 	 * when parsing a path oriented kstat name, uses the <pid> as
49761e1ddd6cScth 	 * a SCSI_VHCI_GET_TARGET_LONGNAME ioctl argument in order
49771e1ddd6cScth 	 * to get the 'target-port'. For KSTAT_FLAG_PERSISTENT kstats,
49781e1ddd6cScth 	 * this ioctl needs to translate a <pid> to a 'target-port'
49791e1ddd6cScth 	 * even after all pathinfo nodes associated with the
49801e1ddd6cScth 	 * 'target-port' have been destroyed. This is needed to support
4981bbf21555SRichard Lowe 	 * consistent first-iteration activity-since-boot iostat(8)
49821e1ddd6cScth 	 * output. Because of this requirement, the mapping can't be
49831e1ddd6cScth 	 * based on pathinfo information in a devinfo snapshot.
49841e1ddd6cScth 	 */
49851e1ddd6cScth 
49861e1ddd6cScth 	/* determine 'target-port' */
49871e1ddd6cScth 	if (mdi_prop_lookup_string(pip,
498855e592a2SRandall Ralphs 	    SCSI_ADDR_PROP_TARGET_PORT, &target_port) == MDI_SUCCESS) {
49891e1ddd6cScth 		target_port_dup = i_ddi_strdup(target_port, KM_SLEEP);
49901e1ddd6cScth 		(void) mdi_prop_free(target_port);
49911e1ddd6cScth 		by_id = 1;
49921e1ddd6cScth 	} else {
49931e1ddd6cScth 		/*
49941e1ddd6cScth 		 * If the pHCI did not set up 'target-port' on this
49951e1ddd6cScth 		 * pathinfo node, assume that our client is the only
49961e1ddd6cScth 		 * one with paths to the device by using the guid
49971e1ddd6cScth 		 * value as the 'target-port'. Since no other client
49981e1ddd6cScth 		 * will have the same guid, no other client will use
49991e1ddd6cScth 		 * the same <pid>.  NOTE: a client with an instance
50001e1ddd6cScth 		 * number always has a guid.
50011e1ddd6cScth 		 */
50021e1ddd6cScth 		(void) ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip,
50031e1ddd6cScth 		    PROPFLAGS, MDI_CLIENT_GUID_PROP, &guid);
50041e1ddd6cScth 		target_port_dup = i_ddi_strdup(guid, KM_SLEEP);
50051e1ddd6cScth 		ddi_prop_free(guid);
50061e1ddd6cScth 
50071e1ddd6cScth 		/*
50081e1ddd6cScth 		 * For this type of mapping we don't want the
50091e1ddd6cScth 		 * <id> -> 'target-port' mapping to be made.  This
50101e1ddd6cScth 		 * will cause the SCSI_VHCI_GET_TARGET_LONGNAME ioctl
5011bbf21555SRichard Lowe 		 * to fail, and the iostat(8) long '-n' output will
50121e1ddd6cScth 		 * still use the <pid>.  We do this because we just
50131e1ddd6cScth 		 * made up the 'target-port' using the guid, and we
50141e1ddd6cScth 		 * don't want to expose that fact in iostat output.
50151e1ddd6cScth 		 */
50161e1ddd6cScth 		by_id = 0;
50171e1ddd6cScth 	}
50181e1ddd6cScth 
50191e1ddd6cScth 	/* find/establish <pid> given 'target-port' */
50201e1ddd6cScth 	mutex_enter(&vhci_targetmap_mutex);
50211e1ddd6cScth 	if (mod_hash_find(vhci_targetmap_byport,
50221e1ddd6cScth 	    (mod_hash_key_t)target_port_dup, &hv) == 0) {
50231e1ddd6cScth 		pid = (int)(intptr_t)hv;	/* mapping exists */
50241e1ddd6cScth 	} else {
50251e1ddd6cScth 		pid = vhci_targetmap_pid++;	/* new mapping */
50261e1ddd6cScth 
50271e1ddd6cScth 		(void) mod_hash_insert(vhci_targetmap_byport,
50281e1ddd6cScth 		    (mod_hash_key_t)target_port_dup,
50291e1ddd6cScth 		    (mod_hash_val_t)(intptr_t)pid);
50301e1ddd6cScth 		if (by_id) {
50311e1ddd6cScth 			(void) mod_hash_insert(vhci_targetmap_bypid,
50321e1ddd6cScth 			    (mod_hash_key_t)(uintptr_t)pid,
50331e1ddd6cScth 			    (mod_hash_val_t)(uintptr_t)target_port_dup);
50341e1ddd6cScth 		}
50351e1ddd6cScth 		target_port_dup = NULL;		/* owned by hash */
50361e1ddd6cScth 	}
50371e1ddd6cScth 	mutex_exit(&vhci_targetmap_mutex);
50381e1ddd6cScth 
50391e1ddd6cScth 	/* form kstat name */
50401e1ddd6cScth 	(void) snprintf(ks_name, KSTAT_STRLEN, "%s%d.t%d.%s%d",
50411e1ddd6cScth 	    ddi_driver_name(tgt_dip), ddi_get_instance(tgt_dip),
50421e1ddd6cScth 	    pid, ddi_driver_name(pdip), ddi_get_instance(pdip));
50431e1ddd6cScth 
50441e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_path_online: path:%p "
50451e1ddd6cScth 	    "kstat %s: pid %x <-> port %s\n", (void *)pip,
50461e1ddd6cScth 	    ks_name, pid, target_port_dup));
50471e1ddd6cScth 	if (target_port_dup)
50481e1ddd6cScth 		kmem_free(target_port_dup, strlen(target_port_dup) + 1);
50491e1ddd6cScth 
50501e1ddd6cScth 	/* call mdi to create kstats with the name we built */
50511e1ddd6cScth 	(void) mdi_pi_kstat_create(pip, ks_name);
50521e1ddd6cScth }
50531e1ddd6cScth 
50541e1ddd6cScth /* ARGSUSED */
50551e1ddd6cScth static int
vhci_pathinfo_online(dev_info_t * vdip,mdi_pathinfo_t * pip,int flags)50561e1ddd6cScth vhci_pathinfo_online(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
50571e1ddd6cScth {
50581e1ddd6cScth 	scsi_hba_tran_t			*hba = NULL;
50591e1ddd6cScth 	struct scsi_device		*psd = NULL;
50601e1ddd6cScth 	scsi_vhci_lun_t			*vlun = NULL;
50611e1ddd6cScth 	dev_info_t			*pdip = NULL;
506255e592a2SRandall Ralphs 	dev_info_t			*cdip;
50631e1ddd6cScth 	dev_info_t			*tgt_dip;
50641e1ddd6cScth 	struct scsi_vhci		*vhci;
50651e1ddd6cScth 	char				*guid;
50661e1ddd6cScth 	struct scsi_failover_ops	*sfo;
50671e1ddd6cScth 	scsi_vhci_priv_t		*svp = NULL;
50681e1ddd6cScth 	struct scsi_address		*ap;
50691e1ddd6cScth 	struct scsi_pkt			*pkt;
50701e1ddd6cScth 	int				rval = MDI_FAILURE;
50711e1ddd6cScth 	mpapi_item_list_t		*list_ptr;
50721e1ddd6cScth 	mpapi_lu_data_t			*ld;
50731e1ddd6cScth 
50741e1ddd6cScth 	ASSERT(vdip != NULL);
50751e1ddd6cScth 	ASSERT(pip != NULL);
50761e1ddd6cScth 
50771e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
50781e1ddd6cScth 	ASSERT(vhci != NULL);
50791e1ddd6cScth 
50801e1ddd6cScth 	pdip = mdi_pi_get_phci(pip);
50811e1ddd6cScth 	hba = ddi_get_driver_private(pdip);
50821e1ddd6cScth 	ASSERT(hba != NULL);
50831e1ddd6cScth 
50841e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
50851e1ddd6cScth 	ASSERT(svp != NULL);
50861e1ddd6cScth 
508755e592a2SRandall Ralphs 	cdip = mdi_pi_get_client(pip);
508855e592a2SRandall Ralphs 	ASSERT(cdip != NULL);
508955e592a2SRandall Ralphs 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip, PROPFLAGS,
50901e1ddd6cScth 	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
50911e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_path_online: lun guid "
50921e1ddd6cScth 		    "property failed"));
50931e1ddd6cScth 		goto failure;
50941e1ddd6cScth 	}
50951e1ddd6cScth 
509655e592a2SRandall Ralphs 	vlun = vhci_lun_lookup(cdip);
50971e1ddd6cScth 	ASSERT(vlun != NULL);
50981e1ddd6cScth 
50991e1ddd6cScth 	ddi_prop_free(guid);
51001e1ddd6cScth 
51011e1ddd6cScth 	vlun->svl_dip = mdi_pi_get_client(pip);
51021e1ddd6cScth 	ASSERT(vlun->svl_dip != NULL);
51031e1ddd6cScth 
51041e1ddd6cScth 	psd = svp->svp_psd;
51051e1ddd6cScth 	ASSERT(psd != NULL);
51061e1ddd6cScth 
5107526aa211SKevin Yu 	ap = &psd->sd_address;
5108526aa211SKevin Yu 
51091e1ddd6cScth 	/*
511055e592a2SRandall Ralphs 	 * Get inquiry data into pathinfo related scsi_device structure.
511155e592a2SRandall Ralphs 	 * Free sq_inq when pathinfo related scsi_device structure is destroyed
511255e592a2SRandall Ralphs 	 * by vhci_pathinfo_uninit(). In other words, vhci maintains its own
511355e592a2SRandall Ralphs 	 * copy of scsi_device and scsi_inquiry data on a per-path basis.
51141e1ddd6cScth 	 */
511555e592a2SRandall Ralphs 	if (scsi_probe(psd, SLEEP_FUNC) != SCSIPROBE_EXISTS) {
51161e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_pathinfo_online: "
511755e592a2SRandall Ralphs 		    "scsi_probe failed path:%p rval:%x\n", (void *)pip, rval));
51181e1ddd6cScth 		rval = MDI_FAILURE;
51191e1ddd6cScth 		goto failure;
51201e1ddd6cScth 	}
51211e1ddd6cScth 
51221e1ddd6cScth 	/*
512355e592a2SRandall Ralphs 	 * See if we have a failover module to support the device.
51241e1ddd6cScth 	 *
512555e592a2SRandall Ralphs 	 * We re-probe to determine the failover ops for each path. This
512655e592a2SRandall Ralphs 	 * is done in case there are any path-specific side-effects associated
512755e592a2SRandall Ralphs 	 * with the sfo_device_probe implementation.
51281e1ddd6cScth 	 *
512955e592a2SRandall Ralphs 	 * Give the first successfull sfo_device_probe the opportunity to
513055e592a2SRandall Ralphs 	 * establish 'ctpriv', vlun/client private data. The ctpriv will
513155e592a2SRandall Ralphs 	 * then be passed into the failover module on all other sfo_device_*()
513255e592a2SRandall Ralphs 	 * operations (and must be freed by sfo_device_unprobe implementation).
51331e1ddd6cScth 	 *
513455e592a2SRandall Ralphs 	 * NOTE: While sfo_device_probe is done once per path,
513555e592a2SRandall Ralphs 	 * sfo_device_unprobe only occurs once - when the vlun is destroyed.
51361e1ddd6cScth 	 *
513755e592a2SRandall Ralphs 	 * NOTE: We don't currently support per-path fops private data
513855e592a2SRandall Ralphs 	 * mechanism.
51391e1ddd6cScth 	 */
514055e592a2SRandall Ralphs 	sfo = vhci_dev_fo(vdip, psd,
514155e592a2SRandall Ralphs 	    &vlun->svl_fops_ctpriv, &vlun->svl_fops_name);
51421e1ddd6cScth 
514355e592a2SRandall Ralphs 	/* check path configuration result with current vlun state */
514455e592a2SRandall Ralphs 	if (((sfo && vlun->svl_fops) && (sfo != vlun->svl_fops)) ||
514555e592a2SRandall Ralphs 	    (sfo && vlun->svl_not_supported) ||
514655e592a2SRandall Ralphs 	    ((sfo == NULL) && vlun->svl_fops)) {
514755e592a2SRandall Ralphs 		/* Getting different results for different paths. */
514855e592a2SRandall Ralphs 		VHCI_DEBUG(1, (CE_NOTE, vhci->vhci_dip,
514955e592a2SRandall Ralphs 		    "!vhci_pathinfo_online: dev (path 0x%p) contradiction\n",
515055e592a2SRandall Ralphs 		    (void *)pip));
51519b4f248cSChris Liu 		cmn_err(CE_WARN, "scsi_vhci: failover contradiction: "
515255e592a2SRandall Ralphs 		    "'%s'.vs.'%s': path %s\n",
515355e592a2SRandall Ralphs 		    vlun->svl_fops ? vlun->svl_fops->sfo_name : "NULL",
515455e592a2SRandall Ralphs 		    sfo ? sfo->sfo_name : "NULL", mdi_pi_pathname(pip));
515555e592a2SRandall Ralphs 		vlun->svl_not_supported = 1;
515655e592a2SRandall Ralphs 		rval = MDI_NOT_SUPPORTED;
515755e592a2SRandall Ralphs 		goto done;
515855e592a2SRandall Ralphs 	} else if (sfo == NULL) {
515955e592a2SRandall Ralphs 		/* No failover module - device not supported under vHCI.  */
51601e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, vhci->vhci_dip,
51611e1ddd6cScth 		    "!vhci_pathinfo_online: dev (path 0x%p) not "
51621e1ddd6cScth 		    "supported\n", (void *)pip));
516355e592a2SRandall Ralphs 
516455e592a2SRandall Ralphs 		/* XXX does this contradict vhci_is_dev_supported ? */
51651e1ddd6cScth 		vlun->svl_not_supported = 1;
51661e1ddd6cScth 		rval = MDI_NOT_SUPPORTED;
51671e1ddd6cScth 		goto done;
51681e1ddd6cScth 	}
51691e1ddd6cScth 
51701e1ddd6cScth 	/* failover supported for device - save failover_ops in vlun */
51711e1ddd6cScth 	vlun->svl_fops = sfo;
517255e592a2SRandall Ralphs 	ASSERT(vlun->svl_fops_name != NULL);
51731e1ddd6cScth 
51741e1ddd6cScth 	/*
51751e1ddd6cScth 	 * Obtain the device-type based mpxio options as specified in
51761e1ddd6cScth 	 * scsi_vhci.conf file.
51771e1ddd6cScth 	 *
51781e1ddd6cScth 	 * NOTE: currently, the end result is a call to
51791e1ddd6cScth 	 * mdi_set_lb_region_size().
51801e1ddd6cScth 	 */
518155e592a2SRandall Ralphs 	tgt_dip = psd->sd_dev;
518255e592a2SRandall Ralphs 	ASSERT(tgt_dip != NULL);
51831e1ddd6cScth 	vhci_get_device_type_mpxio_options(vdip, tgt_dip, psd);
51841e1ddd6cScth 
51851e1ddd6cScth 	/*
51861e1ddd6cScth 	 * if PGR is active, revalidate key and register on this path also,
51871e1ddd6cScth 	 * if key is still valid
51881e1ddd6cScth 	 */
51891e1ddd6cScth 	sema_p(&vlun->svl_pgr_sema);
51901e1ddd6cScth 	if (vlun->svl_pgr_active) {
51911e1ddd6cScth 		rval = vhci_pgr_validate_and_register(svp);
51921e1ddd6cScth 		if (rval != 1) {
51931e1ddd6cScth 			rval = MDI_FAILURE;
51941e1ddd6cScth 			sema_v(&vlun->svl_pgr_sema);
51951e1ddd6cScth 			goto failure;
51961e1ddd6cScth 		}
51971e1ddd6cScth 	}
51981e1ddd6cScth 	sema_v(&vlun->svl_pgr_sema);
51991e1ddd6cScth 
52001e1ddd6cScth 	if (svp->svp_new_path) {
52011e1ddd6cScth 		/*
52021e1ddd6cScth 		 * Last chance to perform any cleanup operations on this
52031e1ddd6cScth 		 * new path before making this path completely online.
52041e1ddd6cScth 		 */
52051e1ddd6cScth 		svp->svp_new_path = 0;
52061e1ddd6cScth 
52071e1ddd6cScth 		/*
52081e1ddd6cScth 		 * If scsi_vhci knows the lun is alread RESERVE'd,
52091e1ddd6cScth 		 * then skip the issue of RELEASE on new path.
52101e1ddd6cScth 		 */
52111e1ddd6cScth 		if ((vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) == 0) {
52121e1ddd6cScth 			/*
52131e1ddd6cScth 			 * Issue SCSI-2 RELEASE only for the first time on
52141e1ddd6cScth 			 * a new path just in case the host rebooted and
52151e1ddd6cScth 			 * a reservation is still pending on this path.
52161e1ddd6cScth 			 * IBM Shark storage does not clear RESERVE upon
52171e1ddd6cScth 			 * host reboot.
52181e1ddd6cScth 			 */
52191e1ddd6cScth 			pkt = scsi_init_pkt(ap, NULL, NULL, CDB_GROUP0,
52201e1ddd6cScth 			    sizeof (struct scsi_arq_status), 0, 0,
52211e1ddd6cScth 			    SLEEP_FUNC, NULL);
52221e1ddd6cScth 			if (pkt == NULL) {
52231e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
52241e1ddd6cScth 				    "!vhci_pathinfo_online: "
52251e1ddd6cScth 				    "Release init_pkt failed :%p\n",
52261e1ddd6cScth 				    (void *)pip));
52271e1ddd6cScth 				rval = MDI_FAILURE;
52281e1ddd6cScth 				goto failure;
52291e1ddd6cScth 			}
52301e1ddd6cScth 			pkt->pkt_cdbp[0] = SCMD_RELEASE;
52311e1ddd6cScth 			pkt->pkt_time = 60;
52321e1ddd6cScth 
52331e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
52341e1ddd6cScth 			    "!vhci_path_online: path:%p "
52351e1ddd6cScth 			    "Issued SCSI-2 RELEASE\n", (void *)pip));
52361e1ddd6cScth 
52371e1ddd6cScth 			/* Ignore the return value */
52381e1ddd6cScth 			(void) vhci_do_scsi_cmd(pkt);
52391e1ddd6cScth 			scsi_destroy_pkt(pkt);
52401e1ddd6cScth 		}
52411e1ddd6cScth 	}
52421e1ddd6cScth 
52431e1ddd6cScth 	rval = vhci_update_pathinfo(psd, pip, sfo, vlun, vhci);
52441e1ddd6cScth 	if (rval == MDI_FAILURE) {
52451e1ddd6cScth 		goto failure;
52461e1ddd6cScth 	}
52471e1ddd6cScth 
52481e1ddd6cScth 	/* Initialize MP-API data */
52491e1ddd6cScth 	vhci_update_mpapi_data(vhci, vlun, pip);
52501e1ddd6cScth 
52511e1ddd6cScth 	/*
52521e1ddd6cScth 	 * MP-API also needs the Inquiry data to be maintained in the
52531e1ddd6cScth 	 * mp_vendor_prop_t structure, so find the lun and update its
52541e1ddd6cScth 	 * structure with this data.
52551e1ddd6cScth 	 */
52561e1ddd6cScth 	list_ptr = (mpapi_item_list_t *)vhci_get_mpapi_item(vhci, NULL,
52571e1ddd6cScth 	    MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
52581e1ddd6cScth 	ld = (mpapi_lu_data_t *)list_ptr->item->idata;
52591e1ddd6cScth 	if (ld != NULL) {
52601e1ddd6cScth 		bcopy(psd->sd_inq->inq_vid, ld->prop.prodInfo.vendor, 8);
52611e1ddd6cScth 		bcopy(psd->sd_inq->inq_pid, ld->prop.prodInfo.product, 16);
52621e1ddd6cScth 		bcopy(psd->sd_inq->inq_revision, ld->prop.prodInfo.revision, 4);
52631e1ddd6cScth 	} else {
52641e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_pathinfo_online: "
52651e1ddd6cScth 		    "mpapi_lu_data_t is NULL"));
52661e1ddd6cScth 	}
52671e1ddd6cScth 
52681e1ddd6cScth 	/* create kstats for path */
52691e1ddd6cScth 	vhci_kstat_create_pathinfo(pip);
52701e1ddd6cScth 
52711e1ddd6cScth done:
52721e1ddd6cScth 	mutex_enter(&vhci_global_mutex);
52731e1ddd6cScth 	cv_broadcast(&vhci_cv);
52741e1ddd6cScth 	mutex_exit(&vhci_global_mutex);
52751e1ddd6cScth 
52761e1ddd6cScth 	if (vlun->svl_setcap_done) {
52771e1ddd6cScth 		(void) vhci_pHCI_cap(ap, "sector-size",
52781e1ddd6cScth 		    vlun->svl_sector_size, 1, pip);
52791e1ddd6cScth 	}
52801e1ddd6cScth 
52811e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_path_online: path:%p\n",
52821e1ddd6cScth 	    (void *)pip));
52831e1ddd6cScth 
52841e1ddd6cScth failure:
52851e1ddd6cScth 	return (rval);
52861e1ddd6cScth }
52871e1ddd6cScth 
52881e1ddd6cScth /*
52891e1ddd6cScth  * path offline handler.  Release all bindings that will not be
52901e1ddd6cScth  * released by the normal packet transport/completion code path.
52911e1ddd6cScth  * Since we don't (presently) keep any bindings alive outside of
52921e1ddd6cScth  * the in-transport packets (which will be released on completion)
52931e1ddd6cScth  * there is not much to do here.
52941e1ddd6cScth  */
52951e1ddd6cScth /* ARGSUSED */
52961e1ddd6cScth static int
vhci_pathinfo_offline(dev_info_t * vdip,mdi_pathinfo_t * pip,int flags)52971e1ddd6cScth vhci_pathinfo_offline(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
52981e1ddd6cScth {
52991e1ddd6cScth 	scsi_hba_tran_t		*hba = NULL;
53001e1ddd6cScth 	struct scsi_device	*psd = NULL;
53011e1ddd6cScth 	dev_info_t		*pdip = NULL;
53021e1ddd6cScth 	dev_info_t		*cdip = NULL;
53031e1ddd6cScth 	scsi_vhci_priv_t	*svp = NULL;
53041e1ddd6cScth 
53051e1ddd6cScth 	ASSERT(vdip != NULL);
53061e1ddd6cScth 	ASSERT(pip != NULL);
53071e1ddd6cScth 
53081e1ddd6cScth 	pdip = mdi_pi_get_phci(pip);
53091e1ddd6cScth 	ASSERT(pdip != NULL);
53101e1ddd6cScth 	if (pdip == NULL) {
53111e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, vdip, "Invalid path 0x%p: NULL "
53121e1ddd6cScth 		    "phci dip", (void *)pip));
53131e1ddd6cScth 		return (MDI_FAILURE);
53141e1ddd6cScth 	}
53151e1ddd6cScth 
53161e1ddd6cScth 	cdip = mdi_pi_get_client(pip);
53171e1ddd6cScth 	ASSERT(cdip != NULL);
53181e1ddd6cScth 	if (cdip == NULL) {
53191e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, vdip, "Invalid path 0x%p: NULL "
53201e1ddd6cScth 		    "client dip", (void *)pip));
53211e1ddd6cScth 		return (MDI_FAILURE);
53221e1ddd6cScth 	}
53231e1ddd6cScth 
53241e1ddd6cScth 	hba = ddi_get_driver_private(pdip);
53251e1ddd6cScth 	ASSERT(hba != NULL);
53261e1ddd6cScth 
53271e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
53281e1ddd6cScth 	if (svp == NULL) {
53291e1ddd6cScth 		/*
53301e1ddd6cScth 		 * mdi_pathinfo node in INIT state can have vHCI private
53311e1ddd6cScth 		 * information set to null
53321e1ddd6cScth 		 */
53331e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
53341e1ddd6cScth 		    "svp is NULL for pip 0x%p\n", (void *)pip));
53351e1ddd6cScth 		return (MDI_SUCCESS);
53361e1ddd6cScth 	}
53371e1ddd6cScth 
53381e1ddd6cScth 	psd = svp->svp_psd;
53391e1ddd6cScth 	ASSERT(psd != NULL);
53401e1ddd6cScth 
53411e1ddd6cScth 	mutex_enter(&svp->svp_mutex);
53421e1ddd6cScth 
53431e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
53441e1ddd6cScth 	    "%d cmds pending on path: 0x%p\n", svp->svp_cmds, (void *)pip));
53451e1ddd6cScth 	while (svp->svp_cmds != 0) {
5346d3d50737SRafael Vanoni 		if (cv_reltimedwait(&svp->svp_cv, &svp->svp_mutex,
5347d3d50737SRafael Vanoni 		    drv_usectohz(vhci_path_quiesce_timeout * 1000000),
5348d3d50737SRafael Vanoni 		    TR_CLOCK_TICK) == -1) {
53491e1ddd6cScth 			/*
53501e1ddd6cScth 			 * The timeout time reached without the condition
53511e1ddd6cScth 			 * being signaled.
53521e1ddd6cScth 			 */
53531e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
53541e1ddd6cScth 			    "Timeout reached on path 0x%p without the cond\n",
53551e1ddd6cScth 			    (void *)pip));
53561e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
53571e1ddd6cScth 			    "%d cmds still pending on path: 0x%p\n",
53581e1ddd6cScth 			    svp->svp_cmds, (void *)pip));
53591e1ddd6cScth 			break;
53601e1ddd6cScth 		}
53611e1ddd6cScth 	}
53621e1ddd6cScth 	mutex_exit(&svp->svp_mutex);
53631e1ddd6cScth 
53641e1ddd6cScth 	/*
53651e1ddd6cScth 	 * Check to see if this vlun has an active SCSI-II RESERVE. And this
53661e1ddd6cScth 	 * is the pip for the path that has been reserved.
53671e1ddd6cScth 	 * If so clear the reservation by sending a reset, so the host will not
53681e1ddd6cScth 	 * get a reservation conflict.  Reset the flag VLUN_RESERVE_ACTIVE_FLG
53691e1ddd6cScth 	 * for this lun.  Also a reset notify is sent to the target driver
53701e1ddd6cScth 	 * just in case the POR check condition is cleared by some other layer
53711e1ddd6cScth 	 * in the stack.
53721e1ddd6cScth 	 */
53731e1ddd6cScth 	if (svp->svp_svl->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
53741e1ddd6cScth 		if (pip == svp->svp_svl->svl_resrv_pip) {
53751e1ddd6cScth 			if (vhci_recovery_reset(svp->svp_svl,
53761e1ddd6cScth 			    &svp->svp_psd->sd_address, TRUE,
53771e1ddd6cScth 			    VHCI_DEPTH_TARGET) == 0) {
53781e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
53791e1ddd6cScth 				    "!vhci_pathinfo_offline (pip:%p):"
53801e1ddd6cScth 				    "reset failed, retrying\n", (void *)pip));
53811e1ddd6cScth 				delay(1 * drv_usectohz(1000000));
53821e1ddd6cScth 				if (vhci_recovery_reset(svp->svp_svl,
53831e1ddd6cScth 				    &svp->svp_psd->sd_address, TRUE,
53841e1ddd6cScth 				    VHCI_DEPTH_TARGET) == 0) {
53851e1ddd6cScth 					VHCI_DEBUG(1, (CE_NOTE, NULL,
53861e1ddd6cScth 					    "!vhci_pathinfo_offline "
53871e1ddd6cScth 					    "(pip:%p): reset failed, "
53881e1ddd6cScth 					    "giving up!\n", (void *)pip));
53891e1ddd6cScth 				}
53901e1ddd6cScth 			}
53911e1ddd6cScth 			svp->svp_svl->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
53921e1ddd6cScth 		}
53931e1ddd6cScth 	}
53941e1ddd6cScth 
53951e1ddd6cScth 	mdi_pi_set_state(pip, MDI_PATHINFO_STATE_OFFLINE);
53961e1ddd6cScth 	vhci_mpapi_set_path_state(vdip, pip, MP_DRVR_PATH_STATE_REMOVED);
53971e1ddd6cScth 
53981e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL,
53991e1ddd6cScth 	    "!vhci_pathinfo_offline: offlined path 0x%p\n", (void *)pip));
54001e1ddd6cScth 	return (MDI_SUCCESS);
54011e1ddd6cScth }
54021e1ddd6cScth 
54031e1ddd6cScth 
54041e1ddd6cScth /*
54051e1ddd6cScth  * routine for SCSI VHCI IOCTL implementation.
54061e1ddd6cScth  */
54071e1ddd6cScth /* ARGSUSED */
54081e1ddd6cScth static int
vhci_ctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rval)54091e1ddd6cScth vhci_ctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval)
54101e1ddd6cScth {
54111e1ddd6cScth 	struct scsi_vhci		*vhci;
54121e1ddd6cScth 	dev_info_t			*vdip;
54131e1ddd6cScth 	mdi_pathinfo_t			*pip;
54141e1ddd6cScth 	int				instance, held;
54151e1ddd6cScth 	int				retval = 0;
54161e1ddd6cScth 	caddr_t				phci_path = NULL, client_path = NULL;
54171e1ddd6cScth 	caddr_t				paddr = NULL;
54181e1ddd6cScth 	sv_iocdata_t			ioc;
54191e1ddd6cScth 	sv_iocdata_t			*pioc = &ioc;
54201e1ddd6cScth 	sv_switch_to_cntlr_iocdata_t	iocsc;
54211e1ddd6cScth 	sv_switch_to_cntlr_iocdata_t	*piocsc = &iocsc;
54221e1ddd6cScth 	caddr_t				s;
54231e1ddd6cScth 	scsi_vhci_lun_t			*vlun;
54241e1ddd6cScth 	struct scsi_failover_ops	*fo;
54251e1ddd6cScth 	char				*pclass;
54261e1ddd6cScth 
54271e1ddd6cScth 	/* Check for validity of vhci structure */
54281e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(dev)));
54291e1ddd6cScth 	if (vhci == NULL) {
54301e1ddd6cScth 		return (ENXIO);
54311e1ddd6cScth 	}
54321e1ddd6cScth 
54331e1ddd6cScth 	mutex_enter(&vhci->vhci_mutex);
54341e1ddd6cScth 	if ((vhci->vhci_state & VHCI_STATE_OPEN) == 0) {
54351e1ddd6cScth 		mutex_exit(&vhci->vhci_mutex);
54361e1ddd6cScth 		return (ENXIO);
54371e1ddd6cScth 	}
54381e1ddd6cScth 	mutex_exit(&vhci->vhci_mutex);
54391e1ddd6cScth 
54401e1ddd6cScth 	/* Get the vhci dip */
54411e1ddd6cScth 	vdip = vhci->vhci_dip;
54421e1ddd6cScth 	ASSERT(vdip != NULL);
54431e1ddd6cScth 	instance = ddi_get_instance(vdip);
54441e1ddd6cScth 
54451e1ddd6cScth 	/* Allocate memory for getting parameters from userland */
54461e1ddd6cScth 	phci_path	= kmem_zalloc(MAXPATHLEN, KM_SLEEP);
54471e1ddd6cScth 	client_path	= kmem_zalloc(MAXPATHLEN, KM_SLEEP);
54481e1ddd6cScth 	paddr		= kmem_zalloc(MAXNAMELEN, KM_SLEEP);
54491e1ddd6cScth 
54501e1ddd6cScth 	/*
54511e1ddd6cScth 	 * Set a local variable indicating the ioctl name. Used for
54521e1ddd6cScth 	 * printing debug strings.
54531e1ddd6cScth 	 */
54541e1ddd6cScth 	switch (cmd) {
54551e1ddd6cScth 	case SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO:
54561e1ddd6cScth 		s = "GET_CLIENT_MULTIPATH_INFO";
54571e1ddd6cScth 		break;
54581e1ddd6cScth 
54591e1ddd6cScth 	case SCSI_VHCI_GET_PHCI_MULTIPATH_INFO:
54601e1ddd6cScth 		s = "GET_PHCI_MULTIPATH_INFO";
54611e1ddd6cScth 		break;
54621e1ddd6cScth 
54631e1ddd6cScth 	case SCSI_VHCI_GET_CLIENT_NAME:
54641e1ddd6cScth 		s = "GET_CLIENT_NAME";
54651e1ddd6cScth 		break;
54661e1ddd6cScth 
54671e1ddd6cScth 	case SCSI_VHCI_PATH_ONLINE:
54681e1ddd6cScth 		s = "PATH_ONLINE";
54691e1ddd6cScth 		break;
54701e1ddd6cScth 
54711e1ddd6cScth 	case SCSI_VHCI_PATH_OFFLINE:
54721e1ddd6cScth 		s = "PATH_OFFLINE";
54731e1ddd6cScth 		break;
54741e1ddd6cScth 
54751e1ddd6cScth 	case SCSI_VHCI_PATH_STANDBY:
54761e1ddd6cScth 		s = "PATH_STANDBY";
54771e1ddd6cScth 		break;
54781e1ddd6cScth 
54791e1ddd6cScth 	case SCSI_VHCI_PATH_TEST:
54801e1ddd6cScth 		s = "PATH_TEST";
54811e1ddd6cScth 		break;
54821e1ddd6cScth 
54831e1ddd6cScth 	case SCSI_VHCI_SWITCH_TO_CNTLR:
54841e1ddd6cScth 		s = "SWITCH_TO_CNTLR";
54851e1ddd6cScth 		break;
54861e1ddd6cScth 	case SCSI_VHCI_PATH_DISABLE:
54871e1ddd6cScth 		s = "PATH_DISABLE";
54881e1ddd6cScth 		break;
54891e1ddd6cScth 	case SCSI_VHCI_PATH_ENABLE:
54901e1ddd6cScth 		s = "PATH_ENABLE";
54911e1ddd6cScth 		break;
54921e1ddd6cScth 
54931e1ddd6cScth 	case SCSI_VHCI_GET_TARGET_LONGNAME:
54941e1ddd6cScth 		s = "GET_TARGET_LONGNAME";
54951e1ddd6cScth 		break;
54961e1ddd6cScth 
54971e1ddd6cScth #ifdef	DEBUG
54981e1ddd6cScth 	case SCSI_VHCI_CONFIGURE_PHCI:
54991e1ddd6cScth 		s = "CONFIGURE_PHCI";
55001e1ddd6cScth 		break;
55011e1ddd6cScth 
55021e1ddd6cScth 	case SCSI_VHCI_UNCONFIGURE_PHCI:
55031e1ddd6cScth 		s = "UNCONFIGURE_PHCI";
55041e1ddd6cScth 		break;
55051e1ddd6cScth #endif
55061e1ddd6cScth 
55071e1ddd6cScth 	default:
55081e1ddd6cScth 		s = "Unknown";
55091e1ddd6cScth 		vhci_log(CE_NOTE, vdip,
55101e1ddd6cScth 		    "!vhci%d: ioctl %x (unsupported ioctl)", instance, cmd);
55111e1ddd6cScth 		retval = ENOTSUP;
55121e1ddd6cScth 		break;
55131e1ddd6cScth 	}
55141e1ddd6cScth 	if (retval != 0) {
55151e1ddd6cScth 		goto end;
55161e1ddd6cScth 	}
55171e1ddd6cScth 
55181e1ddd6cScth 	VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci%d: ioctl <%s>", instance, s));
55191e1ddd6cScth 
55201e1ddd6cScth 	/*
55211e1ddd6cScth 	 * Get IOCTL parameters from userland
55221e1ddd6cScth 	 */
55231e1ddd6cScth 	switch (cmd) {
55241e1ddd6cScth 	case SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO:
55251e1ddd6cScth 	case SCSI_VHCI_GET_PHCI_MULTIPATH_INFO:
55261e1ddd6cScth 	case SCSI_VHCI_GET_CLIENT_NAME:
55271e1ddd6cScth 	case SCSI_VHCI_PATH_ONLINE:
55281e1ddd6cScth 	case SCSI_VHCI_PATH_OFFLINE:
55291e1ddd6cScth 	case SCSI_VHCI_PATH_STANDBY:
55301e1ddd6cScth 	case SCSI_VHCI_PATH_TEST:
55311e1ddd6cScth 	case SCSI_VHCI_PATH_DISABLE:
55321e1ddd6cScth 	case SCSI_VHCI_PATH_ENABLE:
55331e1ddd6cScth 	case SCSI_VHCI_GET_TARGET_LONGNAME:
55341e1ddd6cScth #ifdef	DEBUG
55351e1ddd6cScth 	case SCSI_VHCI_CONFIGURE_PHCI:
55361e1ddd6cScth 	case SCSI_VHCI_UNCONFIGURE_PHCI:
55371e1ddd6cScth #endif
55381e1ddd6cScth 		retval = vhci_get_iocdata((const void *)data, pioc, mode, s);
55391e1ddd6cScth 		break;
55401e1ddd6cScth 
55411e1ddd6cScth 	case SCSI_VHCI_SWITCH_TO_CNTLR:
55421e1ddd6cScth 		retval = vhci_get_iocswitchdata((const void *)data, piocsc,
55431e1ddd6cScth 		    mode, s);
55441e1ddd6cScth 		break;
55451e1ddd6cScth 	}
55461e1ddd6cScth 	if (retval != 0) {
55471e1ddd6cScth 		goto end;
55481e1ddd6cScth 	}
55491e1ddd6cScth 
55501e1ddd6cScth 
55511e1ddd6cScth 	/*
55521e1ddd6cScth 	 * Process the IOCTL
55531e1ddd6cScth 	 */
55541e1ddd6cScth 	switch (cmd) {
55551e1ddd6cScth 	case SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO:
55561e1ddd6cScth 	{
55571e1ddd6cScth 		uint_t		num_paths;	/* Num paths to client dev */
55581e1ddd6cScth 		sv_path_info_t	*upibuf = NULL;	/* To keep userland values */
55591e1ddd6cScth 		sv_path_info_t	*kpibuf = NULL; /* Kernel data for ioctls */
55601e1ddd6cScth 		dev_info_t	*cdip;		/* Client device dip */
55611e1ddd6cScth 
55621e1ddd6cScth 		if (pioc->ret_elem == NULL) {
55631e1ddd6cScth 			retval = EINVAL;
55641e1ddd6cScth 			break;
55651e1ddd6cScth 		}
55661e1ddd6cScth 
55671e1ddd6cScth 		/* Get client device path from user land */
55681e1ddd6cScth 		if (vhci_ioc_get_client_path(pioc, client_path, mode, s)) {
55691e1ddd6cScth 			retval = EFAULT;
55701e1ddd6cScth 			break;
55711e1ddd6cScth 		}
55721e1ddd6cScth 
55731e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
55741e1ddd6cScth 		    "client <%s>", s, client_path));
55751e1ddd6cScth 
55761e1ddd6cScth 		/* Get number of paths to this client device */
55771e1ddd6cScth 		if ((cdip = mdi_client_path2devinfo(vdip, client_path))
55781e1ddd6cScth 		    == NULL) {
55791e1ddd6cScth 			retval = ENXIO;
55801e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
55811e1ddd6cScth 			    "client dip doesn't exist. invalid path <%s>",
55821e1ddd6cScth 			    s, client_path));
55831e1ddd6cScth 			break;
55841e1ddd6cScth 		}
55851e1ddd6cScth 		num_paths = mdi_client_get_path_count(cdip);
55861e1ddd6cScth 
55871e1ddd6cScth 		if (ddi_copyout(&num_paths, pioc->ret_elem,
55881e1ddd6cScth 		    sizeof (num_paths), mode)) {
55891e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
55901e1ddd6cScth 			    "num_paths copyout failed", s));
55911e1ddd6cScth 			retval = EFAULT;
55921e1ddd6cScth 			break;
55931e1ddd6cScth 		}
55941e1ddd6cScth 
55951e1ddd6cScth 		/* If  user just wanted num_paths, then return */
55961e1ddd6cScth 		if (pioc->buf_elem == 0 || pioc->ret_buf == NULL ||
55971e1ddd6cScth 		    num_paths == 0) {
55981e1ddd6cScth 			break;
55991e1ddd6cScth 		}
56001e1ddd6cScth 
56011e1ddd6cScth 		/* Set num_paths to value as much as can be sent to userland */
56021e1ddd6cScth 		if (num_paths > pioc->buf_elem) {
56031e1ddd6cScth 			num_paths = pioc->buf_elem;
56041e1ddd6cScth 		}
56051e1ddd6cScth 
56061e1ddd6cScth 		/* Allocate memory and get userland pointers */
56071e1ddd6cScth 		if (vhci_ioc_alloc_pathinfo(&upibuf, &kpibuf, num_paths,
56081e1ddd6cScth 		    pioc, mode, s) != 0) {
56091e1ddd6cScth 			retval = EFAULT;
56101e1ddd6cScth 			break;
56111e1ddd6cScth 		}
56121e1ddd6cScth 		ASSERT(upibuf != NULL);
56131e1ddd6cScth 		ASSERT(kpibuf != NULL);
56141e1ddd6cScth 
56151e1ddd6cScth 		/*
56161e1ddd6cScth 		 * Get the path information and send it to userland.
56171e1ddd6cScth 		 */
56181e1ddd6cScth 		if (vhci_get_client_path_list(cdip, kpibuf, num_paths)
56191e1ddd6cScth 		    != MDI_SUCCESS) {
56201e1ddd6cScth 			retval = ENXIO;
56211e1ddd6cScth 			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
56221e1ddd6cScth 			break;
56231e1ddd6cScth 		}
56241e1ddd6cScth 
56251e1ddd6cScth 		if (vhci_ioc_send_pathinfo(upibuf, kpibuf, num_paths,
56261e1ddd6cScth 		    pioc, mode, s)) {
56271e1ddd6cScth 			retval = EFAULT;
56281e1ddd6cScth 			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
56291e1ddd6cScth 			break;
56301e1ddd6cScth 		}
56311e1ddd6cScth 
56321e1ddd6cScth 		/* Free the memory allocated for path information */
56331e1ddd6cScth 		vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
56341e1ddd6cScth 		break;
56351e1ddd6cScth 	}
56361e1ddd6cScth 
56371e1ddd6cScth 	case SCSI_VHCI_GET_PHCI_MULTIPATH_INFO:
56381e1ddd6cScth 	{
56391e1ddd6cScth 		uint_t		num_paths;	/* Num paths to client dev */
56401e1ddd6cScth 		sv_path_info_t	*upibuf = NULL;	/* To keep userland values */
56411e1ddd6cScth 		sv_path_info_t	*kpibuf = NULL; /* Kernel data for ioctls */
56421e1ddd6cScth 		dev_info_t	*pdip;		/* PHCI device dip */
56431e1ddd6cScth 
56441e1ddd6cScth 		if (pioc->ret_elem == NULL) {
56451e1ddd6cScth 			retval = EINVAL;
56461e1ddd6cScth 			break;
56471e1ddd6cScth 		}
56481e1ddd6cScth 
56491e1ddd6cScth 		/* Get PHCI device path from user land */
56501e1ddd6cScth 		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
56511e1ddd6cScth 			retval = EFAULT;
56521e1ddd6cScth 			break;
56531e1ddd6cScth 		}
56541e1ddd6cScth 
56551e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip,
56561e1ddd6cScth 		    "!vhci_ioctl: ioctl <%s> phci <%s>", s, phci_path));
56571e1ddd6cScth 
56581e1ddd6cScth 		/* Get number of devices associated with this PHCI device */
56591e1ddd6cScth 		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
56601e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
56611e1ddd6cScth 			    "phci dip doesn't exist. invalid path <%s>",
56621e1ddd6cScth 			    s, phci_path));
56631e1ddd6cScth 			retval = ENXIO;
56641e1ddd6cScth 			break;
56651e1ddd6cScth 		}
56661e1ddd6cScth 
56671e1ddd6cScth 		num_paths = mdi_phci_get_path_count(pdip);
56681e1ddd6cScth 
56691e1ddd6cScth 		if (ddi_copyout(&num_paths, pioc->ret_elem,
56701e1ddd6cScth 		    sizeof (num_paths), mode)) {
56711e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
56721e1ddd6cScth 			    "num_paths copyout failed", s));
56731e1ddd6cScth 			retval = EFAULT;
56741e1ddd6cScth 			break;
56751e1ddd6cScth 		}
56761e1ddd6cScth 
56771e1ddd6cScth 		/* If  user just wanted num_paths, then return */
56781e1ddd6cScth 		if (pioc->buf_elem == 0 || pioc->ret_buf == NULL ||
56791e1ddd6cScth 		    num_paths == 0) {
56801e1ddd6cScth 			break;
56811e1ddd6cScth 		}
56821e1ddd6cScth 
56831e1ddd6cScth 		/* Set num_paths to value as much as can be sent to userland */
56841e1ddd6cScth 		if (num_paths > pioc->buf_elem) {
56851e1ddd6cScth 			num_paths = pioc->buf_elem;
56861e1ddd6cScth 		}
56871e1ddd6cScth 
56881e1ddd6cScth 		/* Allocate memory and get userland pointers */
56891e1ddd6cScth 		if (vhci_ioc_alloc_pathinfo(&upibuf, &kpibuf, num_paths,
56901e1ddd6cScth 		    pioc, mode, s) != 0) {
56911e1ddd6cScth 			retval = EFAULT;
56921e1ddd6cScth 			break;
56931e1ddd6cScth 		}
56941e1ddd6cScth 		ASSERT(upibuf != NULL);
56951e1ddd6cScth 		ASSERT(kpibuf != NULL);
56961e1ddd6cScth 
56971e1ddd6cScth 		/*
56981e1ddd6cScth 		 * Get the path information and send it to userland.
56991e1ddd6cScth 		 */
57001e1ddd6cScth 		if (vhci_get_phci_path_list(pdip, kpibuf, num_paths)
57011e1ddd6cScth 		    != MDI_SUCCESS) {
57021e1ddd6cScth 			retval = ENXIO;
57031e1ddd6cScth 			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
57041e1ddd6cScth 			break;
57051e1ddd6cScth 		}
57061e1ddd6cScth 
57071e1ddd6cScth 		if (vhci_ioc_send_pathinfo(upibuf, kpibuf, num_paths,
57081e1ddd6cScth 		    pioc, mode, s)) {
57091e1ddd6cScth 			retval = EFAULT;
57101e1ddd6cScth 			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
57111e1ddd6cScth 			break;
57121e1ddd6cScth 		}
57131e1ddd6cScth 
57141e1ddd6cScth 		/* Free the memory allocated for path information */
57151e1ddd6cScth 		vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
57161e1ddd6cScth 		break;
57171e1ddd6cScth 	}
57181e1ddd6cScth 
57191e1ddd6cScth 	case SCSI_VHCI_GET_CLIENT_NAME:
57201e1ddd6cScth 	{
57211e1ddd6cScth 		dev_info_t		*cdip, *pdip;
57221e1ddd6cScth 
57231e1ddd6cScth 		/* Get PHCI path and device address from user land */
57241e1ddd6cScth 		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s) ||
57251e1ddd6cScth 		    vhci_ioc_get_paddr(pioc, paddr, mode, s)) {
57261e1ddd6cScth 			retval = EFAULT;
57271e1ddd6cScth 			break;
57281e1ddd6cScth 		}
57291e1ddd6cScth 
57301e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
57311e1ddd6cScth 		    "phci <%s>, paddr <%s>", s, phci_path, paddr));
57321e1ddd6cScth 
57331e1ddd6cScth 		/* Get the PHCI dip */
57341e1ddd6cScth 		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
57351e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
57361e1ddd6cScth 			    "phci dip doesn't exist. invalid path <%s>",
57371e1ddd6cScth 			    s, phci_path));
57381e1ddd6cScth 			retval = ENXIO;
57391e1ddd6cScth 			break;
57401e1ddd6cScth 		}
57411e1ddd6cScth 
57421e1ddd6cScth 		if ((pip = mdi_pi_find(pdip, NULL, paddr)) == NULL) {
57431e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
57441e1ddd6cScth 			    "pathinfo doesn't exist. invalid device addr", s));
57451e1ddd6cScth 			retval = ENXIO;
57461e1ddd6cScth 			break;
57471e1ddd6cScth 		}
57481e1ddd6cScth 
57491e1ddd6cScth 		/* Get the client device pathname and send to userland */
57501e1ddd6cScth 		cdip = mdi_pi_get_client(pip);
57511e1ddd6cScth 		vhci_ioc_devi_to_path(cdip, client_path);
57521e1ddd6cScth 
57531e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
57541e1ddd6cScth 		    "client <%s>", s, client_path));
57551e1ddd6cScth 
57561e1ddd6cScth 		if (vhci_ioc_send_client_path(client_path, pioc, mode, s)) {
57571e1ddd6cScth 			retval = EFAULT;
57581e1ddd6cScth 			break;
57591e1ddd6cScth 		}
57601e1ddd6cScth 		break;
57611e1ddd6cScth 	}
57621e1ddd6cScth 
57631e1ddd6cScth 	case SCSI_VHCI_PATH_ONLINE:
57641e1ddd6cScth 	case SCSI_VHCI_PATH_OFFLINE:
57651e1ddd6cScth 	case SCSI_VHCI_PATH_STANDBY:
57661e1ddd6cScth 	case SCSI_VHCI_PATH_TEST:
57671e1ddd6cScth 	{
57681e1ddd6cScth 		dev_info_t		*pdip;	/* PHCI dip */
57691e1ddd6cScth 
57701e1ddd6cScth 		/* Get PHCI path and device address from user land */
57711e1ddd6cScth 		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s) ||
57721e1ddd6cScth 		    vhci_ioc_get_paddr(pioc, paddr, mode, s)) {
57731e1ddd6cScth 			retval = EFAULT;
57741e1ddd6cScth 			break;
57751e1ddd6cScth 		}
57761e1ddd6cScth 
57771e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
57781e1ddd6cScth 		    "phci <%s>, paddr <%s>", s, phci_path, paddr));
57791e1ddd6cScth 
57801e1ddd6cScth 		/* Get the PHCI dip */
57811e1ddd6cScth 		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
57821e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
57831e1ddd6cScth 			    "phci dip doesn't exist. invalid path <%s>",
57841e1ddd6cScth 			    s, phci_path));
57851e1ddd6cScth 			retval = ENXIO;
57861e1ddd6cScth 			break;
57871e1ddd6cScth 		}
57881e1ddd6cScth 
57891e1ddd6cScth 		if ((pip = mdi_pi_find(pdip, NULL, paddr)) == NULL) {
57901e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
57911e1ddd6cScth 			    "pathinfo doesn't exist. invalid device addr", s));
57921e1ddd6cScth 			retval = ENXIO;
57931e1ddd6cScth 			break;
57941e1ddd6cScth 		}
57951e1ddd6cScth 
57961e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
57971e1ddd6cScth 		    "Calling MDI function to change device state", s));
57981e1ddd6cScth 
57991e1ddd6cScth 		switch (cmd) {
58001e1ddd6cScth 		case SCSI_VHCI_PATH_ONLINE:
58011e1ddd6cScth 			retval = mdi_pi_online(pip, 0);
58021e1ddd6cScth 			break;
58031e1ddd6cScth 
58041e1ddd6cScth 		case SCSI_VHCI_PATH_OFFLINE:
58051e1ddd6cScth 			retval = mdi_pi_offline(pip, 0);
58061e1ddd6cScth 			break;
58071e1ddd6cScth 
58081e1ddd6cScth 		case SCSI_VHCI_PATH_STANDBY:
58091e1ddd6cScth 			retval = mdi_pi_standby(pip, 0);
58101e1ddd6cScth 			break;
58111e1ddd6cScth 
58121e1ddd6cScth 		case SCSI_VHCI_PATH_TEST:
58131e1ddd6cScth 			break;
58141e1ddd6cScth 		}
58151e1ddd6cScth 		break;
58161e1ddd6cScth 	}
58171e1ddd6cScth 
58181e1ddd6cScth 	case SCSI_VHCI_SWITCH_TO_CNTLR:
58191e1ddd6cScth 	{
58201e1ddd6cScth 		dev_info_t *cdip;
58211e1ddd6cScth 		struct scsi_device *devp;
58221e1ddd6cScth 
58231e1ddd6cScth 		/* Get the client device pathname */
58241e1ddd6cScth 		if (ddi_copyin(piocsc->client, client_path,
58251e1ddd6cScth 		    MAXPATHLEN, mode)) {
58261e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
58271e1ddd6cScth 			    "client_path copyin failed", s));
58281e1ddd6cScth 			retval = EFAULT;
58291e1ddd6cScth 			break;
58301e1ddd6cScth 		}
58311e1ddd6cScth 
58321e1ddd6cScth 		/* Get the path class to which user wants to switch */
58331e1ddd6cScth 		if (ddi_copyin(piocsc->class, paddr, MAXNAMELEN, mode)) {
58341e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
58351e1ddd6cScth 			    "controller_class copyin failed", s));
58361e1ddd6cScth 			retval = EFAULT;
58371e1ddd6cScth 			break;
58381e1ddd6cScth 		}
58391e1ddd6cScth 
58401e1ddd6cScth 		/* Perform validity checks */
58411e1ddd6cScth 		if ((cdip = mdi_client_path2devinfo(vdip,
58421e1ddd6cScth 		    client_path)) == NULL) {
58431e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
58441e1ddd6cScth 			    "client dip doesn't exist. invalid path <%s>",
58451e1ddd6cScth 			    s, client_path));
58461e1ddd6cScth 			retval = ENXIO;
58471e1ddd6cScth 			break;
58481e1ddd6cScth 		}
58491e1ddd6cScth 
58501e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: Calling MDI func "
58511e1ddd6cScth 		    "to switch controller"));
58521e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: client <%s> "
58531e1ddd6cScth 		    "class <%s>", client_path, paddr));
58541e1ddd6cScth 
58551e1ddd6cScth 		if (strcmp(paddr, PCLASS_PRIMARY) &&
58561e1ddd6cScth 		    strcmp(paddr, PCLASS_SECONDARY)) {
58571e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
58581e1ddd6cScth 			    "invalid path class <%s>", s, paddr));
58591e1ddd6cScth 			retval = ENXIO;
58601e1ddd6cScth 			break;
58611e1ddd6cScth 		}
58621e1ddd6cScth 
58631e1ddd6cScth 		devp = ddi_get_driver_private(cdip);
58641e1ddd6cScth 		if (devp == NULL) {
58651e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
58661e1ddd6cScth 			    "invalid scsi device <%s>", s, client_path));
58671e1ddd6cScth 			retval = ENXIO;
58681e1ddd6cScth 			break;
58691e1ddd6cScth 		}
58701e1ddd6cScth 		vlun = ADDR2VLUN(&devp->sd_address);
58711e1ddd6cScth 		ASSERT(vlun);
58721e1ddd6cScth 
58731e1ddd6cScth 		/*
58741e1ddd6cScth 		 * Checking to see if device has only one pclass, PRIMARY.
58751e1ddd6cScth 		 * If so this device doesn't support failovers.  Assumed
58761e1ddd6cScth 		 * that the devices with one pclass is PRIMARY, as thats the
58771e1ddd6cScth 		 * case today.  If this is not true and in future other
58781e1ddd6cScth 		 * symmetric devices are supported with other pclass, this
58791e1ddd6cScth 		 * IOCTL shall have to be overhauled anyways as now the only
58801e1ddd6cScth 		 * arguments it accepts are PRIMARY and SECONDARY.
58811e1ddd6cScth 		 */
58821e1ddd6cScth 		fo = vlun->svl_fops;
58830205780bSrralphs 		if (fo->sfo_pathclass_next(PCLASS_PRIMARY, &pclass,
58841e1ddd6cScth 		    vlun->svl_fops_ctpriv)) {
58851e1ddd6cScth 			retval = ENOTSUP;
58861e1ddd6cScth 			break;
58871e1ddd6cScth 		}
58881e1ddd6cScth 
58891e1ddd6cScth 		VHCI_HOLD_LUN(vlun, VH_SLEEP, held);
58901e1ddd6cScth 		mutex_enter(&vlun->svl_mutex);
58911e1ddd6cScth 		if (vlun->svl_active_pclass != NULL) {
58921e1ddd6cScth 			if (strcmp(vlun->svl_active_pclass, paddr) == 0) {
58931e1ddd6cScth 				mutex_exit(&vlun->svl_mutex);
58941e1ddd6cScth 				retval = EALREADY;
58951e1ddd6cScth 				VHCI_RELEASE_LUN(vlun);
58961e1ddd6cScth 				break;
58971e1ddd6cScth 			}
58981e1ddd6cScth 		}
58991e1ddd6cScth 		mutex_exit(&vlun->svl_mutex);
59001e1ddd6cScth 		/* Call mdi function to cause  a switch over */
59011e1ddd6cScth 		retval = mdi_failover(vdip, cdip, MDI_FAILOVER_SYNC);
59021e1ddd6cScth 		if (retval == MDI_SUCCESS) {
59031e1ddd6cScth 			retval = 0;
59041e1ddd6cScth 		} else if (retval == MDI_BUSY) {
59051e1ddd6cScth 			retval = EBUSY;
59061e1ddd6cScth 		} else {
59071e1ddd6cScth 			retval = EIO;
59081e1ddd6cScth 		}
59091e1ddd6cScth 		VHCI_RELEASE_LUN(vlun);
59101e1ddd6cScth 		break;
59111e1ddd6cScth 	}
59121e1ddd6cScth 
59131e1ddd6cScth 	case SCSI_VHCI_PATH_ENABLE:
59141e1ddd6cScth 	case SCSI_VHCI_PATH_DISABLE:
59151e1ddd6cScth 	{
59161e1ddd6cScth 		dev_info_t	*cdip, *pdip;
59171e1ddd6cScth 
59181e1ddd6cScth 		/*
59191e1ddd6cScth 		 * Get client device path from user land
59201e1ddd6cScth 		 */
59211e1ddd6cScth 		if (vhci_ioc_get_client_path(pioc, client_path, mode, s)) {
59221e1ddd6cScth 			retval = EFAULT;
59231e1ddd6cScth 			break;
59241e1ddd6cScth 		}
59251e1ddd6cScth 
59261e1ddd6cScth 		/*
59271e1ddd6cScth 		 * Get Phci device path from user land
59281e1ddd6cScth 		 */
59291e1ddd6cScth 		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
59301e1ddd6cScth 			retval = EFAULT;
59311e1ddd6cScth 			break;
59321e1ddd6cScth 		}
59331e1ddd6cScth 
59341e1ddd6cScth 		/*
59351e1ddd6cScth 		 * Get the devinfo for the Phci.
59361e1ddd6cScth 		 */
59371e1ddd6cScth 		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
59381e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
59391e1ddd6cScth 			    "phci dip doesn't exist. invalid path <%s>",
59401e1ddd6cScth 			    s, phci_path));
59411e1ddd6cScth 			retval = ENXIO;
59421e1ddd6cScth 			break;
59431e1ddd6cScth 		}
59441e1ddd6cScth 
59451e1ddd6cScth 		/*
59461e1ddd6cScth 		 * If the client path is set to /scsi_vhci then we need
59471e1ddd6cScth 		 * to do the operation on all the clients so set cdip to NULL.
59481e1ddd6cScth 		 * Else, try to get the client dip.
59491e1ddd6cScth 		 */
59501e1ddd6cScth 		if (strcmp(client_path, "/scsi_vhci") == 0) {
59511e1ddd6cScth 			cdip = NULL;
59521e1ddd6cScth 		} else {
59531e1ddd6cScth 			if ((cdip = mdi_client_path2devinfo(vdip,
59541e1ddd6cScth 			    client_path)) == NULL) {
59551e1ddd6cScth 				retval = ENXIO;
59561e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, NULL,
59571e1ddd6cScth 				    "!vhci_ioctl: ioctl <%s> client dip "
59581e1ddd6cScth 				    "doesn't exist. invalid path <%s>",
59591e1ddd6cScth 				    s, client_path));
59601e1ddd6cScth 				break;
59611e1ddd6cScth 			}
59621e1ddd6cScth 		}
59631e1ddd6cScth 
59641e1ddd6cScth 		if (cmd == SCSI_VHCI_PATH_ENABLE)
59651e1ddd6cScth 			retval = mdi_pi_enable(cdip, pdip, USER_DISABLE);
59661e1ddd6cScth 		else
59671e1ddd6cScth 			retval = mdi_pi_disable(cdip, pdip, USER_DISABLE);
59681e1ddd6cScth 
59691e1ddd6cScth 		break;
59701e1ddd6cScth 	}
59711e1ddd6cScth 
59721e1ddd6cScth 	case SCSI_VHCI_GET_TARGET_LONGNAME:
59731e1ddd6cScth 	{
59741e1ddd6cScth 		uint_t		pid = pioc->buf_elem;
59751e1ddd6cScth 		char		*target_port;
59761e1ddd6cScth 		mod_hash_val_t	hv;
59771e1ddd6cScth 
59781e1ddd6cScth 		/* targetmap lookup of 'target-port' by <pid> */
59791e1ddd6cScth 		if (mod_hash_find(vhci_targetmap_bypid,
59801e1ddd6cScth 		    (mod_hash_key_t)(uintptr_t)pid, &hv) != 0) {
59811e1ddd6cScth 			/*
59821e1ddd6cScth 			 * NOTE: failure to find the mapping is OK for guid
59831e1ddd6cScth 			 * based 'target-port' values.
59841e1ddd6cScth 			 */
59851e1ddd6cScth 			VHCI_DEBUG(3, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
59861e1ddd6cScth 			    "targetport mapping doesn't exist: pid %d",
59871e1ddd6cScth 			    s, pid));
59881e1ddd6cScth 			retval = ENXIO;
59891e1ddd6cScth 			break;
59901e1ddd6cScth 		}
59911e1ddd6cScth 
59921e1ddd6cScth 		/* copyout 'target-port' result */
59931e1ddd6cScth 		target_port = (char *)hv;
59941e1ddd6cScth 		if (copyoutstr(target_port, pioc->addr, MAXNAMELEN, NULL)) {
59951e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
59961e1ddd6cScth 			    "targetport copyout failed: len: %d",
59971e1ddd6cScth 			    s, (int)strlen(target_port)));
59981e1ddd6cScth 			retval = EFAULT;
59991e1ddd6cScth 		}
60001e1ddd6cScth 		break;
60011e1ddd6cScth 	}
60021e1ddd6cScth 
60031e1ddd6cScth #ifdef	DEBUG
60041e1ddd6cScth 	case SCSI_VHCI_CONFIGURE_PHCI:
60051e1ddd6cScth 	{
60061e1ddd6cScth 		dev_info_t		*pdip;
60071e1ddd6cScth 
60081e1ddd6cScth 		/* Get PHCI path and device address from user land */
60091e1ddd6cScth 		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
60101e1ddd6cScth 			retval = EFAULT;
60111e1ddd6cScth 			break;
60121e1ddd6cScth 		}
60131e1ddd6cScth 
60141e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
60151e1ddd6cScth 		    "phci <%s>", s, phci_path));
60161e1ddd6cScth 
60171e1ddd6cScth 		/* Get the PHCI dip */
60181e1ddd6cScth 		if ((pdip = e_ddi_hold_devi_by_path(phci_path, 0)) == NULL) {
60191e1ddd6cScth 			VHCI_DEBUG(3, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
60201e1ddd6cScth 			    "phci dip doesn't exist. invalid path <%s>",
60211e1ddd6cScth 			    s, phci_path));
60221e1ddd6cScth 			retval = ENXIO;
60231e1ddd6cScth 			break;
60241e1ddd6cScth 		}
60251e1ddd6cScth 
60261e1ddd6cScth 		if (ndi_devi_config(pdip,
60271e1ddd6cScth 		    NDI_DEVFS_CLEAN | NDI_DEVI_PERSIST) != NDI_SUCCESS) {
60281e1ddd6cScth 			retval = EIO;
60291e1ddd6cScth 		}
60301e1ddd6cScth 
60311e1ddd6cScth 		ddi_release_devi(pdip);
60321e1ddd6cScth 		break;
60331e1ddd6cScth 	}
60341e1ddd6cScth 
60351e1ddd6cScth 	case SCSI_VHCI_UNCONFIGURE_PHCI:
60361e1ddd6cScth 	{
60371e1ddd6cScth 		dev_info_t		*pdip;
60381e1ddd6cScth 
60391e1ddd6cScth 		/* Get PHCI path and device address from user land */
60401e1ddd6cScth 		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
60411e1ddd6cScth 			retval = EFAULT;
60421e1ddd6cScth 			break;
60431e1ddd6cScth 		}
60441e1ddd6cScth 
60451e1ddd6cScth 		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
60461e1ddd6cScth 		    "phci <%s>", s, phci_path));
60471e1ddd6cScth 
60481e1ddd6cScth 		/* Get the PHCI dip */
60491e1ddd6cScth 		if ((pdip = e_ddi_hold_devi_by_path(phci_path, 0)) == NULL) {
60501e1ddd6cScth 			VHCI_DEBUG(3, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
60511e1ddd6cScth 			    "phci dip doesn't exist. invalid path <%s>",
60521e1ddd6cScth 			    s, phci_path));
60531e1ddd6cScth 			retval = ENXIO;
60541e1ddd6cScth 			break;
60551e1ddd6cScth 		}
60561e1ddd6cScth 
60571e1ddd6cScth 		if (ndi_devi_unconfig(pdip,
60581e1ddd6cScth 		    NDI_DEVI_REMOVE | NDI_DEVFS_CLEAN) != NDI_SUCCESS) {
60591e1ddd6cScth 			retval = EBUSY;
60601e1ddd6cScth 		}
60611e1ddd6cScth 
60621e1ddd6cScth 		ddi_release_devi(pdip);
60631e1ddd6cScth 		break;
60641e1ddd6cScth 	}
60651e1ddd6cScth #endif
60661e1ddd6cScth 	}
60671e1ddd6cScth 
60681e1ddd6cScth end:
60691e1ddd6cScth 	/* Free the memory allocated above */
60701e1ddd6cScth 	if (phci_path != NULL) {
60711e1ddd6cScth 		kmem_free(phci_path, MAXPATHLEN);
60721e1ddd6cScth 	}
60731e1ddd6cScth 	if (client_path != NULL) {
60741e1ddd6cScth 		kmem_free(client_path, MAXPATHLEN);
60751e1ddd6cScth 	}
60761e1ddd6cScth 	if (paddr != NULL) {
60771e1ddd6cScth 		kmem_free(paddr, MAXNAMELEN);
60781e1ddd6cScth 	}
60791e1ddd6cScth 	return (retval);
60801e1ddd6cScth }
60811e1ddd6cScth 
60821e1ddd6cScth /*
60831e1ddd6cScth  * devctl IOCTL support for client device DR
60841e1ddd6cScth  */
60851e1ddd6cScth /* ARGSUSED */
60861e1ddd6cScth int
vhci_devctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)60871e1ddd6cScth vhci_devctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
60881e1ddd6cScth     int *rvalp)
60891e1ddd6cScth {
60901e1ddd6cScth 	dev_info_t *self;
60911e1ddd6cScth 	dev_info_t *child;
60921e1ddd6cScth 	scsi_hba_tran_t *hba;
60931e1ddd6cScth 	struct devctl_iocdata *dcp;
60941e1ddd6cScth 	struct scsi_vhci *vhci;
60951e1ddd6cScth 	int rv = 0;
60961e1ddd6cScth 	int retval = 0;
60971e1ddd6cScth 	scsi_vhci_priv_t *svp;
60981e1ddd6cScth 	mdi_pathinfo_t  *pip;
60991e1ddd6cScth 
61001e1ddd6cScth 	if ((vhci = ddi_get_soft_state(vhci_softstate,
61011e1ddd6cScth 	    MINOR2INST(getminor(dev)))) == NULL)
61021e1ddd6cScth 		return (ENXIO);
61031e1ddd6cScth 
61041e1ddd6cScth 	/*
61051e1ddd6cScth 	 * check if :devctl minor device has been opened
61061e1ddd6cScth 	 */
61071e1ddd6cScth 	mutex_enter(&vhci->vhci_mutex);
61081e1ddd6cScth 	if ((vhci->vhci_state & VHCI_STATE_OPEN) == 0) {
61091e1ddd6cScth 		mutex_exit(&vhci->vhci_mutex);
61101e1ddd6cScth 		return (ENXIO);
61111e1ddd6cScth 	}
61121e1ddd6cScth 	mutex_exit(&vhci->vhci_mutex);
61131e1ddd6cScth 
61141e1ddd6cScth 	self = vhci->vhci_dip;
61151e1ddd6cScth 	hba = ddi_get_driver_private(self);
61161e1ddd6cScth 	if (hba == NULL)
61171e1ddd6cScth 		return (ENXIO);
61181e1ddd6cScth 
61191e1ddd6cScth 	/*
61201e1ddd6cScth 	 * We can use the generic implementation for these ioctls
61211e1ddd6cScth 	 */
61221e1ddd6cScth 	switch (cmd) {
61231e1ddd6cScth 	case DEVCTL_DEVICE_GETSTATE:
61241e1ddd6cScth 	case DEVCTL_DEVICE_ONLINE:
61251e1ddd6cScth 	case DEVCTL_DEVICE_OFFLINE:
61261e1ddd6cScth 	case DEVCTL_DEVICE_REMOVE:
61271e1ddd6cScth 	case DEVCTL_BUS_GETSTATE:
61281e1ddd6cScth 		return (ndi_devctl_ioctl(self, cmd, arg, mode, 0));
61291e1ddd6cScth 	}
61301e1ddd6cScth 
61311e1ddd6cScth 	/*
61321e1ddd6cScth 	 * read devctl ioctl data
61331e1ddd6cScth 	 */
61341e1ddd6cScth 	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
61351e1ddd6cScth 		return (EFAULT);
61361e1ddd6cScth 
61371e1ddd6cScth 	switch (cmd) {
61381e1ddd6cScth 
61391e1ddd6cScth 	case DEVCTL_DEVICE_RESET:
61401e1ddd6cScth 		/*
61411e1ddd6cScth 		 * lookup and hold child device
61421e1ddd6cScth 		 */
61431e1ddd6cScth 		if ((child = ndi_devi_find(self, ndi_dc_getname(dcp),
61441e1ddd6cScth 		    ndi_dc_getaddr(dcp))) == NULL) {
61451e1ddd6cScth 			rv = ENXIO;
61461e1ddd6cScth 			break;
61471e1ddd6cScth 		}
61481e1ddd6cScth 		retval = mdi_select_path(child, NULL,
61491e1ddd6cScth 		    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
61501e1ddd6cScth 		    NULL, &pip);
61511e1ddd6cScth 		if ((retval != MDI_SUCCESS) || (pip == NULL)) {
61521e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl:"
61531e1ddd6cScth 			    "Unable to get a path, dip 0x%p", (void *)child));
61541e1ddd6cScth 			rv = ENXIO;
61551e1ddd6cScth 			break;
61561e1ddd6cScth 		}
61571e1ddd6cScth 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
61581e1ddd6cScth 		if (vhci_recovery_reset(svp->svp_svl,
61591e1ddd6cScth 		    &svp->svp_psd->sd_address, TRUE,
61601e1ddd6cScth 		    VHCI_DEPTH_TARGET) == 0) {
61611e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
61621e1ddd6cScth 			    "!vhci_ioctl(pip:%p): "
61631e1ddd6cScth 			    "reset failed\n", (void *)pip));
61641e1ddd6cScth 			rv = ENXIO;
61651e1ddd6cScth 		}
61661e1ddd6cScth 		mdi_rele_path(pip);
61671e1ddd6cScth 		break;
61681e1ddd6cScth 
61691e1ddd6cScth 	case DEVCTL_BUS_QUIESCE:
61701e1ddd6cScth 	case DEVCTL_BUS_UNQUIESCE:
61711e1ddd6cScth 	case DEVCTL_BUS_RESET:
61721e1ddd6cScth 	case DEVCTL_BUS_RESETALL:
61731e1ddd6cScth #ifdef	DEBUG
61741e1ddd6cScth 	case DEVCTL_BUS_CONFIGURE:
61751e1ddd6cScth 	case DEVCTL_BUS_UNCONFIGURE:
61761e1ddd6cScth #endif
61771e1ddd6cScth 		rv = ENOTSUP;
61781e1ddd6cScth 		break;
61791e1ddd6cScth 
61801e1ddd6cScth 	default:
61811e1ddd6cScth 		rv = ENOTTY;
61821e1ddd6cScth 	} /* end of outer switch */
61831e1ddd6cScth 
61841e1ddd6cScth 	ndi_dc_freehdl(dcp);
61851e1ddd6cScth 	return (rv);
61861e1ddd6cScth }
61871e1ddd6cScth 
61881e1ddd6cScth /*
61891e1ddd6cScth  * Routine to get the PHCI pathname from ioctl structures in userland
61901e1ddd6cScth  */
61911e1ddd6cScth /* ARGSUSED */
61921e1ddd6cScth static int
vhci_ioc_get_phci_path(sv_iocdata_t * pioc,caddr_t phci_path,int mode,caddr_t s)61931e1ddd6cScth vhci_ioc_get_phci_path(sv_iocdata_t *pioc, caddr_t phci_path,
61941e1ddd6cScth     int mode, caddr_t s)
61951e1ddd6cScth {
61961e1ddd6cScth 	int retval = 0;
61971e1ddd6cScth 
61981e1ddd6cScth 	if (ddi_copyin(pioc->phci, phci_path, MAXPATHLEN, mode)) {
61991e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_get_phci: ioctl <%s> "
62001e1ddd6cScth 		    "phci_path copyin failed", s));
62011e1ddd6cScth 		retval = EFAULT;
62021e1ddd6cScth 	}
62031e1ddd6cScth 	return (retval);
62041e1ddd6cScth 
62051e1ddd6cScth }
62061e1ddd6cScth 
62071e1ddd6cScth 
62081e1ddd6cScth /*
62091e1ddd6cScth  * Routine to get the Client device pathname from ioctl structures in userland
62101e1ddd6cScth  */
62111e1ddd6cScth /* ARGSUSED */
62121e1ddd6cScth static int
vhci_ioc_get_client_path(sv_iocdata_t * pioc,caddr_t client_path,int mode,caddr_t s)62131e1ddd6cScth vhci_ioc_get_client_path(sv_iocdata_t *pioc, caddr_t client_path,
62141e1ddd6cScth     int mode, caddr_t s)
62151e1ddd6cScth {
62161e1ddd6cScth 	int retval = 0;
62171e1ddd6cScth 
62181e1ddd6cScth 	if (ddi_copyin(pioc->client, client_path, MAXPATHLEN, mode)) {
62191e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_get_client: "
62201e1ddd6cScth 		    "ioctl <%s> client_path copyin failed", s));
62211e1ddd6cScth 		retval = EFAULT;
62221e1ddd6cScth 	}
62231e1ddd6cScth 	return (retval);
62241e1ddd6cScth }
62251e1ddd6cScth 
62261e1ddd6cScth 
62271e1ddd6cScth /*
62281e1ddd6cScth  * Routine to get physical device address from ioctl structure in userland
62291e1ddd6cScth  */
62301e1ddd6cScth /* ARGSUSED */
62311e1ddd6cScth static int
vhci_ioc_get_paddr(sv_iocdata_t * pioc,caddr_t paddr,int mode,caddr_t s)62321e1ddd6cScth vhci_ioc_get_paddr(sv_iocdata_t *pioc, caddr_t paddr, int mode, caddr_t s)
62331e1ddd6cScth {
62341e1ddd6cScth 	int retval = 0;
62351e1ddd6cScth 
62361e1ddd6cScth 	if (ddi_copyin(pioc->addr, paddr, MAXNAMELEN, mode)) {
62371e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_get_paddr: "
62381e1ddd6cScth 		    "ioctl <%s> device addr copyin failed", s));
62391e1ddd6cScth 		retval = EFAULT;
62401e1ddd6cScth 	}
62411e1ddd6cScth 	return (retval);
62421e1ddd6cScth }
62431e1ddd6cScth 
62441e1ddd6cScth 
62451e1ddd6cScth /*
62461e1ddd6cScth  * Routine to send client device pathname to userland.
62471e1ddd6cScth  */
62481e1ddd6cScth /* ARGSUSED */
62491e1ddd6cScth static int
vhci_ioc_send_client_path(caddr_t client_path,sv_iocdata_t * pioc,int mode,caddr_t s)62501e1ddd6cScth vhci_ioc_send_client_path(caddr_t client_path, sv_iocdata_t *pioc,
62511e1ddd6cScth     int mode, caddr_t s)
62521e1ddd6cScth {
62531e1ddd6cScth 	int retval = 0;
62541e1ddd6cScth 
62551e1ddd6cScth 	if (ddi_copyout(client_path, pioc->client, MAXPATHLEN, mode)) {
62561e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_send_client: "
62571e1ddd6cScth 		    "ioctl <%s> client_path copyout failed", s));
62581e1ddd6cScth 		retval = EFAULT;
62591e1ddd6cScth 	}
62601e1ddd6cScth 	return (retval);
62611e1ddd6cScth }
62621e1ddd6cScth 
62631e1ddd6cScth 
62641e1ddd6cScth /*
62651e1ddd6cScth  * Routine to translated dev_info pointer (dip) to device pathname.
62661e1ddd6cScth  */
62671e1ddd6cScth static void
vhci_ioc_devi_to_path(dev_info_t * dip,caddr_t path)62681e1ddd6cScth vhci_ioc_devi_to_path(dev_info_t *dip, caddr_t path)
62691e1ddd6cScth {
62701e1ddd6cScth 	(void) ddi_pathname(dip, path);
62711e1ddd6cScth }
62721e1ddd6cScth 
62731e1ddd6cScth 
62741e1ddd6cScth /*
62751e1ddd6cScth  * vhci_get_phci_path_list:
62761e1ddd6cScth  *		get information about devices associated with a
62771e1ddd6cScth  *		given PHCI device.
62781e1ddd6cScth  *
62791e1ddd6cScth  * Return Values:
62801e1ddd6cScth  *		path information elements
62811e1ddd6cScth  */
62821e1ddd6cScth int
vhci_get_phci_path_list(dev_info_t * pdip,sv_path_info_t * pibuf,uint_t num_elems)62831e1ddd6cScth vhci_get_phci_path_list(dev_info_t *pdip, sv_path_info_t *pibuf,
62841e1ddd6cScth     uint_t num_elems)
62851e1ddd6cScth {
62861e1ddd6cScth 	uint_t			count, done;
62871e1ddd6cScth 	mdi_pathinfo_t		*pip;
62881e1ddd6cScth 	sv_path_info_t		*ret_pip;
62891e1ddd6cScth 	int			status;
62901e1ddd6cScth 	size_t			prop_size;
62911e1ddd6cScth 
62921e1ddd6cScth 	/*
62931e1ddd6cScth 	 * Get the PHCI structure and retrieve the path information
62941e1ddd6cScth 	 * from the GUID hash table.
62951e1ddd6cScth 	 */
62961e1ddd6cScth 
62971e1ddd6cScth 	ret_pip = pibuf;
62981e1ddd6cScth 	count = 0;
62991e1ddd6cScth 
63003fe80ca4SDan Cross 	ndi_devi_enter(pdip);
63011e1ddd6cScth 
63021e1ddd6cScth 	done = (count >= num_elems);
63031e1ddd6cScth 	pip = mdi_get_next_client_path(pdip, NULL);
63041e1ddd6cScth 	while (pip && !done) {
63051e1ddd6cScth 		mdi_pi_lock(pip);
63061e1ddd6cScth 		(void) ddi_pathname(mdi_pi_get_phci(pip),
63071e1ddd6cScth 		    ret_pip->device.ret_phci);
63081e1ddd6cScth 		(void) strcpy(ret_pip->ret_addr, mdi_pi_get_addr(pip));
63091e1ddd6cScth 		(void) mdi_pi_get_state2(pip, &ret_pip->ret_state,
63101e1ddd6cScth 		    &ret_pip->ret_ext_state);
63111e1ddd6cScth 
63121e1ddd6cScth 		status = mdi_prop_size(pip, &prop_size);
63131e1ddd6cScth 		if (status == MDI_SUCCESS && ret_pip->ret_prop.ret_buf_size) {
63141e1ddd6cScth 			*ret_pip->ret_prop.ret_buf_size = (uint_t)prop_size;
63151e1ddd6cScth 		}
63161e1ddd6cScth 
63171e1ddd6cScth #ifdef DEBUG
63181e1ddd6cScth 		if (status != MDI_SUCCESS) {
63191e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, NULL,
63201e1ddd6cScth 			    "!vhci_get_phci_path_list: "
63211e1ddd6cScth 			    "phci <%s>, prop size failure 0x%x",
63221e1ddd6cScth 			    ret_pip->device.ret_phci, status));
63231e1ddd6cScth 		}
63241e1ddd6cScth #endif /* DEBUG */
63251e1ddd6cScth 
63261e1ddd6cScth 
63271e1ddd6cScth 		if (status == MDI_SUCCESS && ret_pip->ret_prop.buf &&
63281e1ddd6cScth 		    prop_size && ret_pip->ret_prop.buf_size >= prop_size) {
63291e1ddd6cScth 			status = mdi_prop_pack(pip,
63301e1ddd6cScth 			    &ret_pip->ret_prop.buf,
63311e1ddd6cScth 			    ret_pip->ret_prop.buf_size);
63321e1ddd6cScth 
63331e1ddd6cScth #ifdef DEBUG
63341e1ddd6cScth 			if (status != MDI_SUCCESS) {
63351e1ddd6cScth 				VHCI_DEBUG(2, (CE_WARN, NULL,
63361e1ddd6cScth 				    "!vhci_get_phci_path_list: "
63371e1ddd6cScth 				    "phci <%s>, prop pack failure 0x%x",
63381e1ddd6cScth 				    ret_pip->device.ret_phci, status));
63391e1ddd6cScth 			}
63401e1ddd6cScth #endif /* DEBUG */
63411e1ddd6cScth 		}
63421e1ddd6cScth 
63431e1ddd6cScth 		mdi_pi_unlock(pip);
63441e1ddd6cScth 		pip = mdi_get_next_client_path(pdip, pip);
63451e1ddd6cScth 		ret_pip++;
63461e1ddd6cScth 		count++;
63471e1ddd6cScth 		done = (count >= num_elems);
63481e1ddd6cScth 	}
63491e1ddd6cScth 
63503fe80ca4SDan Cross 	ndi_devi_exit(pdip);
63511e1ddd6cScth 
63521e1ddd6cScth 	return (MDI_SUCCESS);
63531e1ddd6cScth }
63541e1ddd6cScth 
63551e1ddd6cScth 
63561e1ddd6cScth /*
63571e1ddd6cScth  * vhci_get_client_path_list:
63581e1ddd6cScth  *		get information about various paths associated with a
63591e1ddd6cScth  *		given client device.
63601e1ddd6cScth  *
63611e1ddd6cScth  * Return Values:
63621e1ddd6cScth  *		path information elements
63631e1ddd6cScth  */
63641e1ddd6cScth int
vhci_get_client_path_list(dev_info_t * cdip,sv_path_info_t * pibuf,uint_t num_elems)63651e1ddd6cScth vhci_get_client_path_list(dev_info_t *cdip, sv_path_info_t *pibuf,
63661e1ddd6cScth     uint_t num_elems)
63671e1ddd6cScth {
63681e1ddd6cScth 	uint_t			count, done;
63691e1ddd6cScth 	mdi_pathinfo_t		*pip;
63701e1ddd6cScth 	sv_path_info_t		*ret_pip;
63711e1ddd6cScth 	int			status;
63721e1ddd6cScth 	size_t			prop_size;
63731e1ddd6cScth 
63741e1ddd6cScth 	ret_pip = pibuf;
63751e1ddd6cScth 	count = 0;
63761e1ddd6cScth 
63773fe80ca4SDan Cross 	ndi_devi_enter(cdip);
63781e1ddd6cScth 
63791e1ddd6cScth 	done = (count >= num_elems);
63801e1ddd6cScth 	pip = mdi_get_next_phci_path(cdip, NULL);
63811e1ddd6cScth 	while (pip && !done) {
63821e1ddd6cScth 		mdi_pi_lock(pip);
63831e1ddd6cScth 		(void) ddi_pathname(mdi_pi_get_phci(pip),
63841e1ddd6cScth 		    ret_pip->device.ret_phci);
63851e1ddd6cScth 		(void) strcpy(ret_pip->ret_addr, mdi_pi_get_addr(pip));
63861e1ddd6cScth 		(void) mdi_pi_get_state2(pip, &ret_pip->ret_state,
63871e1ddd6cScth 		    &ret_pip->ret_ext_state);
63881e1ddd6cScth 
63891e1ddd6cScth 		status = mdi_prop_size(pip, &prop_size);
63901e1ddd6cScth 		if (status == MDI_SUCCESS && ret_pip->ret_prop.ret_buf_size) {
63911e1ddd6cScth 			*ret_pip->ret_prop.ret_buf_size = (uint_t)prop_size;
63921e1ddd6cScth 		}
63931e1ddd6cScth 
63941e1ddd6cScth #ifdef DEBUG
63951e1ddd6cScth 		if (status != MDI_SUCCESS) {
63961e1ddd6cScth 			VHCI_DEBUG(2, (CE_WARN, NULL,
63971e1ddd6cScth 			    "!vhci_get_client_path_list: "
63981e1ddd6cScth 			    "phci <%s>, prop size failure 0x%x",
63991e1ddd6cScth 			    ret_pip->device.ret_phci, status));
64001e1ddd6cScth 		}
64011e1ddd6cScth #endif /* DEBUG */
64021e1ddd6cScth 
64031e1ddd6cScth 
64041e1ddd6cScth 		if (status == MDI_SUCCESS && ret_pip->ret_prop.buf &&
64051e1ddd6cScth 		    prop_size && ret_pip->ret_prop.buf_size >= prop_size) {
64061e1ddd6cScth 			status = mdi_prop_pack(pip,
64071e1ddd6cScth 			    &ret_pip->ret_prop.buf,
64081e1ddd6cScth 			    ret_pip->ret_prop.buf_size);
64091e1ddd6cScth 
64101e1ddd6cScth #ifdef DEBUG
64111e1ddd6cScth 			if (status != MDI_SUCCESS) {
64121e1ddd6cScth 				VHCI_DEBUG(2, (CE_WARN, NULL,
64131e1ddd6cScth 				    "!vhci_get_client_path_list: "
64141e1ddd6cScth 				    "phci <%s>, prop pack failure 0x%x",
64151e1ddd6cScth 				    ret_pip->device.ret_phci, status));
64161e1ddd6cScth 			}
64171e1ddd6cScth #endif /* DEBUG */
64181e1ddd6cScth 		}
64191e1ddd6cScth 
64201e1ddd6cScth 		mdi_pi_unlock(pip);
64211e1ddd6cScth 		pip = mdi_get_next_phci_path(cdip, pip);
64221e1ddd6cScth 		ret_pip++;
64231e1ddd6cScth 		count++;
64241e1ddd6cScth 		done = (count >= num_elems);
64251e1ddd6cScth 	}
64261e1ddd6cScth 
64273fe80ca4SDan Cross 	ndi_devi_exit(cdip);
64281e1ddd6cScth 
64291e1ddd6cScth 	return (MDI_SUCCESS);
64301e1ddd6cScth }
64311e1ddd6cScth 
64321e1ddd6cScth 
64331e1ddd6cScth /*
64341e1ddd6cScth  * Routine to get ioctl argument structure from userland.
64351e1ddd6cScth  */
64361e1ddd6cScth /* ARGSUSED */
64371e1ddd6cScth static int
vhci_get_iocdata(const void * data,sv_iocdata_t * pioc,int mode,caddr_t s)64381e1ddd6cScth vhci_get_iocdata(const void *data, sv_iocdata_t *pioc, int mode, caddr_t s)
64391e1ddd6cScth {
64401e1ddd6cScth 	int	retval = 0;
64411e1ddd6cScth 
64421e1ddd6cScth #ifdef  _MULTI_DATAMODEL
64431e1ddd6cScth 	switch (ddi_model_convert_from(mode & FMODELS)) {
64441e1ddd6cScth 	case DDI_MODEL_ILP32:
64451e1ddd6cScth 	{
64461e1ddd6cScth 		sv_iocdata32_t	ioc32;
64471e1ddd6cScth 
64481e1ddd6cScth 		if (ddi_copyin(data, &ioc32, sizeof (ioc32), mode)) {
64491e1ddd6cScth 			retval = EFAULT;
64501e1ddd6cScth 			break;
64511e1ddd6cScth 		}
64521e1ddd6cScth 		pioc->client	= (caddr_t)(uintptr_t)ioc32.client;
64531e1ddd6cScth 		pioc->phci	= (caddr_t)(uintptr_t)ioc32.phci;
64541e1ddd6cScth 		pioc->addr	= (caddr_t)(uintptr_t)ioc32.addr;
64551e1ddd6cScth 		pioc->buf_elem	= (uint_t)ioc32.buf_elem;
64561e1ddd6cScth 		pioc->ret_buf	= (sv_path_info_t *)(uintptr_t)ioc32.ret_buf;
64571e1ddd6cScth 		pioc->ret_elem	= (uint_t *)(uintptr_t)ioc32.ret_elem;
64581e1ddd6cScth 		break;
64591e1ddd6cScth 	}
64601e1ddd6cScth 
64611e1ddd6cScth 	case DDI_MODEL_NONE:
64621e1ddd6cScth 		if (ddi_copyin(data, pioc, sizeof (*pioc), mode)) {
64631e1ddd6cScth 			retval = EFAULT;
64641e1ddd6cScth 			break;
64651e1ddd6cScth 		}
64661e1ddd6cScth 		break;
64671e1ddd6cScth 	}
64681e1ddd6cScth #else   /* _MULTI_DATAMODEL */
64691e1ddd6cScth 	if (ddi_copyin(data, pioc, sizeof (*pioc), mode)) {
64701e1ddd6cScth 		retval = EFAULT;
64711e1ddd6cScth 	}
64721e1ddd6cScth #endif  /* _MULTI_DATAMODEL */
64731e1ddd6cScth 
64741e1ddd6cScth #ifdef DEBUG
64751e1ddd6cScth 	if (retval) {
64761e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: cmd <%s> "
64771e1ddd6cScth 		    "iocdata copyin failed", s));
64781e1ddd6cScth 	}
64791e1ddd6cScth #endif
64801e1ddd6cScth 
64811e1ddd6cScth 	return (retval);
64821e1ddd6cScth }
64831e1ddd6cScth 
64841e1ddd6cScth 
64851e1ddd6cScth /*
64861e1ddd6cScth  * Routine to get the ioctl argument for ioctl causing controller switchover.
64871e1ddd6cScth  */
64881e1ddd6cScth /* ARGSUSED */
64891e1ddd6cScth static int
vhci_get_iocswitchdata(const void * data,sv_switch_to_cntlr_iocdata_t * piocsc,int mode,caddr_t s)64901e1ddd6cScth vhci_get_iocswitchdata(const void *data, sv_switch_to_cntlr_iocdata_t *piocsc,
64911e1ddd6cScth     int mode, caddr_t s)
64921e1ddd6cScth {
64931e1ddd6cScth 	int	retval = 0;
64941e1ddd6cScth 
64951e1ddd6cScth #ifdef  _MULTI_DATAMODEL
64961e1ddd6cScth 	switch (ddi_model_convert_from(mode & FMODELS)) {
64971e1ddd6cScth 	case DDI_MODEL_ILP32:
64981e1ddd6cScth 	{
64991e1ddd6cScth 		sv_switch_to_cntlr_iocdata32_t	ioc32;
65001e1ddd6cScth 
65011e1ddd6cScth 		if (ddi_copyin(data, &ioc32, sizeof (ioc32), mode)) {
65021e1ddd6cScth 			retval = EFAULT;
65031e1ddd6cScth 			break;
65041e1ddd6cScth 		}
65051e1ddd6cScth 		piocsc->client	= (caddr_t)(uintptr_t)ioc32.client;
65061e1ddd6cScth 		piocsc->class	= (caddr_t)(uintptr_t)ioc32.class;
65071e1ddd6cScth 		break;
65081e1ddd6cScth 	}
65091e1ddd6cScth 
65101e1ddd6cScth 	case DDI_MODEL_NONE:
65111e1ddd6cScth 		if (ddi_copyin(data, piocsc, sizeof (*piocsc), mode)) {
65121e1ddd6cScth 			retval = EFAULT;
65131e1ddd6cScth 		}
65141e1ddd6cScth 		break;
65151e1ddd6cScth 	}
65161e1ddd6cScth #else   /* _MULTI_DATAMODEL */
65171e1ddd6cScth 	if (ddi_copyin(data, piocsc, sizeof (*piocsc), mode)) {
65181e1ddd6cScth 		retval = EFAULT;
65191e1ddd6cScth 	}
65201e1ddd6cScth #endif  /* _MULTI_DATAMODEL */
65211e1ddd6cScth 
65221e1ddd6cScth #ifdef DEBUG
65231e1ddd6cScth 	if (retval) {
65241e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: cmd <%s> "
65251e1ddd6cScth 		    "switch_to_cntlr_iocdata copyin failed", s));
65261e1ddd6cScth 	}
65271e1ddd6cScth #endif
65281e1ddd6cScth 
65291e1ddd6cScth 	return (retval);
65301e1ddd6cScth }
65311e1ddd6cScth 
65321e1ddd6cScth 
65331e1ddd6cScth /*
65341e1ddd6cScth  * Routine to allocate memory for the path information structures.
65351e1ddd6cScth  * It allocates two chunks of memory - one for keeping userland
65361e1ddd6cScth  * pointers/values for path information and path properties, second for
65371e1ddd6cScth  * keeping allocating kernel memory for path properties. These path
65381e1ddd6cScth  * properties are finally copied to userland.
65391e1ddd6cScth  */
65401e1ddd6cScth /* ARGSUSED */
65411e1ddd6cScth static int
vhci_ioc_alloc_pathinfo(sv_path_info_t ** upibuf,sv_path_info_t ** kpibuf,uint_t num_paths,sv_iocdata_t * pioc,int mode,caddr_t s)65421e1ddd6cScth vhci_ioc_alloc_pathinfo(sv_path_info_t **upibuf, sv_path_info_t **kpibuf,
65431e1ddd6cScth     uint_t num_paths, sv_iocdata_t *pioc, int mode, caddr_t s)
65441e1ddd6cScth {
65451e1ddd6cScth 	sv_path_info_t	*pi;
65461e1ddd6cScth 	uint_t		bufsize;
65471e1ddd6cScth 	int		retval = 0;
65481e1ddd6cScth 	int		index;
65491e1ddd6cScth 
65501e1ddd6cScth 	/* Allocate memory */
65511e1ddd6cScth 	*upibuf = (sv_path_info_t *)
65521e1ddd6cScth 	    kmem_zalloc(sizeof (sv_path_info_t) * num_paths, KM_SLEEP);
65531e1ddd6cScth 	ASSERT(*upibuf != NULL);
65541e1ddd6cScth 	*kpibuf = (sv_path_info_t *)
65551e1ddd6cScth 	    kmem_zalloc(sizeof (sv_path_info_t) * num_paths, KM_SLEEP);
65561e1ddd6cScth 	ASSERT(*kpibuf != NULL);
65571e1ddd6cScth 
65581e1ddd6cScth 	/*
65591e1ddd6cScth 	 * Get the path info structure from the user space.
65601e1ddd6cScth 	 * We are interested in the following fields:
65611e1ddd6cScth 	 *	- user size of buffer for per path properties.
65621e1ddd6cScth 	 *	- user address of buffer for path info properties.
65631e1ddd6cScth 	 *	- user pointer for returning actual buffer size
65641e1ddd6cScth 	 * Keep these fields in the 'upibuf' structures.
65651e1ddd6cScth 	 * Allocate buffer for per path info properties in kernel
65661e1ddd6cScth 	 * structure ('kpibuf').
65671e1ddd6cScth 	 * Size of these buffers will be equal to the size of buffers
65681e1ddd6cScth 	 * in the user space.
65691e1ddd6cScth 	 */
65701e1ddd6cScth #ifdef  _MULTI_DATAMODEL
65711e1ddd6cScth 	switch (ddi_model_convert_from(mode & FMODELS)) {
65721e1ddd6cScth 	case DDI_MODEL_ILP32:
65731e1ddd6cScth 	{
65741e1ddd6cScth 		sv_path_info32_t	*src;
65751e1ddd6cScth 		sv_path_info32_t	pi32;
65761e1ddd6cScth 
65771e1ddd6cScth 		src  = (sv_path_info32_t *)pioc->ret_buf;
65781e1ddd6cScth 		pi = (sv_path_info_t *)*upibuf;
65791e1ddd6cScth 		for (index = 0; index < num_paths; index++, src++, pi++) {
65801e1ddd6cScth 			if (ddi_copyin(src, &pi32, sizeof (pi32), mode)) {
65811e1ddd6cScth 				retval = EFAULT;
65821e1ddd6cScth 				break;
65831e1ddd6cScth 			}
65841e1ddd6cScth 
65851e1ddd6cScth 			pi->ret_prop.buf_size	=
65861e1ddd6cScth 			    (uint_t)pi32.ret_prop.buf_size;
65871e1ddd6cScth 			pi->ret_prop.ret_buf_size =
65881e1ddd6cScth 			    (uint_t *)(uintptr_t)pi32.ret_prop.ret_buf_size;
65891e1ddd6cScth 			pi->ret_prop.buf	=
65901e1ddd6cScth 			    (caddr_t)(uintptr_t)pi32.ret_prop.buf;
65911e1ddd6cScth 		}
65921e1ddd6cScth 		break;
65931e1ddd6cScth 	}
65941e1ddd6cScth 
65951e1ddd6cScth 	case DDI_MODEL_NONE:
65961e1ddd6cScth 		if (ddi_copyin(pioc->ret_buf, *upibuf,
65971e1ddd6cScth 		    sizeof (sv_path_info_t) * num_paths, mode)) {
65981e1ddd6cScth 			retval = EFAULT;
65991e1ddd6cScth 		}
66001e1ddd6cScth 		break;
66011e1ddd6cScth 	}
66021e1ddd6cScth #else   /* _MULTI_DATAMODEL */
66031e1ddd6cScth 	if (ddi_copyin(pioc->ret_buf, *upibuf,
66041e1ddd6cScth 	    sizeof (sv_path_info_t) * num_paths, mode)) {
66051e1ddd6cScth 		retval = EFAULT;
66061e1ddd6cScth 	}
66071e1ddd6cScth #endif  /* _MULTI_DATAMODEL */
66081e1ddd6cScth 
66091e1ddd6cScth 	if (retval != 0) {
66101e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_alloc_path_info: "
66111e1ddd6cScth 		    "ioctl <%s> normal: path_info copyin failed", s));
66121e1ddd6cScth 		kmem_free(*upibuf, sizeof (sv_path_info_t) * num_paths);
66131e1ddd6cScth 		kmem_free(*kpibuf, sizeof (sv_path_info_t) * num_paths);
66141e1ddd6cScth 		*upibuf = NULL;
66151e1ddd6cScth 		*kpibuf = NULL;
66161e1ddd6cScth 		return (retval);
66171e1ddd6cScth 	}
66181e1ddd6cScth 
66191e1ddd6cScth 	/*
66201e1ddd6cScth 	 * Allocate memory for per path properties.
66211e1ddd6cScth 	 */
66221e1ddd6cScth 	for (index = 0, pi = *kpibuf; index < num_paths; index++, pi++) {
66231e1ddd6cScth 		bufsize = (*upibuf)[index].ret_prop.buf_size;
66241e1ddd6cScth 
66251e1ddd6cScth 		if (bufsize && bufsize <= SV_PROP_MAX_BUF_SIZE) {
66261e1ddd6cScth 			pi->ret_prop.buf_size = bufsize;
66271e1ddd6cScth 			pi->ret_prop.buf = (caddr_t)
66281e1ddd6cScth 			    kmem_zalloc(bufsize, KM_SLEEP);
66291e1ddd6cScth 			ASSERT(pi->ret_prop.buf != NULL);
66301e1ddd6cScth 		} else {
66311e1ddd6cScth 			pi->ret_prop.buf_size = 0;
66321e1ddd6cScth 			pi->ret_prop.buf = NULL;
66331e1ddd6cScth 		}
66341e1ddd6cScth 
66351e1ddd6cScth 		if ((*upibuf)[index].ret_prop.ret_buf_size != NULL) {
66361e1ddd6cScth 			pi->ret_prop.ret_buf_size = (uint_t *)kmem_zalloc(
66371e1ddd6cScth 			    sizeof (*pi->ret_prop.ret_buf_size), KM_SLEEP);
66381e1ddd6cScth 			ASSERT(pi->ret_prop.ret_buf_size != NULL);
66391e1ddd6cScth 		} else {
66401e1ddd6cScth 			pi->ret_prop.ret_buf_size = NULL;
66411e1ddd6cScth 		}
66421e1ddd6cScth 	}
66431e1ddd6cScth 
66441e1ddd6cScth 	return (0);
66451e1ddd6cScth }
66461e1ddd6cScth 
66471e1ddd6cScth 
66481e1ddd6cScth /*
66491e1ddd6cScth  * Routine to free memory for the path information structures.
66501e1ddd6cScth  * This is the memory which was allocated earlier.
66511e1ddd6cScth  */
66521e1ddd6cScth /* ARGSUSED */
66531e1ddd6cScth static void
vhci_ioc_free_pathinfo(sv_path_info_t * upibuf,sv_path_info_t * kpibuf,uint_t num_paths)66541e1ddd6cScth vhci_ioc_free_pathinfo(sv_path_info_t *upibuf, sv_path_info_t *kpibuf,
66551e1ddd6cScth     uint_t num_paths)
66561e1ddd6cScth {
66571e1ddd6cScth 	sv_path_info_t	*pi;
66581e1ddd6cScth 	int		index;
66591e1ddd6cScth 
66601e1ddd6cScth 	/* Free memory for per path properties */
66611e1ddd6cScth 	for (index = 0, pi = kpibuf; index < num_paths; index++, pi++) {
66621e1ddd6cScth 		if (pi->ret_prop.ret_buf_size != NULL) {
66631e1ddd6cScth 			kmem_free(pi->ret_prop.ret_buf_size,
66641e1ddd6cScth 			    sizeof (*pi->ret_prop.ret_buf_size));
66651e1ddd6cScth 		}
66661e1ddd6cScth 
66671e1ddd6cScth 		if (pi->ret_prop.buf != NULL) {
66681e1ddd6cScth 			kmem_free(pi->ret_prop.buf, pi->ret_prop.buf_size);
66691e1ddd6cScth 		}
66701e1ddd6cScth 	}
66711e1ddd6cScth 
66721e1ddd6cScth 	/* Free memory for path info structures */
66731e1ddd6cScth 	kmem_free(upibuf, sizeof (sv_path_info_t) * num_paths);
66741e1ddd6cScth 	kmem_free(kpibuf, sizeof (sv_path_info_t) * num_paths);
66751e1ddd6cScth }
66761e1ddd6cScth 
66771e1ddd6cScth 
66781e1ddd6cScth /*
66791e1ddd6cScth  * Routine to copy path information and path properties to userland.
66801e1ddd6cScth  */
66811e1ddd6cScth /* ARGSUSED */
66821e1ddd6cScth static int
vhci_ioc_send_pathinfo(sv_path_info_t * upibuf,sv_path_info_t * kpibuf,uint_t num_paths,sv_iocdata_t * pioc,int mode,caddr_t s)66831e1ddd6cScth vhci_ioc_send_pathinfo(sv_path_info_t *upibuf, sv_path_info_t *kpibuf,
66841e1ddd6cScth     uint_t num_paths, sv_iocdata_t *pioc, int mode, caddr_t s)
66851e1ddd6cScth {
66861e1ddd6cScth 	int			retval = 0, index;
66871e1ddd6cScth 	sv_path_info_t		*upi_ptr;
66881e1ddd6cScth 	sv_path_info32_t	*upi32_ptr;
66891e1ddd6cScth 
66901e1ddd6cScth #ifdef  _MULTI_DATAMODEL
66911e1ddd6cScth 	switch (ddi_model_convert_from(mode & FMODELS)) {
66921e1ddd6cScth 	case DDI_MODEL_ILP32:
66931e1ddd6cScth 		goto copy_32bit;
66941e1ddd6cScth 
66951e1ddd6cScth 	case DDI_MODEL_NONE:
66961e1ddd6cScth 		goto copy_normal;
66971e1ddd6cScth 	}
66981e1ddd6cScth #else   /* _MULTI_DATAMODEL */
66991e1ddd6cScth 
67001e1ddd6cScth 	goto copy_normal;
67011e1ddd6cScth 
67021e1ddd6cScth #endif  /* _MULTI_DATAMODEL */
67031e1ddd6cScth 
67041e1ddd6cScth copy_normal:
67051e1ddd6cScth 
67061e1ddd6cScth 	/*
67071e1ddd6cScth 	 * Copy path information and path properties to user land.
67081e1ddd6cScth 	 * Pointer fields inside the path property structure were
67091e1ddd6cScth 	 * saved in the 'upibuf' structure earlier.
67101e1ddd6cScth 	 */
67111e1ddd6cScth 	upi_ptr = pioc->ret_buf;
67121e1ddd6cScth 	for (index = 0; index < num_paths; index++) {
67131e1ddd6cScth 		if (ddi_copyout(kpibuf[index].device.ret_ct,
67141e1ddd6cScth 		    upi_ptr[index].device.ret_ct, MAXPATHLEN, mode)) {
67151e1ddd6cScth 			retval = EFAULT;
67161e1ddd6cScth 			break;
67171e1ddd6cScth 		}
67181e1ddd6cScth 
67191e1ddd6cScth 		if (ddi_copyout(kpibuf[index].ret_addr,
67201e1ddd6cScth 		    upi_ptr[index].ret_addr, MAXNAMELEN, mode)) {
67211e1ddd6cScth 			retval = EFAULT;
67221e1ddd6cScth 			break;
67231e1ddd6cScth 		}
67241e1ddd6cScth 
67251e1ddd6cScth 		if (ddi_copyout(&kpibuf[index].ret_state,
67261e1ddd6cScth 		    &upi_ptr[index].ret_state, sizeof (kpibuf[index].ret_state),
67271e1ddd6cScth 		    mode)) {
67281e1ddd6cScth 			retval = EFAULT;
67291e1ddd6cScth 			break;
67301e1ddd6cScth 		}
67311e1ddd6cScth 
67321e1ddd6cScth 		if (ddi_copyout(&kpibuf[index].ret_ext_state,
67331e1ddd6cScth 		    &upi_ptr[index].ret_ext_state,
67341e1ddd6cScth 		    sizeof (kpibuf[index].ret_ext_state), mode)) {
67351e1ddd6cScth 			retval = EFAULT;
67361e1ddd6cScth 			break;
67371e1ddd6cScth 		}
67381e1ddd6cScth 
67391e1ddd6cScth 		if ((kpibuf[index].ret_prop.ret_buf_size != NULL) &&
67401e1ddd6cScth 		    ddi_copyout(kpibuf[index].ret_prop.ret_buf_size,
67411e1ddd6cScth 		    upibuf[index].ret_prop.ret_buf_size,
67421e1ddd6cScth 		    sizeof (*upibuf[index].ret_prop.ret_buf_size), mode)) {
67431e1ddd6cScth 			retval = EFAULT;
67441e1ddd6cScth 			break;
67451e1ddd6cScth 		}
67461e1ddd6cScth 
67471e1ddd6cScth 		if ((kpibuf[index].ret_prop.buf != NULL) &&
67481e1ddd6cScth 		    ddi_copyout(kpibuf[index].ret_prop.buf,
67491e1ddd6cScth 		    upibuf[index].ret_prop.buf,
67501e1ddd6cScth 		    upibuf[index].ret_prop.buf_size, mode)) {
67511e1ddd6cScth 			retval = EFAULT;
67521e1ddd6cScth 			break;
67531e1ddd6cScth 		}
67541e1ddd6cScth 	}
67551e1ddd6cScth 
67561e1ddd6cScth #ifdef DEBUG
67571e1ddd6cScth 	if (retval) {
67581e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: ioctl <%s> "
67591e1ddd6cScth 		    "normal: path_info copyout failed", s));
67601e1ddd6cScth 	}
67611e1ddd6cScth #endif
67621e1ddd6cScth 
67631e1ddd6cScth 	return (retval);
67641e1ddd6cScth 
67651e1ddd6cScth copy_32bit:
67661e1ddd6cScth 	/*
67671e1ddd6cScth 	 * Copy path information and path properties to user land.
67681e1ddd6cScth 	 * Pointer fields inside the path property structure were
67691e1ddd6cScth 	 * saved in the 'upibuf' structure earlier.
67701e1ddd6cScth 	 */
67711e1ddd6cScth 	upi32_ptr = (sv_path_info32_t *)pioc->ret_buf;
67721e1ddd6cScth 	for (index = 0; index < num_paths; index++) {
67731e1ddd6cScth 		if (ddi_copyout(kpibuf[index].device.ret_ct,
67741e1ddd6cScth 		    upi32_ptr[index].device.ret_ct, MAXPATHLEN, mode)) {
67751e1ddd6cScth 			retval = EFAULT;
67761e1ddd6cScth 			break;
67771e1ddd6cScth 		}
67781e1ddd6cScth 
67791e1ddd6cScth 		if (ddi_copyout(kpibuf[index].ret_addr,
67801e1ddd6cScth 		    upi32_ptr[index].ret_addr, MAXNAMELEN, mode)) {
67811e1ddd6cScth 			retval = EFAULT;
67821e1ddd6cScth 			break;
67831e1ddd6cScth 		}
67841e1ddd6cScth 
67851e1ddd6cScth 		if (ddi_copyout(&kpibuf[index].ret_state,
67861e1ddd6cScth 		    &upi32_ptr[index].ret_state,
67871e1ddd6cScth 		    sizeof (kpibuf[index].ret_state), mode)) {
67881e1ddd6cScth 			retval = EFAULT;
67891e1ddd6cScth 			break;
67901e1ddd6cScth 		}
67911e1ddd6cScth 
67921e1ddd6cScth 		if (ddi_copyout(&kpibuf[index].ret_ext_state,
67931e1ddd6cScth 		    &upi32_ptr[index].ret_ext_state,
67941e1ddd6cScth 		    sizeof (kpibuf[index].ret_ext_state), mode)) {
67951e1ddd6cScth 			retval = EFAULT;
67961e1ddd6cScth 			break;
67971e1ddd6cScth 		}
67981e1ddd6cScth 		if ((kpibuf[index].ret_prop.ret_buf_size != NULL) &&
67991e1ddd6cScth 		    ddi_copyout(kpibuf[index].ret_prop.ret_buf_size,
68001e1ddd6cScth 		    upibuf[index].ret_prop.ret_buf_size,
68011e1ddd6cScth 		    sizeof (*upibuf[index].ret_prop.ret_buf_size), mode)) {
68021e1ddd6cScth 			retval = EFAULT;
68031e1ddd6cScth 			break;
68041e1ddd6cScth 		}
68051e1ddd6cScth 
68061e1ddd6cScth 		if ((kpibuf[index].ret_prop.buf != NULL) &&
68071e1ddd6cScth 		    ddi_copyout(kpibuf[index].ret_prop.buf,
68081e1ddd6cScth 		    upibuf[index].ret_prop.buf,
68091e1ddd6cScth 		    upibuf[index].ret_prop.buf_size, mode)) {
68101e1ddd6cScth 			retval = EFAULT;
68111e1ddd6cScth 			break;
68121e1ddd6cScth 		}
68131e1ddd6cScth 	}
68141e1ddd6cScth 
68151e1ddd6cScth #ifdef DEBUG
68161e1ddd6cScth 	if (retval) {
68171e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: ioctl <%s> "
68181e1ddd6cScth 		    "normal: path_info copyout failed", s));
68191e1ddd6cScth 	}
68201e1ddd6cScth #endif
68211e1ddd6cScth 
68221e1ddd6cScth 	return (retval);
68231e1ddd6cScth }
68241e1ddd6cScth 
68251e1ddd6cScth 
68261e1ddd6cScth /*
68271e1ddd6cScth  * vhci_failover()
68281e1ddd6cScth  * This routine expects VHCI_HOLD_LUN before being invoked.  It can be invoked
68291e1ddd6cScth  * as MDI_FAILOVER_ASYNC or MDI_FAILOVER_SYNC.  For Asynchronous failovers
68301e1ddd6cScth  * this routine shall VHCI_RELEASE_LUN on exiting.  For synchronous failovers
68311e1ddd6cScth  * it is the callers responsibility to release lun.
68321e1ddd6cScth  */
68331e1ddd6cScth 
68341e1ddd6cScth /* ARGSUSED */
68351e1ddd6cScth static int
vhci_failover(dev_info_t * vdip,dev_info_t * cdip,int flags)68361e1ddd6cScth vhci_failover(dev_info_t *vdip, dev_info_t *cdip, int flags)
68371e1ddd6cScth {
68381e1ddd6cScth 	char			*guid;
68391e1ddd6cScth 	scsi_vhci_lun_t		*vlun = NULL;
68401e1ddd6cScth 	struct scsi_vhci	*vhci;
68411e1ddd6cScth 	mdi_pathinfo_t		*pip, *npip;
68421e1ddd6cScth 	char			*s_pclass, *pclass1, *pclass2, *pclass;
68431e1ddd6cScth 	char			active_pclass_copy[255], *active_pclass_ptr;
68441e1ddd6cScth 	char			*ptr1, *ptr2;
68451e1ddd6cScth 	mdi_pathinfo_state_t	pi_state;
68461e1ddd6cScth 	uint32_t		pi_ext_state;
68471e1ddd6cScth 	scsi_vhci_priv_t	*svp;
68481e1ddd6cScth 	struct scsi_device	*sd;
68491e1ddd6cScth 	struct scsi_failover_ops	*sfo;
68501e1ddd6cScth 	int			sps; /* mdi_select_path() status */
68511e1ddd6cScth 	int			activation_done = 0;
68521e1ddd6cScth 	int			rval, retval = MDI_FAILURE;
68531e1ddd6cScth 	int			reserve_pending, check_condition, UA_condition;
68541e1ddd6cScth 	struct scsi_pkt		*pkt;
68551e1ddd6cScth 	struct buf		*bp;
6856*28c5054dSJason King 	size_t			blksize;
68571e1ddd6cScth 
68581e1ddd6cScth 	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
68591e1ddd6cScth 	sd = ddi_get_driver_private(cdip);
68601e1ddd6cScth 	vlun = ADDR2VLUN(&sd->sd_address);
68611e1ddd6cScth 	ASSERT(vlun != 0);
68621e1ddd6cScth 	ASSERT(VHCI_LUN_IS_HELD(vlun));
68631e1ddd6cScth 	guid = vlun->svl_lun_wwn;
68641e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(1): guid %s\n", guid));
68651e1ddd6cScth 	vhci_log(CE_NOTE, vdip, "!Initiating failover for device %s "
68661e1ddd6cScth 	    "(GUID %s)", ddi_node_name(cdip), guid);
68671e1ddd6cScth 
6868*28c5054dSJason King 	blksize = vhci_get_blocksize(cdip);
6869*28c5054dSJason King 
68701e1ddd6cScth 	/*
68711e1ddd6cScth 	 * Lets maintain a local copy of the vlun->svl_active_pclass
68721e1ddd6cScth 	 * for the rest of the processing. Accessing the field
68731e1ddd6cScth 	 * directly in the loop below causes loop logic to break
68741e1ddd6cScth 	 * especially when the field gets updated by other threads
68751e1ddd6cScth 	 * update path status etc and causes 'paths are not currently
68761e1ddd6cScth 	 * available' condition to be declared prematurely.
68771e1ddd6cScth 	 */
68781e1ddd6cScth 	mutex_enter(&vlun->svl_mutex);
68791e1ddd6cScth 	if (vlun->svl_active_pclass != NULL) {
68801e1ddd6cScth 		(void) strlcpy(active_pclass_copy, vlun->svl_active_pclass,
68811e1ddd6cScth 		    sizeof (active_pclass_copy));
68821e1ddd6cScth 		active_pclass_ptr = &active_pclass_copy[0];
68831e1ddd6cScth 		mutex_exit(&vlun->svl_mutex);
68841e1ddd6cScth 		if (vhci_quiesce_paths(vdip, cdip, vlun, guid,
68851e1ddd6cScth 		    active_pclass_ptr) != 0) {
68861e1ddd6cScth 			retval = MDI_FAILURE;
68871e1ddd6cScth 		}
68881e1ddd6cScth 	} else {
68891e1ddd6cScth 		/*
68901e1ddd6cScth 		 * can happen only when the available path to device
68911e1ddd6cScth 		 * discovered is a STANDBY path.
68921e1ddd6cScth 		 */
68931e1ddd6cScth 		mutex_exit(&vlun->svl_mutex);
68941e1ddd6cScth 		active_pclass_copy[0] = '\0';
68951e1ddd6cScth 		active_pclass_ptr = NULL;
68961e1ddd6cScth 	}
68971e1ddd6cScth 
68981e1ddd6cScth 	sfo = vlun->svl_fops;
68991e1ddd6cScth 	ASSERT(sfo != NULL);
69001e1ddd6cScth 	pclass1 = s_pclass = active_pclass_ptr;
69011e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!(%s)failing over from %s\n", guid,
69021e1ddd6cScth 	    (s_pclass == NULL ? "<none>" : s_pclass)));
69031e1ddd6cScth 
69041e1ddd6cScth next_pathclass:
69051e1ddd6cScth 
69060205780bSrralphs 	rval = sfo->sfo_pathclass_next(pclass1, &pclass2,
69071e1ddd6cScth 	    vlun->svl_fops_ctpriv);
69081e1ddd6cScth 	if (rval == ENOENT) {
69091e1ddd6cScth 		if (s_pclass == NULL) {
69101e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(4)(%s): "
69111e1ddd6cScth 			    "failed, no more pathclasses\n", guid));
69121e1ddd6cScth 			goto done;
69131e1ddd6cScth 		} else {
69140205780bSrralphs 			(void) sfo->sfo_pathclass_next(NULL, &pclass2,
69151e1ddd6cScth 			    vlun->svl_fops_ctpriv);
69161e1ddd6cScth 		}
69171e1ddd6cScth 	} else if (rval == EINVAL) {
69181e1ddd6cScth 		vhci_log(CE_NOTE, vdip, "!Failover operation failed for "
69191e1ddd6cScth 		    "device %s (GUID %s): Invalid path-class %s",
69201e1ddd6cScth 		    ddi_node_name(cdip), guid,
69211e1ddd6cScth 		    ((pclass1 == NULL) ? "<none>" : pclass1));
69221e1ddd6cScth 		goto done;
69231e1ddd6cScth 	}
69241e1ddd6cScth 	if ((s_pclass != NULL) && (strcmp(pclass2, s_pclass) == 0)) {
69251e1ddd6cScth 		/*
69261e1ddd6cScth 		 * paths are not currently available
69271e1ddd6cScth 		 */
69281e1ddd6cScth 		vhci_log(CE_NOTE, vdip, "!Failover path currently unavailable"
69291e1ddd6cScth 		    " for device %s (GUID %s)",
69301e1ddd6cScth 		    ddi_node_name(cdip), guid);
69311e1ddd6cScth 		goto done;
69321e1ddd6cScth 	}
69331e1ddd6cScth 	pip = npip = NULL;
69341e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(5.2)(%s): considering "
69351e1ddd6cScth 	    "%s as failover destination\n", guid, pclass2));
69361e1ddd6cScth 	sps = mdi_select_path(cdip, NULL, MDI_SELECT_STANDBY_PATH, NULL, &npip);
69371e1ddd6cScth 	if ((npip == NULL) || (sps != MDI_SUCCESS)) {
69381e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(%s): no "
69391e1ddd6cScth 		    "STANDBY paths found (status:%x)!\n", guid, sps));
69401e1ddd6cScth 		pclass1 = pclass2;
69411e1ddd6cScth 		goto next_pathclass;
69421e1ddd6cScth 	}
69431e1ddd6cScth 	do {
69441e1ddd6cScth 		pclass = NULL;
69451e1ddd6cScth 		if ((mdi_prop_lookup_string(npip, "path-class",
69461e1ddd6cScth 		    &pclass) != MDI_SUCCESS) || (strcmp(pclass2,
69471e1ddd6cScth 		    pclass) != 0)) {
69481e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
69491e1ddd6cScth 			    "!vhci_failover(5.5)(%s): skipping path "
69501e1ddd6cScth 			    "%p(%s)...\n", guid, (void *)npip, pclass));
69511e1ddd6cScth 			pip = npip;
69521e1ddd6cScth 			sps = mdi_select_path(cdip, NULL,
69531e1ddd6cScth 			    MDI_SELECT_STANDBY_PATH, pip, &npip);
69541e1ddd6cScth 			mdi_rele_path(pip);
69551e1ddd6cScth 			(void) mdi_prop_free(pclass);
69561e1ddd6cScth 			continue;
69571e1ddd6cScth 		}
69581e1ddd6cScth 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(npip);
69591e1ddd6cScth 
69601e1ddd6cScth 		/*
69611e1ddd6cScth 		 * Issue READ at non-zer block on this STANDBY path.
69621e1ddd6cScth 		 * Purple returns
69631e1ddd6cScth 		 * 1. RESERVATION_CONFLICT if reservation is pending
69641e1ddd6cScth 		 * 2. POR check condition if it reset happened.
69651e1ddd6cScth 		 * 2. failover Check Conditions if one is already in progress.
69661e1ddd6cScth 		 */
69671e1ddd6cScth 		reserve_pending = 0;
69681e1ddd6cScth 		check_condition = 0;
69691e1ddd6cScth 		UA_condition = 0;
69701e1ddd6cScth 
69711e1ddd6cScth 		bp = scsi_alloc_consistent_buf(&svp->svp_psd->sd_address,
6972*28c5054dSJason King 		    (struct buf *)NULL, blksize, B_READ, NULL, NULL);
69731e1ddd6cScth 		if (!bp) {
69741e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
69751e1ddd6cScth 			    "vhci_failover !No resources (buf)\n"));
69761e1ddd6cScth 			mdi_rele_path(npip);
69771e1ddd6cScth 			goto done;
69781e1ddd6cScth 		}
69791e1ddd6cScth 		pkt = scsi_init_pkt(&svp->svp_psd->sd_address, NULL, bp,
69801e1ddd6cScth 		    CDB_GROUP1, sizeof (struct scsi_arq_status), 0,
69811e1ddd6cScth 		    PKT_CONSISTENT, NULL, NULL);
69821e1ddd6cScth 		if (pkt) {
69831e1ddd6cScth 			(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)
6984*28c5054dSJason King 			    pkt->pkt_cdbp, SCMD_READ_G1, 1, 1, 0);
69851e1ddd6cScth 			pkt->pkt_flags = FLAG_NOINTR;
69861e1ddd6cScth check_path_again:
6987602ca9eaScth 			pkt->pkt_path_instance = mdi_pi_get_path_instance(npip);
69881e1ddd6cScth 			pkt->pkt_time = 3 * 30;
6989602ca9eaScth 
69901e1ddd6cScth 			if (scsi_transport(pkt) == TRAN_ACCEPT) {
69911e1ddd6cScth 				switch (pkt->pkt_reason) {
69921e1ddd6cScth 				case CMD_CMPLT:
69931e1ddd6cScth 					switch (SCBP_C(pkt)) {
69941e1ddd6cScth 					case STATUS_GOOD:
69951e1ddd6cScth 						/* Already failed over */
69961e1ddd6cScth 						activation_done = 1;
69971e1ddd6cScth 						break;
69981e1ddd6cScth 					case STATUS_RESERVATION_CONFLICT:
69991e1ddd6cScth 						reserve_pending = 1;
70001e1ddd6cScth 						break;
70011e1ddd6cScth 					case STATUS_CHECK:
70021e1ddd6cScth 						check_condition = 1;
70031e1ddd6cScth 						break;
70041e1ddd6cScth 					}
70051e1ddd6cScth 				}
70061e1ddd6cScth 			}
70071e1ddd6cScth 			if (check_condition &&
70081e1ddd6cScth 			    (pkt->pkt_state & STATE_ARQ_DONE)) {
70090c45178bSwl202157@icefox 				uint8_t *sns, skey, asc, ascq;
70100c45178bSwl202157@icefox 				sns = (uint8_t *)
70111e1ddd6cScth 				    &(((struct scsi_arq_status *)(uintptr_t)
70121e1ddd6cScth 				    (pkt->pkt_scbp))->sts_sensedata);
70130c45178bSwl202157@icefox 				skey = scsi_sense_key(sns);
70140c45178bSwl202157@icefox 				asc = scsi_sense_asc(sns);
70150c45178bSwl202157@icefox 				ascq = scsi_sense_ascq(sns);
70160c45178bSwl202157@icefox 				if (skey == KEY_UNIT_ATTENTION &&
70170c45178bSwl202157@icefox 				    asc == 0x29) {
70181e1ddd6cScth 					/* Already failed over */
70191e1ddd6cScth 					VHCI_DEBUG(1, (CE_NOTE, NULL,
70201e1ddd6cScth 					    "!vhci_failover(7)(%s): "
70211e1ddd6cScth 					    "path 0x%p POR UA condition\n",
70221e1ddd6cScth 					    guid, (void *)npip));
70231e1ddd6cScth 					if (UA_condition == 0) {
70241e1ddd6cScth 						UA_condition = 1;
70251e1ddd6cScth 						goto check_path_again;
70261e1ddd6cScth 					}
70271e1ddd6cScth 				} else {
70281e1ddd6cScth 					activation_done = 0;
70291e1ddd6cScth 					VHCI_DEBUG(1, (CE_NOTE, NULL,
70301e1ddd6cScth 					    "!vhci_failover(%s): path 0x%p "
70311e1ddd6cScth 					    "unhandled chkcond %x %x %x\n",
70320c45178bSwl202157@icefox 					    guid, (void *)npip, skey,
70330c45178bSwl202157@icefox 					    asc, ascq));
70341e1ddd6cScth 				}
70351e1ddd6cScth 			}
70361e1ddd6cScth 			scsi_destroy_pkt(pkt);
70371e1ddd6cScth 		}
70381e1ddd6cScth 		scsi_free_consistent_buf(bp);
70391e1ddd6cScth 
70401e1ddd6cScth 		if (activation_done) {
70411e1ddd6cScth 			mdi_rele_path(npip);
70421e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(7)(%s): "
70431e1ddd6cScth 			    "path 0x%p already failedover\n", guid,
70441e1ddd6cScth 			    (void *)npip));
70451e1ddd6cScth 			break;
70461e1ddd6cScth 		}
70471e1ddd6cScth 		if (reserve_pending && (vlun->svl_xlf_capable == 0)) {
70481e1ddd6cScth 			(void) vhci_recovery_reset(vlun,
70491e1ddd6cScth 			    &svp->svp_psd->sd_address,
70501e1ddd6cScth 			    FALSE, VHCI_DEPTH_ALL);
70511e1ddd6cScth 		}
70521e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(6)(%s): "
70531e1ddd6cScth 		    "activating path 0x%p(psd:%p)\n", guid, (void *)npip,
70541e1ddd6cScth 		    (void *)svp->svp_psd));
70550205780bSrralphs 		if (sfo->sfo_path_activate(svp->svp_psd, pclass2,
70561e1ddd6cScth 		    vlun->svl_fops_ctpriv) == 0) {
70571e1ddd6cScth 			activation_done = 1;
70581e1ddd6cScth 			mdi_rele_path(npip);
70591e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(7)(%s): "
70601e1ddd6cScth 			    "path 0x%p successfully activated\n", guid,
70611e1ddd6cScth 			    (void *)npip));
70621e1ddd6cScth 			break;
70631e1ddd6cScth 		}
70641e1ddd6cScth 		pip = npip;
70651e1ddd6cScth 		sps = mdi_select_path(cdip, NULL, MDI_SELECT_STANDBY_PATH,
70661e1ddd6cScth 		    pip, &npip);
70671e1ddd6cScth 		mdi_rele_path(pip);
70681e1ddd6cScth 	} while ((npip != NULL) && (sps == MDI_SUCCESS));
70691e1ddd6cScth 	if (activation_done == 0) {
70701e1ddd6cScth 		pclass1 = pclass2;
70711e1ddd6cScth 		goto next_pathclass;
70721e1ddd6cScth 	}
70731e1ddd6cScth 
70741e1ddd6cScth 	/*
70751e1ddd6cScth 	 * if we are here, we have succeeded in activating path npip of
70761e1ddd6cScth 	 * pathclass pclass2; let us validate all paths of pclass2 by
70771e1ddd6cScth 	 * "ping"-ing each one and mark the good ones ONLINE
70781e1ddd6cScth 	 * Also, set the state of the paths belonging to the previously
70791e1ddd6cScth 	 * active pathclass to STANDBY
70801e1ddd6cScth 	 */
70811e1ddd6cScth 	pip = npip = NULL;
70821e1ddd6cScth 	sps = mdi_select_path(cdip, NULL, (MDI_SELECT_ONLINE_PATH |
70831e1ddd6cScth 	    MDI_SELECT_STANDBY_PATH | MDI_SELECT_USER_DISABLE_PATH),
70841e1ddd6cScth 	    NULL, &npip);
70851e1ddd6cScth 	if (npip == NULL || sps != MDI_SUCCESS) {
70861e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Failover operation failed for "
70871e1ddd6cScth 		    "device %s (GUID %s): paths may be busy\n",
70881e1ddd6cScth 		    ddi_node_name(cdip), guid));
70891e1ddd6cScth 		goto done;
70901e1ddd6cScth 	}
70911e1ddd6cScth 	do {
70921e1ddd6cScth 		(void) mdi_pi_get_state2(npip, &pi_state, &pi_ext_state);
70931e1ddd6cScth 		if (mdi_prop_lookup_string(npip, "path-class", &pclass)
70941e1ddd6cScth 		    != MDI_SUCCESS) {
70951e1ddd6cScth 			pip = npip;
70961e1ddd6cScth 			sps = mdi_select_path(cdip, NULL,
70971e1ddd6cScth 			    (MDI_SELECT_ONLINE_PATH |
70981e1ddd6cScth 			    MDI_SELECT_STANDBY_PATH |
70991e1ddd6cScth 			    MDI_SELECT_USER_DISABLE_PATH),
71001e1ddd6cScth 			    pip, &npip);
71011e1ddd6cScth 			mdi_rele_path(pip);
71021e1ddd6cScth 			continue;
71031e1ddd6cScth 		}
71041e1ddd6cScth 		if (strcmp(pclass, pclass2) == 0) {
71051e1ddd6cScth 			if (pi_state == MDI_PATHINFO_STATE_STANDBY) {
71061e1ddd6cScth 				svp = (scsi_vhci_priv_t *)
71071e1ddd6cScth 				    mdi_pi_get_vhci_private(npip);
71081e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
71091e1ddd6cScth 				    "!vhci_failover(8)(%s): "
71101e1ddd6cScth 				    "pinging path 0x%p\n",
71111e1ddd6cScth 				    guid, (void *)npip));
71120205780bSrralphs 				if (sfo->sfo_path_ping(svp->svp_psd,
71131e1ddd6cScth 				    vlun->svl_fops_ctpriv) == 1) {
71141e1ddd6cScth 					mdi_pi_set_state(npip,
71151e1ddd6cScth 					    MDI_PATHINFO_STATE_ONLINE);
71161e1ddd6cScth 					VHCI_DEBUG(1, (CE_NOTE, NULL,
71171e1ddd6cScth 					    "!vhci_failover(9)(%s): "
71181e1ddd6cScth 					    "path 0x%p ping successful, "
71191e1ddd6cScth 					    "marked online\n", guid,
71201e1ddd6cScth 					    (void *)npip));
71211e1ddd6cScth 					MDI_PI_ERRSTAT(npip, MDI_PI_FAILTO);
71221e1ddd6cScth 				}
71231e1ddd6cScth 			}
71241e1ddd6cScth 		} else if ((s_pclass != NULL) && (strcmp(pclass, s_pclass)
71251e1ddd6cScth 		    == 0)) {
71261e1ddd6cScth 			if (pi_state == MDI_PATHINFO_STATE_ONLINE) {
71271e1ddd6cScth 				mdi_pi_set_state(npip,
71281e1ddd6cScth 				    MDI_PATHINFO_STATE_STANDBY);
71291e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
71301e1ddd6cScth 				    "!vhci_failover(10)(%s): path 0x%p marked "
71311e1ddd6cScth 				    "STANDBY\n", guid, (void *)npip));
71321e1ddd6cScth 				MDI_PI_ERRSTAT(npip, MDI_PI_FAILFROM);
71331e1ddd6cScth 			}
71341e1ddd6cScth 		}
71351e1ddd6cScth 		(void) mdi_prop_free(pclass);
71361e1ddd6cScth 		pip = npip;
71371e1ddd6cScth 		sps = mdi_select_path(cdip, NULL, (MDI_SELECT_ONLINE_PATH |
71381e1ddd6cScth 		    MDI_SELECT_STANDBY_PATH | MDI_SELECT_USER_DISABLE_PATH),
71391e1ddd6cScth 		    pip, &npip);
71401e1ddd6cScth 		mdi_rele_path(pip);
71411e1ddd6cScth 	} while ((npip != NULL) && (sps == MDI_SUCCESS));
71421e1ddd6cScth 
71431e1ddd6cScth 	/*
71441e1ddd6cScth 	 * Update the AccessState of related MP-API TPGs
71451e1ddd6cScth 	 */
71461e1ddd6cScth 	(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci, vlun);
71471e1ddd6cScth 
71481e1ddd6cScth 	vhci_log(CE_NOTE, vdip, "!Failover operation completed successfully "
71491e1ddd6cScth 	    "for device %s (GUID %s): failed over from %s to %s",
71501e1ddd6cScth 	    ddi_node_name(cdip), guid, ((s_pclass == NULL) ? "<none>" :
71511e1ddd6cScth 	    s_pclass), pclass2);
71521e1ddd6cScth 	ptr1 = kmem_alloc(strlen(pclass2) + 1, KM_SLEEP);
71531e1ddd6cScth 	(void) strlcpy(ptr1, pclass2, (strlen(pclass2) + 1));
71541e1ddd6cScth 	mutex_enter(&vlun->svl_mutex);
71551e1ddd6cScth 	ptr2 = vlun->svl_active_pclass;
71561e1ddd6cScth 	vlun->svl_active_pclass = ptr1;
71571e1ddd6cScth 	mutex_exit(&vlun->svl_mutex);
71581e1ddd6cScth 	if (ptr2) {
71591e1ddd6cScth 		kmem_free(ptr2, strlen(ptr2) + 1);
71601e1ddd6cScth 	}
71611e1ddd6cScth 	mutex_enter(&vhci->vhci_mutex);
71621e1ddd6cScth 	scsi_hba_reset_notify_callback(&vhci->vhci_mutex,
71631e1ddd6cScth 	    &vhci->vhci_reset_notify_listf);
71641e1ddd6cScth 	/* All reservations are cleared upon these resets. */
71651e1ddd6cScth 	vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
71661e1ddd6cScth 	mutex_exit(&vhci->vhci_mutex);
71671e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(11): DONE! Active "
71681e1ddd6cScth 	    "pathclass for %s is now %s\n", guid, pclass2));
71691e1ddd6cScth 	retval = MDI_SUCCESS;
71701e1ddd6cScth 
71711e1ddd6cScth done:
717240764c95SVictor Li 	vlun->svl_failover_status = retval;
71731e1ddd6cScth 	if (flags == MDI_FAILOVER_ASYNC) {
71741e1ddd6cScth 		VHCI_RELEASE_LUN(vlun);
71751e1ddd6cScth 		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_failover(12): DONE! "
71761e1ddd6cScth 		    "releasing lun, as failover was ASYNC\n"));
71771e1ddd6cScth 	} else {
71781e1ddd6cScth 		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_failover(12): DONE! "
71791e1ddd6cScth 		    "NOT releasing lun, as failover was SYNC\n"));
71801e1ddd6cScth 	}
71811e1ddd6cScth 	return (retval);
71821e1ddd6cScth }
71831e1ddd6cScth 
71841e1ddd6cScth /*
71851e1ddd6cScth  * vhci_client_attached is called after the successful attach of a
71861e1ddd6cScth  * client devinfo node.
71871e1ddd6cScth  */
71881e1ddd6cScth static void
vhci_client_attached(dev_info_t * cdip)71891e1ddd6cScth vhci_client_attached(dev_info_t *cdip)
71901e1ddd6cScth {
71911e1ddd6cScth 	mdi_pathinfo_t	*pip;
71921e1ddd6cScth 
71931e1ddd6cScth 	/*
71941e1ddd6cScth 	 * At this point the client has attached and it's instance number is
71951e1ddd6cScth 	 * valid, so we can set up kstats.  We need to do this here because it
71961e1ddd6cScth 	 * is possible for paths to go online prior to client attach, in which
71971e1ddd6cScth 	 * case the call to vhci_kstat_create_pathinfo in vhci_pathinfo_online
71981e1ddd6cScth 	 * was a noop.
71991e1ddd6cScth 	 */
72003fe80ca4SDan Cross 	ndi_devi_enter(cdip);
72011e1ddd6cScth 	for (pip = mdi_get_next_phci_path(cdip, NULL); pip;
72021e1ddd6cScth 	    pip = mdi_get_next_phci_path(cdip, pip))
72031e1ddd6cScth 		vhci_kstat_create_pathinfo(pip);
72043fe80ca4SDan Cross 	ndi_devi_exit(cdip);
72051e1ddd6cScth }
72061e1ddd6cScth 
72071e1ddd6cScth /*
72081e1ddd6cScth  * quiesce all of the online paths
72091e1ddd6cScth  */
72101e1ddd6cScth static int
vhci_quiesce_paths(dev_info_t * vdip,dev_info_t * cdip,scsi_vhci_lun_t * vlun,char * guid,char * active_pclass_ptr)72111e1ddd6cScth vhci_quiesce_paths(dev_info_t *vdip, dev_info_t *cdip, scsi_vhci_lun_t *vlun,
72121e1ddd6cScth     char *guid, char *active_pclass_ptr)
72131e1ddd6cScth {
72141e1ddd6cScth 	scsi_vhci_priv_t	*svp;
72151e1ddd6cScth 	char			*s_pclass = NULL;
72161e1ddd6cScth 	mdi_pathinfo_t		*npip, *pip;
72171e1ddd6cScth 	int			sps;
72181e1ddd6cScth 
72191e1ddd6cScth 	/* quiesce currently active paths */
72201e1ddd6cScth 	s_pclass = NULL;
72211e1ddd6cScth 	pip = npip = NULL;
72221e1ddd6cScth 	sps = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH, NULL, &npip);
72231e1ddd6cScth 	if ((npip == NULL) || (sps != MDI_SUCCESS)) {
72241e1ddd6cScth 		return (1);
72251e1ddd6cScth 	}
72261e1ddd6cScth 	do {
72271e1ddd6cScth 		if (mdi_prop_lookup_string(npip, "path-class",
72281e1ddd6cScth 		    &s_pclass) != MDI_SUCCESS) {
72291e1ddd6cScth 			mdi_rele_path(npip);
72301e1ddd6cScth 			vhci_log(CE_NOTE, vdip, "!Failover operation failed "
72311e1ddd6cScth 			    "for device %s (GUID %s) due to an internal "
72321e1ddd6cScth 			    "error", ddi_node_name(cdip), guid);
72331e1ddd6cScth 			return (1);
72341e1ddd6cScth 		}
72351e1ddd6cScth 		if (strcmp(s_pclass, active_pclass_ptr) == 0) {
72361e1ddd6cScth 			/*
72371e1ddd6cScth 			 * quiesce path. Free s_pclass since
72381e1ddd6cScth 			 * we don't need it anymore
72391e1ddd6cScth 			 */
72401e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL,
72411e1ddd6cScth 			    "!vhci_failover(2)(%s): failing over "
72421e1ddd6cScth 			    "from %s; quiescing path %p\n",
72431e1ddd6cScth 			    guid, s_pclass, (void *)npip));
72441e1ddd6cScth 			(void) mdi_prop_free(s_pclass);
72451e1ddd6cScth 			svp = (scsi_vhci_priv_t *)
72461e1ddd6cScth 			    mdi_pi_get_vhci_private(npip);
72471e1ddd6cScth 			if (svp == NULL) {
72481e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
72491e1ddd6cScth 				    "!vhci_failover(2.5)(%s): no "
72501e1ddd6cScth 				    "client priv! %p offlined?\n",
72511e1ddd6cScth 				    guid, (void *)npip));
72521e1ddd6cScth 				pip = npip;
72531e1ddd6cScth 				sps = mdi_select_path(cdip, NULL,
72541e1ddd6cScth 				    MDI_SELECT_ONLINE_PATH, pip, &npip);
72551e1ddd6cScth 				mdi_rele_path(pip);
72561e1ddd6cScth 				continue;
72571e1ddd6cScth 			}
72581e1ddd6cScth 			if (scsi_abort(&svp->svp_psd->sd_address, NULL)
72591e1ddd6cScth 			    == 0) {
72601e1ddd6cScth 				(void) vhci_recovery_reset(vlun,
72611e1ddd6cScth 				    &svp->svp_psd->sd_address, FALSE,
72621e1ddd6cScth 				    VHCI_DEPTH_TARGET);
72631e1ddd6cScth 			}
72641e1ddd6cScth 			mutex_enter(&svp->svp_mutex);
72651e1ddd6cScth 			if (svp->svp_cmds == 0) {
72661e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
72671e1ddd6cScth 				    "!vhci_failover(3)(%s):"
72681e1ddd6cScth 				    "quiesced path %p\n", guid, (void *)npip));
72691e1ddd6cScth 			} else {
72701e1ddd6cScth 				while (svp->svp_cmds != 0) {
72711e1ddd6cScth 					cv_wait(&svp->svp_cv, &svp->svp_mutex);
72721e1ddd6cScth 					VHCI_DEBUG(1, (CE_NOTE, NULL,
72731e1ddd6cScth 					    "!vhci_failover(3.cv)(%s):"
72741e1ddd6cScth 					    "quiesced path %p\n", guid,
72751e1ddd6cScth 					    (void *)npip));
72761e1ddd6cScth 				}
72771e1ddd6cScth 			}
72781e1ddd6cScth 			mutex_exit(&svp->svp_mutex);
72791e1ddd6cScth 		} else {
72801e1ddd6cScth 			/*
72811e1ddd6cScth 			 * make sure we freeup the memory
72821e1ddd6cScth 			 */
72831e1ddd6cScth 			(void) mdi_prop_free(s_pclass);
72841e1ddd6cScth 		}
72851e1ddd6cScth 		pip = npip;
72861e1ddd6cScth 		sps = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH,
72871e1ddd6cScth 		    pip, &npip);
72881e1ddd6cScth 		mdi_rele_path(pip);
72891e1ddd6cScth 	} while ((npip != NULL) && (sps == MDI_SUCCESS));
72901e1ddd6cScth 	return (0);
72911e1ddd6cScth }
72921e1ddd6cScth 
72931e1ddd6cScth static struct scsi_vhci_lun *
vhci_lun_lookup(dev_info_t * tgt_dip)72941e1ddd6cScth vhci_lun_lookup(dev_info_t *tgt_dip)
72951e1ddd6cScth {
72961e1ddd6cScth 	return ((struct scsi_vhci_lun *)
72971e1ddd6cScth 	    mdi_client_get_vhci_private(tgt_dip));
72981e1ddd6cScth }
72991e1ddd6cScth 
73001e1ddd6cScth static struct scsi_vhci_lun *
vhci_lun_lookup_alloc(dev_info_t * tgt_dip,char * guid,int * didalloc)73011e1ddd6cScth vhci_lun_lookup_alloc(dev_info_t *tgt_dip, char *guid, int *didalloc)
73021e1ddd6cScth {
73031e1ddd6cScth 	struct scsi_vhci_lun *svl;
73041e1ddd6cScth 
73051e1ddd6cScth 	if (svl = vhci_lun_lookup(tgt_dip)) {
73061e1ddd6cScth 		return (svl);
73071e1ddd6cScth 	}
73081e1ddd6cScth 
73091e1ddd6cScth 	svl = kmem_zalloc(sizeof (*svl), KM_SLEEP);
73101e1ddd6cScth 	svl->svl_lun_wwn = kmem_zalloc(strlen(guid) + 1, KM_SLEEP);
73111e1ddd6cScth 	(void) strcpy(svl->svl_lun_wwn,  guid);
73121e1ddd6cScth 	mutex_init(&svl->svl_mutex, NULL, MUTEX_DRIVER, NULL);
73131e1ddd6cScth 	cv_init(&svl->svl_cv, NULL, CV_DRIVER, NULL);
73141e1ddd6cScth 	sema_init(&svl->svl_pgr_sema, 1, NULL, SEMA_DRIVER, NULL);
73151e1ddd6cScth 	svl->svl_waiting_for_activepath = 1;
73161e1ddd6cScth 	svl->svl_sector_size = 1;
73171e1ddd6cScth 	mdi_client_set_vhci_private(tgt_dip, svl);
73181e1ddd6cScth 	*didalloc = 1;
73191e1ddd6cScth 	VHCI_DEBUG(1, (CE_NOTE, NULL,
73201e1ddd6cScth 	    "vhci_lun_lookup_alloc: guid %s vlun 0x%p\n",
73211e1ddd6cScth 	    guid, (void *)svl));
73221e1ddd6cScth 	return (svl);
73231e1ddd6cScth }
73241e1ddd6cScth 
73251e1ddd6cScth static void
vhci_lun_free(struct scsi_vhci_lun * dvlp,struct scsi_device * sd)732629c56acbSRaghuram Prahlada vhci_lun_free(struct scsi_vhci_lun *dvlp, struct scsi_device *sd)
73271e1ddd6cScth {
73281e1ddd6cScth 	char *guid;
73291e1ddd6cScth 
73301e1ddd6cScth 	guid = dvlp->svl_lun_wwn;
73311e1ddd6cScth 	ASSERT(guid != NULL);
73321e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_lun_free: %s\n", guid));
73331e1ddd6cScth 
73341e1ddd6cScth 	mutex_enter(&dvlp->svl_mutex);
73351e1ddd6cScth 	if (dvlp->svl_active_pclass != NULL) {
73361e1ddd6cScth 		kmem_free(dvlp->svl_active_pclass,
73371e1ddd6cScth 		    strlen(dvlp->svl_active_pclass) + 1);
73381e1ddd6cScth 	}
73391e1ddd6cScth 	dvlp->svl_active_pclass = NULL;
73401e1ddd6cScth 	mutex_exit(&dvlp->svl_mutex);
73411e1ddd6cScth 
73421e1ddd6cScth 	if (dvlp->svl_lun_wwn != NULL) {
73431e1ddd6cScth 		kmem_free(dvlp->svl_lun_wwn, strlen(dvlp->svl_lun_wwn) + 1);
73441e1ddd6cScth 	}
73451e1ddd6cScth 	dvlp->svl_lun_wwn = NULL;
73461e1ddd6cScth 
73471e1ddd6cScth 	if (dvlp->svl_fops_name) {
73481e1ddd6cScth 		kmem_free(dvlp->svl_fops_name, strlen(dvlp->svl_fops_name) + 1);
73491e1ddd6cScth 	}
73501e1ddd6cScth 	dvlp->svl_fops_name = NULL;
73511e1ddd6cScth 
7352f8324768Swl202157@icefox 	if (dvlp->svl_fops_ctpriv != NULL &&
7353f8324768Swl202157@icefox 	    dvlp->svl_fops != NULL) {
73547bc7346cScm136836 		dvlp->svl_fops->sfo_device_unprobe(sd, dvlp->svl_fops_ctpriv);
73557bc7346cScm136836 	}
73567bc7346cScm136836 
73571e1ddd6cScth 	if (dvlp->svl_flags & VLUN_TASK_D_ALIVE_FLG)
73581e1ddd6cScth 		taskq_destroy(dvlp->svl_taskq);
73591e1ddd6cScth 
73601e1ddd6cScth 	mutex_destroy(&dvlp->svl_mutex);
73611e1ddd6cScth 	cv_destroy(&dvlp->svl_cv);
73621e1ddd6cScth 	sema_destroy(&dvlp->svl_pgr_sema);
73631e1ddd6cScth 	kmem_free(dvlp, sizeof (*dvlp));
736482b6b7e4Sjw149990 	/*
736582b6b7e4Sjw149990 	 * vhci_lun_free may be called before the tgt_dip
736682b6b7e4Sjw149990 	 * initialization so check if the sd is NULL.
736782b6b7e4Sjw149990 	 */
736882b6b7e4Sjw149990 	if (sd != NULL)
736953a7b6b6SChris Horne 		scsi_device_hba_private_set(sd, NULL);
73701e1ddd6cScth }
73711e1ddd6cScth 
73721e1ddd6cScth int
vhci_do_scsi_cmd(struct scsi_pkt * pkt)73731e1ddd6cScth vhci_do_scsi_cmd(struct scsi_pkt *pkt)
73741e1ddd6cScth {
73751e1ddd6cScth 	int	err = 0;
73761e1ddd6cScth 	int	retry_cnt = 0;
73770c45178bSwl202157@icefox 	uint8_t	*sns, skey;
73781e1ddd6cScth 
73790205780bSrralphs #ifdef DEBUG
738082b70b7cSrralphs 	if (vhci_debug > 5) {
738182b70b7cSrralphs 		vhci_print_cdb(pkt->pkt_address.a_hba_tran->tran_hba_dip,
738282b70b7cSrralphs 		    CE_WARN, "Vhci command", pkt->pkt_cdbp);
738382b70b7cSrralphs 	}
73840205780bSrralphs #endif
73850205780bSrralphs 
73861e1ddd6cScth retry:
73871e1ddd6cScth 	err = scsi_poll(pkt);
73881e1ddd6cScth 	if (err) {
73891e1ddd6cScth 		if (pkt->pkt_cdbp[0] == SCMD_RELEASE) {
73901e1ddd6cScth 			if (SCBP_C(pkt) == STATUS_RESERVATION_CONFLICT) {
73911e1ddd6cScth 				VHCI_DEBUG(1, (CE_NOTE, NULL,
73921e1ddd6cScth 				    "!v_s_do_s_c: RELEASE conflict\n"));
73931e1ddd6cScth 				return (0);
73941e1ddd6cScth 			}
73951e1ddd6cScth 		}
739611f7d05eSChris Liu 		if (retry_cnt++ < 6) {
73971e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL,
73981e1ddd6cScth 			    "!v_s_do_s_c:retry packet 0x%p "
73991e1ddd6cScth 			    "status 0x%x reason %s",
74001e1ddd6cScth 			    (void *)pkt, SCBP_C(pkt),
74011e1ddd6cScth 			    scsi_rname(pkt->pkt_reason)));
74021e1ddd6cScth 			if ((pkt->pkt_reason == CMD_CMPLT) &&
74031e1ddd6cScth 			    (SCBP_C(pkt) == STATUS_CHECK) &&
74041e1ddd6cScth 			    (pkt->pkt_state & STATE_ARQ_DONE)) {
74050c45178bSwl202157@icefox 				sns = (uint8_t *)
74060c45178bSwl202157@icefox 				    &(((struct scsi_arq_status *)(uintptr_t)
74071e1ddd6cScth 				    (pkt->pkt_scbp))->sts_sensedata);
74080c45178bSwl202157@icefox 				skey = scsi_sense_key(sns);
74091e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, NULL,
74101e1ddd6cScth 				    "!v_s_do_s_c:retry "
74111e1ddd6cScth 				    "packet 0x%p  sense data %s", (void *)pkt,
74120c45178bSwl202157@icefox 				    scsi_sname(skey)));
74131e1ddd6cScth 			}
74141e1ddd6cScth 			goto retry;
74151e1ddd6cScth 		}
74161e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, NULL,
74171e1ddd6cScth 		    "!v_s_do_s_c: failed transport 0x%p 0x%x",
74181e1ddd6cScth 		    (void *)pkt, SCBP_C(pkt)));
74191e1ddd6cScth 		return (0);
74201e1ddd6cScth 	}
74211e1ddd6cScth 
74221e1ddd6cScth 	switch (pkt->pkt_reason) {
74231e1ddd6cScth 		case CMD_TIMEOUT:
74241e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!pkt timed "
74251e1ddd6cScth 			    "out (pkt 0x%p)", (void *)pkt));
74261e1ddd6cScth 			return (0);
74271e1ddd6cScth 		case CMD_CMPLT:
74281e1ddd6cScth 			switch (SCBP_C(pkt)) {
74291e1ddd6cScth 				case STATUS_GOOD:
74301e1ddd6cScth 					break;
74311e1ddd6cScth 				case STATUS_CHECK:
74321e1ddd6cScth 					if (pkt->pkt_state & STATE_ARQ_DONE) {
74330c45178bSwl202157@icefox 						sns = (uint8_t *)&(((
74341e1ddd6cScth 						    struct scsi_arq_status *)
74351e1ddd6cScth 						    (uintptr_t)
74361e1ddd6cScth 						    (pkt->pkt_scbp))->
74371e1ddd6cScth 						    sts_sensedata);
74380c45178bSwl202157@icefox 						skey = scsi_sense_key(sns);
74390c45178bSwl202157@icefox 						if ((skey ==
74401e1ddd6cScth 						    KEY_UNIT_ATTENTION) ||
74410c45178bSwl202157@icefox 						    (skey ==
74421e1ddd6cScth 						    KEY_NOT_READY)) {
74431e1ddd6cScth 							/*
74441e1ddd6cScth 							 * clear unit attn.
74451e1ddd6cScth 							 */
74461e1ddd6cScth 
74471e1ddd6cScth 							VHCI_DEBUG(1,
74481e1ddd6cScth 							    (CE_WARN, NULL,
74491e1ddd6cScth 							    "!v_s_do_s_c: "
74501e1ddd6cScth 							    "retry "
74511e1ddd6cScth 							    "packet 0x%p sense "
74521e1ddd6cScth 							    "data %s",
74531e1ddd6cScth 							    (void *)pkt,
74541e1ddd6cScth 							    scsi_sname
74550c45178bSwl202157@icefox 							    (skey)));
74561e1ddd6cScth 							goto retry;
74571e1ddd6cScth 						}
74581e1ddd6cScth 						VHCI_DEBUG(4, (CE_WARN, NULL,
74591e1ddd6cScth 						    "!ARQ while "
74601e1ddd6cScth 						    "transporting "
74611e1ddd6cScth 						    "(pkt 0x%p)",
74621e1ddd6cScth 						    (void *)pkt));
74631e1ddd6cScth 						return (0);
74641e1ddd6cScth 					}
74651e1ddd6cScth 					return (0);
74661e1ddd6cScth 				default:
74671e1ddd6cScth 					VHCI_DEBUG(1, (CE_WARN, NULL,
74681e1ddd6cScth 					    "!Bad status returned "
74691e1ddd6cScth 					    "(pkt 0x%p, status %x)",
74701e1ddd6cScth 					    (void *)pkt, SCBP_C(pkt)));
74711e1ddd6cScth 					return (0);
74721e1ddd6cScth 			}
74731e1ddd6cScth 			break;
74741e1ddd6cScth 		case CMD_INCOMPLETE:
74751e1ddd6cScth 		case CMD_RESET:
74761e1ddd6cScth 		case CMD_ABORTED:
74771e1ddd6cScth 		case CMD_TRAN_ERR:
74781e1ddd6cScth 			if (retry_cnt++ < 1) {
74791e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, NULL,
74801e1ddd6cScth 				    "!v_s_do_s_c: retry packet 0x%p %s",
74811e1ddd6cScth 				    (void *)pkt, scsi_rname(pkt->pkt_reason)));
74821e1ddd6cScth 				goto retry;
74831e1ddd6cScth 			}
74841e1ddd6cScth 			/* FALLTHROUGH */
74851e1ddd6cScth 		default:
74861e1ddd6cScth 			VHCI_DEBUG(1, (CE_WARN, NULL, "!pkt did not "
74871e1ddd6cScth 			    "complete successfully (pkt 0x%p,"
74881e1ddd6cScth 			    "reason %x)", (void *)pkt, pkt->pkt_reason));
74891e1ddd6cScth 			return (0);
74901e1ddd6cScth 	}
74911e1ddd6cScth 	return (1);
74921e1ddd6cScth }
74931e1ddd6cScth 
74941e1ddd6cScth static int
vhci_quiesce_lun(struct scsi_vhci_lun * vlun)74951e1ddd6cScth vhci_quiesce_lun(struct scsi_vhci_lun *vlun)
74961e1ddd6cScth {
74971e1ddd6cScth 	mdi_pathinfo_t		*pip, *spip;
74981e1ddd6cScth 	dev_info_t		*cdip;
74991e1ddd6cScth 	struct scsi_vhci_priv	*svp;
75001e1ddd6cScth 	mdi_pathinfo_state_t	pstate;
75011e1ddd6cScth 	uint32_t		p_ext_state;
75021e1ddd6cScth 
75031e1ddd6cScth 	cdip = vlun->svl_dip;
75041e1ddd6cScth 	pip = spip = NULL;
75053fe80ca4SDan Cross 	ndi_devi_enter(cdip);
75061e1ddd6cScth 	pip = mdi_get_next_phci_path(cdip, NULL);
75071e1ddd6cScth 	while (pip != NULL) {
75081e1ddd6cScth 		(void) mdi_pi_get_state2(pip, &pstate, &p_ext_state);
75091e1ddd6cScth 		if (pstate != MDI_PATHINFO_STATE_ONLINE) {
75101e1ddd6cScth 			spip = pip;
75111e1ddd6cScth 			pip = mdi_get_next_phci_path(cdip, spip);
75121e1ddd6cScth 			continue;
75131e1ddd6cScth 		}
75141e1ddd6cScth 		mdi_hold_path(pip);
75153fe80ca4SDan Cross 		ndi_devi_exit(cdip);
75161e1ddd6cScth 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
75171e1ddd6cScth 		mutex_enter(&svp->svp_mutex);
75181e1ddd6cScth 		while (svp->svp_cmds != 0) {
7519d3d50737SRafael Vanoni 			if (cv_reltimedwait(&svp->svp_cv, &svp->svp_mutex,
7520d3d50737SRafael Vanoni 			    drv_usectohz(vhci_path_quiesce_timeout * 1000000),
7521d3d50737SRafael Vanoni 			    TR_CLOCK_TICK) == -1) {
75221e1ddd6cScth 				mutex_exit(&svp->svp_mutex);
75231e1ddd6cScth 				mdi_rele_path(pip);
75241e1ddd6cScth 				VHCI_DEBUG(1, (CE_WARN, NULL,
75251e1ddd6cScth 				    "Quiesce of lun is not successful "
75261e1ddd6cScth 				    "vlun: 0x%p.", (void *)vlun));
75271e1ddd6cScth 				return (0);
75281e1ddd6cScth 			}
75291e1ddd6cScth 		}
75301e1ddd6cScth 		mutex_exit(&svp->svp_mutex);
75313fe80ca4SDan Cross 		ndi_devi_enter(cdip);
75321e1ddd6cScth 		spip = pip;
75331e1ddd6cScth 		pip = mdi_get_next_phci_path(cdip, spip);
75341e1ddd6cScth 		mdi_rele_path(spip);
75351e1ddd6cScth 	}
75363fe80ca4SDan Cross 	ndi_devi_exit(cdip);
75371e1ddd6cScth 	return (1);
75381e1ddd6cScth }
75391e1ddd6cScth 
75401e1ddd6cScth static int
vhci_pgr_validate_and_register(scsi_vhci_priv_t * svp)75411e1ddd6cScth vhci_pgr_validate_and_register(scsi_vhci_priv_t *svp)
75421e1ddd6cScth {
75431e1ddd6cScth 	scsi_vhci_lun_t		*vlun;
75441e1ddd6cScth 	vhci_prout_t		*prout;
75451e1ddd6cScth 	int			rval, success;
75461e1ddd6cScth 	mdi_pathinfo_t		*pip, *npip;
75471e1ddd6cScth 	scsi_vhci_priv_t	*osvp;
75481e1ddd6cScth 	dev_info_t		*cdip;
75491e1ddd6cScth 	uchar_t			cdb_1;
75501e1ddd6cScth 	uchar_t			temp_res_key[MHIOC_RESV_KEY_SIZE];
75511e1ddd6cScth 
75521e1ddd6cScth 
75531e1ddd6cScth 	/*
75541e1ddd6cScth 	 * see if there are any other paths available; if none,
75551e1ddd6cScth 	 * then there is nothing to do.
75561e1ddd6cScth 	 */
75571e1ddd6cScth 	cdip = svp->svp_svl->svl_dip;
75581e1ddd6cScth 	rval = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH |
75591e1ddd6cScth 	    MDI_SELECT_STANDBY_PATH, NULL, &pip);
75601e1ddd6cScth 	if ((rval != MDI_SUCCESS) || (pip == NULL)) {
75611e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
75621e1ddd6cScth 		    "%s%d: vhci_pgr_validate_and_register: first path\n",
75631e1ddd6cScth 		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
75641e1ddd6cScth 		return (1);
75651e1ddd6cScth 	}
75661e1ddd6cScth 
75671e1ddd6cScth 	vlun = svp->svp_svl;
75681e1ddd6cScth 	prout = &vlun->svl_prout;
75691e1ddd6cScth 	ASSERT(vlun->svl_pgr_active != 0);
75701e1ddd6cScth 
75711e1ddd6cScth 	/*
75721e1ddd6cScth 	 * When the path was busy/offlined, some other host might have
75731e1ddd6cScth 	 * cleared this key. Validate key on some other path first.
75741e1ddd6cScth 	 * If it fails, return failure.
75751e1ddd6cScth 	 */
75761e1ddd6cScth 
75771e1ddd6cScth 	npip = pip;
75781e1ddd6cScth 	pip = NULL;
75791e1ddd6cScth 	success = 0;
75801e1ddd6cScth 
75811e1ddd6cScth 	/* Save the res key */
75820205780bSrralphs 	bcopy(prout->res_key, temp_res_key, MHIOC_RESV_KEY_SIZE);
75831e1ddd6cScth 
75841e1ddd6cScth 	/*
75851e1ddd6cScth 	 * Sometimes CDB from application can be a Register_And_Ignore.
75861e1ddd6cScth 	 * Instead of validation, this cdb would result in force registration.
75871e1ddd6cScth 	 * Convert it to normal cdb for validation.
75881e1ddd6cScth 	 * After that be sure to restore the cdb.
75891e1ddd6cScth 	 */
75901e1ddd6cScth 	cdb_1 = vlun->svl_cdb[1];
75911e1ddd6cScth 	vlun->svl_cdb[1] &= 0xe0;
75921e1ddd6cScth 
75931e1ddd6cScth 	do {
75940205780bSrralphs 		osvp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(npip);
75951e1ddd6cScth 		if (osvp == NULL) {
75961e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
75971e1ddd6cScth 			    "vhci_pgr_validate_and_register: no "
75981e1ddd6cScth 			    "client priv! 0x%p offlined?\n",
75991e1ddd6cScth 			    (void *)npip));
76001e1ddd6cScth 			goto next_path_1;
76011e1ddd6cScth 		}
76021e1ddd6cScth 
76031e1ddd6cScth 		if (osvp == svp) {
76041e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
76051e1ddd6cScth 			    "vhci_pgr_validate_and_register: same svp 0x%p"
76061e1ddd6cScth 			    " npip 0x%p vlun 0x%p\n",
76071e1ddd6cScth 			    (void *)svp, (void *)npip, (void *)vlun));
76081e1ddd6cScth 			goto next_path_1;
76091e1ddd6cScth 		}
76101e1ddd6cScth 
76111e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
76121e1ddd6cScth 		    "vhci_pgr_validate_and_register: First validate on"
76131e1ddd6cScth 		    " osvp 0x%p being done. vlun 0x%p thread 0x%p Before bcopy"
76141e1ddd6cScth 		    " cdb1 %x\n", (void *)osvp, (void *)vlun,
76151e1ddd6cScth 		    (void *)curthread, vlun->svl_cdb[1]));
76161e1ddd6cScth 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy:");
76171e1ddd6cScth 
76180205780bSrralphs 		bcopy(prout->service_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
76191e1ddd6cScth 
76201e1ddd6cScth 		VHCI_DEBUG(4, (CE_WARN, NULL, "vlun 0x%p After bcopy",
76211e1ddd6cScth 		    (void *)vlun));
76221e1ddd6cScth 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: after bcopy: ");
76231e1ddd6cScth 
76241e1ddd6cScth 		rval = vhci_do_prout(osvp);
76251e1ddd6cScth 		if (rval == 1) {
76261e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
76271e1ddd6cScth 			    "%s%d: vhci_pgr_validate_and_register: key"
76281e1ddd6cScth 			    " validated thread 0x%p\n", ddi_driver_name(cdip),
76291e1ddd6cScth 			    ddi_get_instance(cdip), (void *)curthread));
76301e1ddd6cScth 			pip = npip;
76311e1ddd6cScth 			success = 1;
76321e1ddd6cScth 			break;
76331e1ddd6cScth 		} else {
76341e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
76351e1ddd6cScth 			    "vhci_pgr_validate_and_register: First validation"
76361e1ddd6cScth 			    " on osvp 0x%p failed %x\n", (void *)osvp, rval));
76371e1ddd6cScth 			vhci_print_prout_keys(vlun, "v_pgr_val_reg: failed:");
76381e1ddd6cScth 		}
76391e1ddd6cScth 
76401e1ddd6cScth 		/*
76411e1ddd6cScth 		 * Try other paths
76421e1ddd6cScth 		 */
76431e1ddd6cScth next_path_1:
76441e1ddd6cScth 		pip = npip;
76451e1ddd6cScth 		rval = mdi_select_path(cdip, NULL,
76461e1ddd6cScth 		    MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH,
76471e1ddd6cScth 		    pip, &npip);
76481e1ddd6cScth 		mdi_rele_path(pip);
76491e1ddd6cScth 	} while ((rval == MDI_SUCCESS) && (npip != NULL));
76501e1ddd6cScth 
76511e1ddd6cScth 
76521e1ddd6cScth 	/* Be sure to restore original cdb */
76531e1ddd6cScth 	vlun->svl_cdb[1] = cdb_1;
76541e1ddd6cScth 
76551e1ddd6cScth 	/* Restore the res_key */
76560205780bSrralphs 	bcopy(temp_res_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
76571e1ddd6cScth 
76581e1ddd6cScth 	/*
76591e1ddd6cScth 	 * If key could not be registered on any path for the first time,
76601e1ddd6cScth 	 * return success as online should still continue.
76611e1ddd6cScth 	 */
76621e1ddd6cScth 	if (success == 0) {
76631e1ddd6cScth 		return (1);
76641e1ddd6cScth 	}
76651e1ddd6cScth 
76661e1ddd6cScth 	ASSERT(pip != NULL);
76671e1ddd6cScth 
76681e1ddd6cScth 	/*
76691e1ddd6cScth 	 * Force register on new path
76701e1ddd6cScth 	 */
76711e1ddd6cScth 	cdb_1 = vlun->svl_cdb[1];		/* store the cdb */
76721e1ddd6cScth 
76731e1ddd6cScth 	vlun->svl_cdb[1] &= 0xe0;
76741e1ddd6cScth 	vlun->svl_cdb[1] |= VHCI_PROUT_R_AND_IGNORE;
76751e1ddd6cScth 
76761e1ddd6cScth 	vhci_print_prout_keys(vlun, "v_pgr_val_reg: keys before bcopy: ");
76771e1ddd6cScth 
76780205780bSrralphs 	bcopy(prout->active_service_key, prout->service_key,
76790205780bSrralphs 	    MHIOC_RESV_KEY_SIZE);
76800205780bSrralphs 	bcopy(prout->active_res_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
76811e1ddd6cScth 
76821e1ddd6cScth 	vhci_print_prout_keys(vlun, "v_pgr_val_reg:keys after bcopy: ");
76831e1ddd6cScth 
76841e1ddd6cScth 	rval = vhci_do_prout(svp);
76851e1ddd6cScth 	vlun->svl_cdb[1] = cdb_1;		/* restore the cdb */
76861e1ddd6cScth 	if (rval != 1) {
76871e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
76881e1ddd6cScth 		    "vhci_pgr_validate_and_register: register on new"
76891e1ddd6cScth 		    " path 0x%p svp 0x%p failed %x\n",
76901e1ddd6cScth 		    (void *)pip, (void *)svp, rval));
76911e1ddd6cScth 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: reg failed: ");
76921e1ddd6cScth 		mdi_rele_path(pip);
76931e1ddd6cScth 		return (0);
76941e1ddd6cScth 	}
76951e1ddd6cScth 
76961e1ddd6cScth 	if (bcmp(prout->service_key, zero_key, MHIOC_RESV_KEY_SIZE) == 0) {
76971e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
76981e1ddd6cScth 		    "vhci_pgr_validate_and_register: zero service key\n"));
76991e1ddd6cScth 		mdi_rele_path(pip);
77001e1ddd6cScth 		return (rval);
77011e1ddd6cScth 	}
77021e1ddd6cScth 
77031e1ddd6cScth 	/*
77041e1ddd6cScth 	 * While the key was force registered, some other host might have
77051e1ddd6cScth 	 * cleared the key. Re-validate key on another pre-existing path
77061e1ddd6cScth 	 * before declaring success.
77071e1ddd6cScth 	 */
77081e1ddd6cScth 	npip = pip;
77091e1ddd6cScth 	pip = NULL;
77101e1ddd6cScth 
77111e1ddd6cScth 	/*
77121e1ddd6cScth 	 * Sometimes CDB from application can be Register and Ignore.
77131e1ddd6cScth 	 * Instead of validation, it would result in force registration.
77141e1ddd6cScth 	 * Convert it to normal cdb for validation.
77151e1ddd6cScth 	 * After that be sure to restore the cdb.
77161e1ddd6cScth 	 */
77171e1ddd6cScth 	cdb_1 = vlun->svl_cdb[1];
77181e1ddd6cScth 	vlun->svl_cdb[1] &= 0xe0;
77191e1ddd6cScth 	success = 0;
77201e1ddd6cScth 
77211e1ddd6cScth 	do {
77221e1ddd6cScth 		osvp = (scsi_vhci_priv_t *)
77231e1ddd6cScth 		    mdi_pi_get_vhci_private(npip);
77241e1ddd6cScth 		if (osvp == NULL) {
77251e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
77261e1ddd6cScth 			    "vhci_pgr_validate_and_register: no "
77271e1ddd6cScth 			    "client priv! 0x%p offlined?\n",
77281e1ddd6cScth 			    (void *)npip));
77291e1ddd6cScth 			goto next_path_2;
77301e1ddd6cScth 		}
77311e1ddd6cScth 
77321e1ddd6cScth 		if (osvp == svp) {
77331e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
77341e1ddd6cScth 			    "vhci_pgr_validate_and_register: same osvp 0x%p"
77351e1ddd6cScth 			    " npip 0x%p vlun 0x%p\n",
77361e1ddd6cScth 			    (void *)svp, (void *)npip, (void *)vlun));
77371e1ddd6cScth 			goto next_path_2;
77381e1ddd6cScth 		}
77391e1ddd6cScth 
77401e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
77411e1ddd6cScth 		    "vhci_pgr_validate_and_register: Re-validation on"
77421e1ddd6cScth 		    " osvp 0x%p being done. vlun 0x%p Before bcopy cdb1 %x\n",
77431e1ddd6cScth 		    (void *)osvp, (void *)vlun, vlun->svl_cdb[1]));
77441e1ddd6cScth 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy: ");
77451e1ddd6cScth 
77460205780bSrralphs 		bcopy(prout->service_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
77471e1ddd6cScth 
77481e1ddd6cScth 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: after bcopy: ");
77491e1ddd6cScth 
77501e1ddd6cScth 		rval = vhci_do_prout(osvp);
77511e1ddd6cScth 		if (rval == 1) {
77521e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
77531e1ddd6cScth 			    "%s%d: vhci_pgr_validate_and_register: key"
77541e1ddd6cScth 			    " validated thread 0x%p\n", ddi_driver_name(cdip),
77551e1ddd6cScth 			    ddi_get_instance(cdip), (void *)curthread));
77561e1ddd6cScth 			pip = npip;
77571e1ddd6cScth 			success = 1;
77581e1ddd6cScth 			break;
77591e1ddd6cScth 		} else {
77601e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
77611e1ddd6cScth 			    "vhci_pgr_validate_and_register: Re-validation on"
77621e1ddd6cScth 			    " osvp 0x%p failed %x\n", (void *)osvp, rval));
77631e1ddd6cScth 			vhci_print_prout_keys(vlun,
77641e1ddd6cScth 			    "v_pgr_val_reg: reval failed: ");
77651e1ddd6cScth 		}
77661e1ddd6cScth 
77671e1ddd6cScth 		/*
77681e1ddd6cScth 		 * Try other paths
77691e1ddd6cScth 		 */
77701e1ddd6cScth next_path_2:
77711e1ddd6cScth 		pip = npip;
77721e1ddd6cScth 		rval = mdi_select_path(cdip, NULL,
77731e1ddd6cScth 		    MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH,
77741e1ddd6cScth 		    pip, &npip);
77751e1ddd6cScth 		mdi_rele_path(pip);
77761e1ddd6cScth 	} while ((rval == MDI_SUCCESS) && (npip != NULL));
77771e1ddd6cScth 
77781e1ddd6cScth 	/* Be sure to restore original cdb */
77791e1ddd6cScth 	vlun->svl_cdb[1] = cdb_1;
77801e1ddd6cScth 
77811e1ddd6cScth 	if (success == 1) {
77821e1ddd6cScth 		/* Successfully validated registration */
77831e1ddd6cScth 		mdi_rele_path(pip);
77841e1ddd6cScth 		return (1);
77851e1ddd6cScth 	}
77861e1ddd6cScth 
77871e1ddd6cScth 	VHCI_DEBUG(4, (CE_WARN, NULL, "key validation failed"));
77881e1ddd6cScth 
77891e1ddd6cScth 	/*
77901e1ddd6cScth 	 * key invalid, back out by registering key value of 0
77911e1ddd6cScth 	 */
77921e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL,
77931e1ddd6cScth 	    "vhci_pgr_validate_and_register: backout on"
77941e1ddd6cScth 	    " svp 0x%p being done\n", (void *)svp));
77951e1ddd6cScth 	vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy: ");
77961e1ddd6cScth 
77970205780bSrralphs 	bcopy(prout->service_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
77980205780bSrralphs 	bzero(prout->service_key, MHIOC_RESV_KEY_SIZE);
77991e1ddd6cScth 
78001e1ddd6cScth 	vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy: ");
78011e1ddd6cScth 
78021e1ddd6cScth 	/*
78031e1ddd6cScth 	 * Get a new path
78041e1ddd6cScth 	 */
78051e1ddd6cScth 	rval = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH |
78061e1ddd6cScth 	    MDI_SELECT_STANDBY_PATH, NULL, &pip);
78071e1ddd6cScth 	if ((rval != MDI_SUCCESS) || (pip == NULL)) {
78081e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
78091e1ddd6cScth 		    "%s%d: vhci_pgr_validate_and_register: no valid pip\n",
78101e1ddd6cScth 		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
78111e1ddd6cScth 		return (0);
78121e1ddd6cScth 	}
78131e1ddd6cScth 
78141e1ddd6cScth 	if ((rval = vhci_do_prout(svp)) != 1) {
78151e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
78161e1ddd6cScth 		    "vhci_pgr_validate_and_register: backout on"
78171e1ddd6cScth 		    " svp 0x%p failed\n", (void *)svp));
78181e1ddd6cScth 		vhci_print_prout_keys(vlun, "backout failed");
78191e1ddd6cScth 
78201e1ddd6cScth 		VHCI_DEBUG(4, (CE_WARN, NULL,
78211e1ddd6cScth 		    "%s%d: vhci_pgr_validate_and_register: key"
78221e1ddd6cScth 		    " validation and backout failed", ddi_driver_name(cdip),
78231e1ddd6cScth 		    ddi_get_instance(cdip)));
78241e1ddd6cScth 		if (rval == VHCI_PGR_ILLEGALOP) {
78251e1ddd6cScth 			VHCI_DEBUG(4, (CE_WARN, NULL,
78261e1ddd6cScth 			    "%s%d: vhci_pgr_validate_and_register: key"
78271e1ddd6cScth 			    " already cleared", ddi_driver_name(cdip),
78281e1ddd6cScth 			    ddi_get_instance(cdip)));
78291e1ddd6cScth 			rval = 1;
78301e1ddd6cScth 		} else
78311e1ddd6cScth 			rval = 0;
78321e1ddd6cScth 	} else {
78331e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
78341e1ddd6cScth 		    "%s%d: vhci_pgr_validate_and_register: key"
78351e1ddd6cScth 		    " validation failed, key backed out\n",
78361e1ddd6cScth 		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
78371e1ddd6cScth 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: key backed out: ");
78381e1ddd6cScth 	}
78391e1ddd6cScth 	mdi_rele_path(pip);
78401e1ddd6cScth 
78411e1ddd6cScth 	return (rval);
78421e1ddd6cScth }
78431e1ddd6cScth 
78441e1ddd6cScth /*
78451e1ddd6cScth  * taskq routine to dispatch a scsi cmd to vhci_scsi_start.  This ensures
78461e1ddd6cScth  * that vhci_scsi_start is not called in interrupt context.
78471e1ddd6cScth  * As the upper layer gets TRAN_ACCEPT when the command is dispatched, we
78481e1ddd6cScth  * need to complete the command if something goes wrong.
78491e1ddd6cScth  */
78501e1ddd6cScth static void
vhci_dispatch_scsi_start(void * arg)78511e1ddd6cScth vhci_dispatch_scsi_start(void *arg)
78521e1ddd6cScth {
78531e1ddd6cScth 	struct vhci_pkt *vpkt	= (struct vhci_pkt *)arg;
78541e1ddd6cScth 	struct scsi_pkt *tpkt	= vpkt->vpkt_tgt_pkt;
78551e1ddd6cScth 	int rval		= TRAN_BUSY;
78561e1ddd6cScth 
78571e1ddd6cScth 	VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_dispatch_scsi_start: sending"
78581e1ddd6cScth 	    " scsi-2 reserve for 0x%p\n",
78591e1ddd6cScth 	    (void *)ADDR2DIP(&(vpkt->vpkt_tgt_pkt->pkt_address))));
78601e1ddd6cScth 
78611e1ddd6cScth 	/*
78621e1ddd6cScth 	 * To prevent the taskq from being called recursively we set the
78631e1ddd6cScth 	 * the VHCI_PKT_THRU_TASKQ bit in the vhci_pkt_states.
78641e1ddd6cScth 	 */
78651e1ddd6cScth 	vpkt->vpkt_state |= VHCI_PKT_THRU_TASKQ;
78661e1ddd6cScth 
78671e1ddd6cScth 	/*
78681e1ddd6cScth 	 * Wait for the transport to get ready to send packets
78691e1ddd6cScth 	 * and if it times out, it will return something other than
78701e1ddd6cScth 	 * TRAN_BUSY. The vhci_reserve_delay may want to
78711e1ddd6cScth 	 * get tuned for other transports and is therefore a global.
78721e1ddd6cScth 	 * Using delay since this routine is called by taskq dispatch
78731e1ddd6cScth 	 * and not called during interrupt context.
78741e1ddd6cScth 	 */
78751e1ddd6cScth 	while ((rval = vhci_scsi_start(&(vpkt->vpkt_tgt_pkt->pkt_address),
78761e1ddd6cScth 	    vpkt->vpkt_tgt_pkt)) == TRAN_BUSY) {
78771e1ddd6cScth 		delay(drv_usectohz(vhci_reserve_delay));
78781e1ddd6cScth 	}
78791e1ddd6cScth 
78801e1ddd6cScth 	switch (rval) {
78811e1ddd6cScth 	case TRAN_ACCEPT:
78821e1ddd6cScth 		return;
78831e1ddd6cScth 
78841e1ddd6cScth 	default:
78851e1ddd6cScth 		/*
78861e1ddd6cScth 		 * This pkt shall be retried, and to ensure another taskq
78871e1ddd6cScth 		 * is dispatched for it, clear the VHCI_PKT_THRU_TASKQ
78881e1ddd6cScth 		 * flag.
78891e1ddd6cScth 		 */
78901e1ddd6cScth 		vpkt->vpkt_state &= ~VHCI_PKT_THRU_TASKQ;
78911e1ddd6cScth 
78921e1ddd6cScth 		/* Ensure that the pkt is retried without a reset */
78931e1ddd6cScth 		tpkt->pkt_reason = CMD_ABORTED;
78941e1ddd6cScth 		tpkt->pkt_statistics |= STAT_ABORTED;
78951e1ddd6cScth 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_dispatch_scsi_start: "
78961e1ddd6cScth 		    "TRAN_rval %d returned for dip 0x%p", rval,
78971e1ddd6cScth 		    (void *)ADDR2DIP(&(vpkt->vpkt_tgt_pkt->pkt_address))));
78981e1ddd6cScth 		break;
78991e1ddd6cScth 	}
79001e1ddd6cScth 
79011e1ddd6cScth 	/*
79021e1ddd6cScth 	 * vpkt_org_vpkt should always be NULL here if the retry command
79031e1ddd6cScth 	 * has been successfully dispatched.  If vpkt_org_vpkt != NULL at
79041e1ddd6cScth 	 * this point, it is an error so restore the original vpkt and
79051e1ddd6cScth 	 * return an error to the target driver so it can retry the
79061e1ddd6cScth 	 * command as appropriate.
79071e1ddd6cScth 	 */
79081e1ddd6cScth 	if (vpkt->vpkt_org_vpkt != NULL) {
79091e1ddd6cScth 		struct vhci_pkt		*new_vpkt = vpkt;
79101e1ddd6cScth 		scsi_vhci_priv_t	*svp = (scsi_vhci_priv_t *)
79111e1ddd6cScth 		    mdi_pi_get_vhci_private(vpkt->vpkt_path);
79121e1ddd6cScth 
79131e1ddd6cScth 		vpkt = vpkt->vpkt_org_vpkt;
79141e1ddd6cScth 
79151e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_reason = tpkt->pkt_reason;
79161e1ddd6cScth 		vpkt->vpkt_tgt_pkt->pkt_statistics = tpkt->pkt_statistics;
79171e1ddd6cScth 
79181e1ddd6cScth 		vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
79191e1ddd6cScth 		    new_vpkt->vpkt_tgt_pkt);
79201e1ddd6cScth 
79211e1ddd6cScth 		tpkt = vpkt->vpkt_tgt_pkt;
79221e1ddd6cScth 	}
79231e1ddd6cScth 
79249c57abc8Ssrivijitha dugganapalli 	scsi_hba_pkt_comp(tpkt);
79251e1ddd6cScth }
79261e1ddd6cScth 
79271e1ddd6cScth static void
vhci_initiate_auto_failback(void * arg)79281e1ddd6cScth vhci_initiate_auto_failback(void *arg)
79291e1ddd6cScth {
79301e1ddd6cScth 	struct scsi_vhci_lun	*vlun = (struct scsi_vhci_lun *)arg;
79311e1ddd6cScth 	dev_info_t		*vdip, *cdip;
79321e1ddd6cScth 	int			held;
79331e1ddd6cScth 
79341e1ddd6cScth 	cdip = vlun->svl_dip;
79351e1ddd6cScth 	vdip = ddi_get_parent(cdip);
79361e1ddd6cScth 
79371e1ddd6cScth 	VHCI_HOLD_LUN(vlun, VH_SLEEP, held);
79381e1ddd6cScth 
79391e1ddd6cScth 	/*
79401e1ddd6cScth 	 * Perform a final check to see if the active path class is indeed
794155e592a2SRandall Ralphs 	 * not the preferred path class.  As in the time the auto failback
79421e1ddd6cScth 	 * was dispatched, an external failover could have been detected.
79431e1ddd6cScth 	 * [Some other host could have detected this condition and triggered
79441e1ddd6cScth 	 *  the auto failback before].
79451e1ddd6cScth 	 * In such a case if we go ahead with failover we will be negating the
79461e1ddd6cScth 	 * whole purpose of auto failback.
79471e1ddd6cScth 	 */
79481e1ddd6cScth 	mutex_enter(&vlun->svl_mutex);
79491e1ddd6cScth 	if (vlun->svl_active_pclass != NULL) {
79501e1ddd6cScth 		char				*best_pclass;
79511e1ddd6cScth 		struct scsi_failover_ops	*fo;
79521e1ddd6cScth 
79531e1ddd6cScth 		fo = vlun->svl_fops;
79541e1ddd6cScth 
79550205780bSrralphs 		(void) fo->sfo_pathclass_next(NULL, &best_pclass,
79561e1ddd6cScth 		    vlun->svl_fops_ctpriv);
79571e1ddd6cScth 		if (strcmp(vlun->svl_active_pclass, best_pclass) == 0) {
79581e1ddd6cScth 			mutex_exit(&vlun->svl_mutex);
79591e1ddd6cScth 			VHCI_RELEASE_LUN(vlun);
79601e1ddd6cScth 			VHCI_DEBUG(1, (CE_NOTE, NULL, "Not initiating "
79611e1ddd6cScth 			    "auto failback for %s as %s pathclass already "
79621e1ddd6cScth 			    "active.\n", vlun->svl_lun_wwn, best_pclass));
79631e1ddd6cScth 			return;
79641e1ddd6cScth 		}
79651e1ddd6cScth 	}
79661e1ddd6cScth 	mutex_exit(&vlun->svl_mutex);
79671e1ddd6cScth 	if (mdi_failover(vdip, vlun->svl_dip, MDI_FAILOVER_SYNC)
79681e1ddd6cScth 	    == MDI_SUCCESS) {
79691e1ddd6cScth 		vhci_log(CE_NOTE, vdip, "!Auto failback operation "
79701e1ddd6cScth 		    "succeeded for device %s (GUID %s)",
79711e1ddd6cScth 		    ddi_node_name(cdip), vlun->svl_lun_wwn);
79721e1ddd6cScth 	} else {
79731e1ddd6cScth 		vhci_log(CE_NOTE, vdip, "!Auto failback operation "
79741e1ddd6cScth 		    "failed for device %s (GUID %s)",
79751e1ddd6cScth 		    ddi_node_name(cdip), vlun->svl_lun_wwn);
79761e1ddd6cScth 	}
79771e1ddd6cScth 	VHCI_RELEASE_LUN(vlun);
79781e1ddd6cScth }
79791e1ddd6cScth 
79801e1ddd6cScth #ifdef DEBUG
79811e1ddd6cScth static void
vhci_print_prin_keys(vhci_prin_readkeys_t * prin,int numkeys)79821e1ddd6cScth vhci_print_prin_keys(vhci_prin_readkeys_t *prin, int numkeys)
79831e1ddd6cScth {
79840205780bSrralphs 	vhci_clean_print(NULL, 5, "Current PGR Keys",
79850205780bSrralphs 	    (uchar_t *)prin, numkeys * 8);
79861e1ddd6cScth }
79871e1ddd6cScth #endif
79881e1ddd6cScth 
79891e1ddd6cScth static void
vhci_print_prout_keys(scsi_vhci_lun_t * vlun,char * msg)79901e1ddd6cScth vhci_print_prout_keys(scsi_vhci_lun_t *vlun, char *msg)
79911e1ddd6cScth {
79921e1ddd6cScth 	int			i;
79931e1ddd6cScth 	vhci_prout_t		*prout;
79941e1ddd6cScth 	char			buf1[4 * MHIOC_RESV_KEY_SIZE + 1];
79951e1ddd6cScth 	char			buf2[4 * MHIOC_RESV_KEY_SIZE + 1];
79961e1ddd6cScth 	char			buf3[4 * MHIOC_RESV_KEY_SIZE + 1];
79971e1ddd6cScth 	char			buf4[4 * MHIOC_RESV_KEY_SIZE + 1];
79981e1ddd6cScth 
79991e1ddd6cScth 	prout = &vlun->svl_prout;
80001e1ddd6cScth 
80011e1ddd6cScth 	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
80021e1ddd6cScth 		(void) sprintf(&buf1[4 * i], "[%02x]", prout->res_key[i]);
80031e1ddd6cScth 	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
80041e1ddd6cScth 		(void) sprintf(&buf2[(4 * i)], "[%02x]", prout->service_key[i]);
80051e1ddd6cScth 	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
80064e40636aSToomas Soome 		(void) sprintf(&buf3[4 * i], "[%02x]",
80074e40636aSToomas Soome 		    prout->active_res_key[i]);
80081e1ddd6cScth 	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
80091e1ddd6cScth 		(void) sprintf(&buf4[4 * i], "[%02x]",
80101e1ddd6cScth 		    prout->active_service_key[i]);
80111e1ddd6cScth 
80121e1ddd6cScth 	/* Printing all in one go. Otherwise it will jumble up */
80131e1ddd6cScth 	VHCI_DEBUG(5, (CE_CONT, NULL, "%s vlun 0x%p, thread 0x%p\n"
80141e1ddd6cScth 	    "res_key:          : %s\n"
80151e1ddd6cScth 	    "service_key       : %s\n"
80161e1ddd6cScth 	    "active_res_key    : %s\n"
80171e1ddd6cScth 	    "active_service_key: %s\n",
80181e1ddd6cScth 	    msg, (void *)vlun, (void *)curthread, buf1, buf2, buf3, buf4));
80191e1ddd6cScth }
80201e1ddd6cScth 
80211e1ddd6cScth /*
80221e1ddd6cScth  * Called from vhci_scsi_start to update the pHCI pkt with target packet.
80231e1ddd6cScth  */
80241e1ddd6cScth static void
vhci_update_pHCI_pkt(struct vhci_pkt * vpkt,struct scsi_pkt * pkt)80251e1ddd6cScth vhci_update_pHCI_pkt(struct vhci_pkt *vpkt, struct scsi_pkt *pkt)
80261e1ddd6cScth {
80271e1ddd6cScth 
80281e1ddd6cScth 	ASSERT(vpkt->vpkt_hba_pkt);
80291e1ddd6cScth 
80301e1ddd6cScth 	vpkt->vpkt_hba_pkt->pkt_flags = pkt->pkt_flags;
80311e1ddd6cScth 	vpkt->vpkt_hba_pkt->pkt_flags |= FLAG_NOQUEUE;
80321e1ddd6cScth 
80331e1ddd6cScth 	if ((vpkt->vpkt_hba_pkt->pkt_flags & FLAG_NOINTR) ||
80341e1ddd6cScth 	    MDI_PI_IS_SUSPENDED(vpkt->vpkt_path)) {
80351e1ddd6cScth 		/*
80361e1ddd6cScth 		 * Polled Command is requested or HBA is in
80371e1ddd6cScth 		 * suspended state
80381e1ddd6cScth 		 */
80391e1ddd6cScth 		vpkt->vpkt_hba_pkt->pkt_flags |= FLAG_NOINTR;
80401e1ddd6cScth 		vpkt->vpkt_hba_pkt->pkt_comp = NULL;
80411e1ddd6cScth 	} else {
80421e1ddd6cScth 		vpkt->vpkt_hba_pkt->pkt_comp = vhci_intr;
80431e1ddd6cScth 	}
80441e1ddd6cScth 	vpkt->vpkt_hba_pkt->pkt_time = pkt->pkt_time;
80451e1ddd6cScth 	bcopy(pkt->pkt_cdbp, vpkt->vpkt_hba_pkt->pkt_cdbp,
80461e1ddd6cScth 	    vpkt->vpkt_tgt_init_cdblen);
80471e1ddd6cScth 	vpkt->vpkt_hba_pkt->pkt_resid = pkt->pkt_resid;
80481e1ddd6cScth 
80491e1ddd6cScth 	/* Re-initialize the following pHCI packet state information */
80501e1ddd6cScth 	vpkt->vpkt_hba_pkt->pkt_state = 0;
80511e1ddd6cScth 	vpkt->vpkt_hba_pkt->pkt_statistics = 0;
80521e1ddd6cScth 	vpkt->vpkt_hba_pkt->pkt_reason = 0;
80531e1ddd6cScth }
80541e1ddd6cScth 
80551e1ddd6cScth static int
vhci_scsi_bus_power(dev_info_t * parent,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)80561e1ddd6cScth vhci_scsi_bus_power(dev_info_t *parent, void *impl_arg, pm_bus_power_op_t op,
80571e1ddd6cScth     void *arg, void *result)
80581e1ddd6cScth {
80591e1ddd6cScth 	int ret = DDI_SUCCESS;
80601e1ddd6cScth 
80611e1ddd6cScth 	/*
80621e1ddd6cScth 	 * Generic processing in MPxIO framework
80631e1ddd6cScth 	 */
80641e1ddd6cScth 	ret = mdi_bus_power(parent, impl_arg, op, arg, result);
80651e1ddd6cScth 
80661e1ddd6cScth 	switch (ret) {
80671e1ddd6cScth 	case MDI_SUCCESS:
80681e1ddd6cScth 		ret = DDI_SUCCESS;
80691e1ddd6cScth 		break;
80701e1ddd6cScth 	case MDI_FAILURE:
80711e1ddd6cScth 		ret = DDI_FAILURE;
80721e1ddd6cScth 		break;
80731e1ddd6cScth 	default:
80741e1ddd6cScth 		break;
80751e1ddd6cScth 	}
80761e1ddd6cScth 
80771e1ddd6cScth 	return (ret);
80781e1ddd6cScth }
80791e1ddd6cScth 
80801e1ddd6cScth static int
vhci_pHCI_cap(struct scsi_address * ap,char * cap,int val,int whom,mdi_pathinfo_t * pip)80811e1ddd6cScth vhci_pHCI_cap(struct scsi_address *ap, char *cap, int val, int whom,
80821e1ddd6cScth     mdi_pathinfo_t *pip)
80831e1ddd6cScth {
80841e1ddd6cScth 	dev_info_t		*cdip;
80851e1ddd6cScth 	mdi_pathinfo_t		*npip = NULL;
80861e1ddd6cScth 	scsi_vhci_priv_t	*svp = NULL;
80871e1ddd6cScth 	struct scsi_address	*pap = NULL;
80881e1ddd6cScth 	scsi_hba_tran_t		*hba = NULL;
80891e1ddd6cScth 	int			sps;
80901e1ddd6cScth 	int			mps_flag;
80911e1ddd6cScth 	int			rval = 0;
80921e1ddd6cScth 
80931e1ddd6cScth 	mps_flag = (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH);
80941e1ddd6cScth 	if (pip) {
80951e1ddd6cScth 		/*
80961e1ddd6cScth 		 * If the call is from vhci_pathinfo_state_change,
80971e1ddd6cScth 		 * then this path was busy and is becoming ready to accept IO.
80981e1ddd6cScth 		 */
80991e1ddd6cScth 		ASSERT(ap != NULL);
81001e1ddd6cScth 		hba = ap->a_hba_tran;
81011e1ddd6cScth 		ASSERT(hba != NULL);
81021e1ddd6cScth 		rval = scsi_ifsetcap(ap, cap, val, whom);
81031e1ddd6cScth 
81041e1ddd6cScth 		VHCI_DEBUG(2, (CE_NOTE, NULL,
81051e1ddd6cScth 		    "!vhci_pHCI_cap: only on path %p, ap %p, rval %x\n",
81061e1ddd6cScth 		    (void *)pip, (void *)ap, rval));
81071e1ddd6cScth 
81081e1ddd6cScth 		return (rval);
81091e1ddd6cScth 	}
81101e1ddd6cScth 
81111e1ddd6cScth 	/*
81121e1ddd6cScth 	 * Set capability on all the pHCIs.
81131e1ddd6cScth 	 * If any path is busy, then the capability would be set by
81141e1ddd6cScth 	 * vhci_pathinfo_state_change.
81151e1ddd6cScth 	 */
81161e1ddd6cScth 
81171e1ddd6cScth 	cdip = ADDR2DIP(ap);
81181e1ddd6cScth 	ASSERT(cdip != NULL);
81191e1ddd6cScth 	sps = mdi_select_path(cdip, NULL, mps_flag, NULL, &pip);
81201e1ddd6cScth 	if ((sps != MDI_SUCCESS) || (pip == NULL)) {
81211e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL,
81221e1ddd6cScth 		    "!vhci_pHCI_cap: Unable to get a path, dip 0x%p",
81231e1ddd6cScth 		    (void *)cdip));
81241e1ddd6cScth 		return (0);
81251e1ddd6cScth 	}
81261e1ddd6cScth 
81271e1ddd6cScth again:
81281e1ddd6cScth 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
81291e1ddd6cScth 	if (svp == NULL) {
81301e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_pHCI_cap: "
81311e1ddd6cScth 		    "priv is NULL, pip 0x%p", (void *)pip));
81321e1ddd6cScth 		mdi_rele_path(pip);
81331e1ddd6cScth 		return (rval);
81341e1ddd6cScth 	}
81351e1ddd6cScth 
81361e1ddd6cScth 	if (svp->svp_psd == NULL) {
81371e1ddd6cScth 		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_pHCI_cap: "
81381e1ddd6cScth 		    "psd is NULL, pip 0x%p, svp 0x%p",
81391e1ddd6cScth 		    (void *)pip, (void *)svp));
81401e1ddd6cScth 		mdi_rele_path(pip);
81411e1ddd6cScth 		return (rval);
81421e1ddd6cScth 	}
81431e1ddd6cScth 
81441e1ddd6cScth 	pap = &svp->svp_psd->sd_address;
81451e1ddd6cScth 	ASSERT(pap != NULL);
81461e1ddd6cScth 	hba = pap->a_hba_tran;
81471e1ddd6cScth 	ASSERT(hba != NULL);
81481e1ddd6cScth 
81491e1ddd6cScth 	if (hba->tran_setcap != NULL) {
81501e1ddd6cScth 		rval = scsi_ifsetcap(pap, cap, val, whom);
81511e1ddd6cScth 
81521e1ddd6cScth 		VHCI_DEBUG(2, (CE_NOTE, NULL,
81531e1ddd6cScth 		    "!vhci_pHCI_cap: path %p, ap %p, rval %x\n",
81541e1ddd6cScth 		    (void *)pip, (void *)ap, rval));
81551e1ddd6cScth 
81561e1ddd6cScth 		/*
81571e1ddd6cScth 		 * Select next path and issue the setcap, repeat
81581e1ddd6cScth 		 * until all paths are exhausted
81591e1ddd6cScth 		 */
81601e1ddd6cScth 		sps = mdi_select_path(cdip, NULL, mps_flag, pip, &npip);
81611e1ddd6cScth 		if ((sps != MDI_SUCCESS) || (npip == NULL)) {
81621e1ddd6cScth 			mdi_rele_path(pip);
81631e1ddd6cScth 			return (1);
81641e1ddd6cScth 		}
81651e1ddd6cScth 		mdi_rele_path(pip);
81661e1ddd6cScth 		pip = npip;
81671e1ddd6cScth 		goto again;
81681e1ddd6cScth 	}
81691e1ddd6cScth 	mdi_rele_path(pip);
81701e1ddd6cScth 	return (rval);
81711e1ddd6cScth }
81721e1ddd6cScth 
81731e1ddd6cScth static int
vhci_scsi_bus_config(dev_info_t * pdip,uint_t flags,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)81741e1ddd6cScth vhci_scsi_bus_config(dev_info_t *pdip, uint_t flags, ddi_bus_config_op_t op,
81751e1ddd6cScth     void *arg, dev_info_t **child)
81761e1ddd6cScth {
81771e1ddd6cScth 	char *guid;
81781e1ddd6cScth 
817953a7b6b6SChris Horne 	if (vhci_bus_config_debug)
818053a7b6b6SChris Horne 		flags |= NDI_DEVI_DEBUG;
818153a7b6b6SChris Horne 
81821e1ddd6cScth 	if (op == BUS_CONFIG_ONE || op == BUS_UNCONFIG_ONE)
81831e1ddd6cScth 		guid = vhci_devnm_to_guid((char *)arg);
81841e1ddd6cScth 	else
81851e1ddd6cScth 		guid = NULL;
81861e1ddd6cScth 
81871e1ddd6cScth 	if (mdi_vhci_bus_config(pdip, flags, op, arg, child, guid)
81881e1ddd6cScth 	    == MDI_SUCCESS)
81891e1ddd6cScth 		return (NDI_SUCCESS);
81901e1ddd6cScth 	else
81911e1ddd6cScth 		return (NDI_FAILURE);
81921e1ddd6cScth }
81931e1ddd6cScth 
819453a7b6b6SChris Horne static int
vhci_scsi_bus_unconfig(dev_info_t * pdip,uint_t flags,ddi_bus_config_op_t op,void * arg)819553a7b6b6SChris Horne vhci_scsi_bus_unconfig(dev_info_t *pdip, uint_t flags, ddi_bus_config_op_t op,
819653a7b6b6SChris Horne     void *arg)
819753a7b6b6SChris Horne {
819853a7b6b6SChris Horne 	if (vhci_bus_config_debug)
819953a7b6b6SChris Horne 		flags |= NDI_DEVI_DEBUG;
820053a7b6b6SChris Horne 
820153a7b6b6SChris Horne 	return (ndi_busop_bus_unconfig(pdip, flags, op, arg));
820253a7b6b6SChris Horne }
820353a7b6b6SChris Horne 
82041e1ddd6cScth /*
82051e1ddd6cScth  * Take the original vhci_pkt, create a duplicate of the pkt for resending
82061e1ddd6cScth  * as though it originated in ssd.
82071e1ddd6cScth  */
82081e1ddd6cScth static struct scsi_pkt *
vhci_create_retry_pkt(struct vhci_pkt * vpkt)82091e1ddd6cScth vhci_create_retry_pkt(struct vhci_pkt *vpkt)
82101e1ddd6cScth {
82111e1ddd6cScth 	struct vhci_pkt *new_vpkt = NULL;
82121e1ddd6cScth 	struct scsi_pkt	*pkt = NULL;
82131e1ddd6cScth 
82141e1ddd6cScth 	scsi_vhci_priv_t *svp = (scsi_vhci_priv_t *)
82151e1ddd6cScth 	    mdi_pi_get_vhci_private(vpkt->vpkt_path);
82161e1ddd6cScth 
82171e1ddd6cScth 	/*
82181e1ddd6cScth 	 * Ensure consistent data at completion time by setting PKT_CONSISTENT
82191e1ddd6cScth 	 */
82201e1ddd6cScth 	pkt = vhci_scsi_init_pkt(&svp->svp_psd->sd_address, pkt,
82211e1ddd6cScth 	    vpkt->vpkt_tgt_init_bp, vpkt->vpkt_tgt_init_cdblen,
82220205780bSrralphs 	    vpkt->vpkt_tgt_init_scblen, 0, PKT_CONSISTENT, NULL_FUNC, NULL);
82231e1ddd6cScth 	if (pkt != NULL) {
82241e1ddd6cScth 		new_vpkt = TGTPKT2VHCIPKT(pkt);
82251e1ddd6cScth 
82261e1ddd6cScth 		pkt->pkt_address = vpkt->vpkt_tgt_pkt->pkt_address;
82271e1ddd6cScth 		pkt->pkt_flags = vpkt->vpkt_tgt_pkt->pkt_flags;
82281e1ddd6cScth 		pkt->pkt_time = vpkt->vpkt_tgt_pkt->pkt_time;
82291e1ddd6cScth 		pkt->pkt_comp = vpkt->vpkt_tgt_pkt->pkt_comp;
82301e1ddd6cScth 
82311e1ddd6cScth 		pkt->pkt_resid = 0;
82321e1ddd6cScth 		pkt->pkt_statistics = 0;
82331e1ddd6cScth 		pkt->pkt_reason = 0;
82341e1ddd6cScth 
82351e1ddd6cScth 		bcopy(vpkt->vpkt_tgt_pkt->pkt_cdbp,
82361e1ddd6cScth 		    pkt->pkt_cdbp, vpkt->vpkt_tgt_init_cdblen);
82371e1ddd6cScth 
82381e1ddd6cScth 		/*
82391e1ddd6cScth 		 * Save a pointer to the original vhci_pkt
82401e1ddd6cScth 		 */
82411e1ddd6cScth 		new_vpkt->vpkt_org_vpkt = vpkt;
82421e1ddd6cScth 	}
82431e1ddd6cScth 
82441e1ddd6cScth 	return (pkt);
82451e1ddd6cScth }
82461e1ddd6cScth 
82471e1ddd6cScth /*
82481e1ddd6cScth  * Copy the successful completion information from the hba packet into
82491e1ddd6cScth  * the original target pkt from the upper layer.  Returns the original
82501e1ddd6cScth  * vpkt and destroys the new vpkt from the internal retry.
82511e1ddd6cScth  */
82521e1ddd6cScth static struct vhci_pkt *
vhci_sync_retry_pkt(struct vhci_pkt * vpkt)82531e1ddd6cScth vhci_sync_retry_pkt(struct vhci_pkt *vpkt)
82541e1ddd6cScth {
82551e1ddd6cScth 	struct vhci_pkt		*ret_vpkt = NULL;
82561e1ddd6cScth 	struct scsi_pkt		*tpkt = NULL;
82571e1ddd6cScth 	struct scsi_pkt		*hba_pkt = NULL;
82581e1ddd6cScth 	scsi_vhci_priv_t	*svp = (scsi_vhci_priv_t *)
82591e1ddd6cScth 	    mdi_pi_get_vhci_private(vpkt->vpkt_path);
82601e1ddd6cScth 
82611e1ddd6cScth 	ASSERT(vpkt->vpkt_org_vpkt != NULL);
82621e1ddd6cScth 	VHCI_DEBUG(0, (CE_NOTE, NULL, "vhci_sync_retry_pkt: Retry pkt "
82631e1ddd6cScth 	    "completed successfully!\n"));
82641e1ddd6cScth 
82651e1ddd6cScth 	ret_vpkt = vpkt->vpkt_org_vpkt;
82661e1ddd6cScth 	tpkt = ret_vpkt->vpkt_tgt_pkt;
82671e1ddd6cScth 	hba_pkt = vpkt->vpkt_hba_pkt;
82681e1ddd6cScth 
82691e1ddd6cScth 	/*
82701e1ddd6cScth 	 * Copy the good status into the target driver's packet
82711e1ddd6cScth 	 */
82721e1ddd6cScth 	*(tpkt->pkt_scbp) = *(hba_pkt->pkt_scbp);
82731e1ddd6cScth 	tpkt->pkt_resid = hba_pkt->pkt_resid;
82741e1ddd6cScth 	tpkt->pkt_state = hba_pkt->pkt_state;
82751e1ddd6cScth 	tpkt->pkt_statistics = hba_pkt->pkt_statistics;
82761e1ddd6cScth 	tpkt->pkt_reason = hba_pkt->pkt_reason;
82771e1ddd6cScth 
82781e1ddd6cScth 	/*
82791e1ddd6cScth 	 * Destroy the internally created vpkt for the retry
82801e1ddd6cScth 	 */
82811e1ddd6cScth 	vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
82821e1ddd6cScth 	    vpkt->vpkt_tgt_pkt);
82831e1ddd6cScth 
82841e1ddd6cScth 	return (ret_vpkt);
82851e1ddd6cScth }
82861e1ddd6cScth 
82871e1ddd6cScth /* restart the request sense request */
82881e1ddd6cScth static void
vhci_uscsi_restart_sense(void * arg)82891e1ddd6cScth vhci_uscsi_restart_sense(void *arg)
82901e1ddd6cScth {
82911e1ddd6cScth 	struct buf	*rqbp;
82921e1ddd6cScth 	struct buf	*bp;
82931e1ddd6cScth 	struct scsi_pkt *rqpkt = (struct scsi_pkt *)arg;
82941e1ddd6cScth 	mp_uscsi_cmd_t	*mp_uscmdp;
82951e1ddd6cScth 
82961e1ddd6cScth 	VHCI_DEBUG(4, (CE_WARN, NULL,
82971e1ddd6cScth 	    "vhci_uscsi_restart_sense: enter: rqpkt: %p", (void *)rqpkt));
82981e1ddd6cScth 
82991e1ddd6cScth 	if (scsi_transport(rqpkt) != TRAN_ACCEPT) {
83001e1ddd6cScth 		/* if it fails - need to wakeup the original command */
83011e1ddd6cScth 		mp_uscmdp = rqpkt->pkt_private;
83021e1ddd6cScth 		bp = mp_uscmdp->cmdbp;
83031e1ddd6cScth 		rqbp = mp_uscmdp->rqbp;
83041e1ddd6cScth 		ASSERT(mp_uscmdp && bp && rqbp);
83051e1ddd6cScth 		scsi_free_consistent_buf(rqbp);
83061e1ddd6cScth 		scsi_destroy_pkt(rqpkt);
83071e1ddd6cScth 		bp->b_resid = bp->b_bcount;
83081e1ddd6cScth 		bioerror(bp, EIO);
83091e1ddd6cScth 		biodone(bp);
83101e1ddd6cScth 	}
83111e1ddd6cScth }
83121e1ddd6cScth 
83131e1ddd6cScth /*
83141e1ddd6cScth  * auto-rqsense is not enabled so we have to retrieve the request sense
83151e1ddd6cScth  * manually.
83161e1ddd6cScth  */
83171e1ddd6cScth static int
vhci_uscsi_send_sense(struct scsi_pkt * pkt,mp_uscsi_cmd_t * mp_uscmdp)83181e1ddd6cScth vhci_uscsi_send_sense(struct scsi_pkt *pkt, mp_uscsi_cmd_t *mp_uscmdp)
83191e1ddd6cScth {
83201e1ddd6cScth 	struct buf		*rqbp, *cmdbp;
83211e1ddd6cScth 	struct scsi_pkt		*rqpkt;
83221e1ddd6cScth 	int			rval = 0;
83231e1ddd6cScth 
83241e1ddd6cScth 	cmdbp = mp_uscmdp->cmdbp;
83251e1ddd6cScth 	ASSERT(cmdbp != NULL);
83261e1ddd6cScth 
83271e1ddd6cScth 	VHCI_DEBUG(4, (CE_WARN, NULL,
83281e1ddd6cScth 	    "vhci_uscsi_send_sense: enter: bp: %p pkt: %p scmd: %p",
83291e1ddd6cScth 	    (void *)cmdbp, (void *)pkt, (void *)mp_uscmdp));
83301e1ddd6cScth 	/* set up the packet information and cdb */
83311e1ddd6cScth 	if ((rqbp = scsi_alloc_consistent_buf(mp_uscmdp->ap, NULL,
83321e1ddd6cScth 	    SENSE_LENGTH, B_READ, NULL, NULL)) == NULL) {
83331e1ddd6cScth 		return (-1);
83341e1ddd6cScth 	}
83351e1ddd6cScth 
83361e1ddd6cScth 	if ((rqpkt = scsi_init_pkt(mp_uscmdp->ap, NULL, rqbp,
83371e1ddd6cScth 	    CDB_GROUP0, 1, 0, PKT_CONSISTENT, NULL, NULL)) == NULL) {
83381e1ddd6cScth 		scsi_free_consistent_buf(rqbp);
83391e1ddd6cScth 		return (-1);
83401e1ddd6cScth 	}
83411e1ddd6cScth 
83421e1ddd6cScth 	(void) scsi_setup_cdb((union scsi_cdb *)(intptr_t)rqpkt->pkt_cdbp,
83431e1ddd6cScth 	    SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
83441e1ddd6cScth 
83451e1ddd6cScth 	mp_uscmdp->rqbp = rqbp;
83461e1ddd6cScth 	rqbp->b_private = mp_uscmdp;
83471e1ddd6cScth 	rqpkt->pkt_flags |= FLAG_SENSING;
83481e1ddd6cScth 	rqpkt->pkt_time = 60;
83491e1ddd6cScth 	rqpkt->pkt_comp = vhci_uscsi_iodone;
83501e1ddd6cScth 	rqpkt->pkt_private = mp_uscmdp;
83511e1ddd6cScth 
8352602ca9eaScth 	/*
8353bbf21555SRichard Lowe 	 * NOTE: This code path is related to MPAPI uscsi(4I), so path
8354602ca9eaScth 	 * selection is not based on path_instance.
8355602ca9eaScth 	 */
8356602ca9eaScth 	if (scsi_pkt_allocated_correctly(rqpkt))
8357602ca9eaScth 		rqpkt->pkt_path_instance = 0;
8358602ca9eaScth 
83591e1ddd6cScth 	switch (scsi_transport(rqpkt)) {
83601e1ddd6cScth 	case TRAN_ACCEPT:
83611e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_uscsi_send_sense: "
83621e1ddd6cScth 		    "transport accepted."));
83631e1ddd6cScth 		break;
83641e1ddd6cScth 	case TRAN_BUSY:
83651e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_uscsi_send_sense: "
83661e1ddd6cScth 		    "transport busy, setting timeout."));
83671e1ddd6cScth 		vhci_restart_timeid = timeout(vhci_uscsi_restart_sense, rqpkt,
83681e1ddd6cScth 		    (drv_usectohz(5 * 1000000)));
83691e1ddd6cScth 		break;
83701e1ddd6cScth 	default:
83711e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_uscsi_send_sense: "
83721e1ddd6cScth 		    "transport failed"));
83731e1ddd6cScth 		scsi_free_consistent_buf(rqbp);
83741e1ddd6cScth 		scsi_destroy_pkt(rqpkt);
83751e1ddd6cScth 		rval = -1;
83761e1ddd6cScth 	}
83771e1ddd6cScth 
83781e1ddd6cScth 	return (rval);
83791e1ddd6cScth }
83801e1ddd6cScth 
83811e1ddd6cScth /*
83821e1ddd6cScth  * done routine for the mpapi uscsi command - this is behaving as though
83831e1ddd6cScth  * FLAG_DIAGNOSE is set meaning there are no retries except for a manual
83841e1ddd6cScth  * request sense.
83851e1ddd6cScth  */
83861e1ddd6cScth void
vhci_uscsi_iodone(struct scsi_pkt * pkt)83871e1ddd6cScth vhci_uscsi_iodone(struct scsi_pkt *pkt)
83881e1ddd6cScth {
83891e1ddd6cScth 	struct buf			*bp;
83901e1ddd6cScth 	mp_uscsi_cmd_t			*mp_uscmdp;
83911e1ddd6cScth 	struct uscsi_cmd		*uscmdp;
83921e1ddd6cScth 	struct scsi_arq_status		*arqstat;
83931e1ddd6cScth 	int				err;
83941e1ddd6cScth 
83951e1ddd6cScth 	mp_uscmdp = (mp_uscsi_cmd_t *)pkt->pkt_private;
83961e1ddd6cScth 	uscmdp = mp_uscmdp->uscmdp;
83971e1ddd6cScth 	bp = mp_uscmdp->cmdbp;
83981e1ddd6cScth 	ASSERT(bp != NULL);
83991e1ddd6cScth 	VHCI_DEBUG(4, (CE_WARN, NULL,
84001e1ddd6cScth 	    "vhci_uscsi_iodone: enter: bp: %p pkt: %p scmd: %p",
84011e1ddd6cScth 	    (void *)bp, (void *)pkt, (void *)mp_uscmdp));
84021e1ddd6cScth 	/* Save the status and the residual into the uscsi_cmd struct */
84031e1ddd6cScth 	uscmdp->uscsi_status = ((*(pkt)->pkt_scbp) & STATUS_MASK);
84041e1ddd6cScth 	uscmdp->uscsi_resid = bp->b_resid;
84051e1ddd6cScth 
84061e1ddd6cScth 	/* return on a very successful command */
84071e1ddd6cScth 	if (pkt->pkt_reason == CMD_CMPLT &&
84081e1ddd6cScth 	    SCBP_C(pkt) == 0 && ((pkt->pkt_flags & FLAG_SENSING) == 0) &&
84091e1ddd6cScth 	    pkt->pkt_resid == 0) {
84101e1ddd6cScth 		mdi_pi_kstat_iosupdate(mp_uscmdp->pip, bp);
84111e1ddd6cScth 		scsi_destroy_pkt(pkt);
84121e1ddd6cScth 		biodone(bp);
84131e1ddd6cScth 		return;
84141e1ddd6cScth 	}
84151e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL, "iodone: reason=0x%x "
84161e1ddd6cScth 	    " pkt_resid=%ld pkt_state: 0x%x b_count: %ld b_resid: %ld",
84171e1ddd6cScth 	    pkt->pkt_reason, pkt->pkt_resid,
84181e1ddd6cScth 	    pkt->pkt_state, bp->b_bcount, bp->b_resid));
84191e1ddd6cScth 
84201e1ddd6cScth 	err = EIO;
84211e1ddd6cScth 
84221e1ddd6cScth 	arqstat = (struct scsi_arq_status *)(intptr_t)(pkt->pkt_scbp);
84231e1ddd6cScth 	if (pkt->pkt_reason != CMD_CMPLT) {
84241e1ddd6cScth 		/*
84251e1ddd6cScth 		 * The command did not complete.
84261e1ddd6cScth 		 */
84271e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
84281e1ddd6cScth 		    "vhci_uscsi_iodone: command did not complete."
84291e1ddd6cScth 		    " reason: %x flag: %x", pkt->pkt_reason, pkt->pkt_flags));
84301e1ddd6cScth 		if (pkt->pkt_flags & FLAG_SENSING) {
84311e1ddd6cScth 			MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_TRANSERR);
84321e1ddd6cScth 		} else if (pkt->pkt_reason == CMD_TIMEOUT) {
84331e1ddd6cScth 			MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_HARDERR);
84341e1ddd6cScth 			err = ETIMEDOUT;
84351e1ddd6cScth 		}
84361e1ddd6cScth 	} else if (pkt->pkt_state & STATE_ARQ_DONE && mp_uscmdp->arq_enabled) {
84371e1ddd6cScth 		/*
84381e1ddd6cScth 		 * The auto-rqsense happened, and the packet has a filled-in
84391e1ddd6cScth 		 * scsi_arq_status structure, pointed to by pkt_scbp.
84401e1ddd6cScth 		 */
84411e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
84421e1ddd6cScth 		    "vhci_uscsi_iodone: received auto-requested sense"));
84431e1ddd6cScth 		if (uscmdp->uscsi_flags & USCSI_RQENABLE) {
84441e1ddd6cScth 			/* get the amount of data to copy into rqbuf */
84451e1ddd6cScth 			int rqlen = SENSE_LENGTH - arqstat->sts_rqpkt_resid;
84461e1ddd6cScth 			rqlen = min(((int)uscmdp->uscsi_rqlen), rqlen);
84471e1ddd6cScth 			uscmdp->uscsi_rqresid = uscmdp->uscsi_rqlen - rqlen;
84481e1ddd6cScth 			uscmdp->uscsi_rqstatus =
84491e1ddd6cScth 			    *((char *)&arqstat->sts_rqpkt_status);
84501e1ddd6cScth 			if (uscmdp->uscsi_rqbuf && uscmdp->uscsi_rqlen &&
84511e1ddd6cScth 			    rqlen != 0) {
84521e1ddd6cScth 				bcopy(&(arqstat->sts_sensedata),
84531e1ddd6cScth 				    uscmdp->uscsi_rqbuf, rqlen);
84541e1ddd6cScth 			}
84551e1ddd6cScth 			mdi_pi_kstat_iosupdate(mp_uscmdp->pip, bp);
84561e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
84571e1ddd6cScth 			    "vhci_uscsi_iodone: ARQ "
84581e1ddd6cScth 			    "uscsi_rqstatus=0x%x uscsi_rqresid=%d rqlen: %d "
84591e1ddd6cScth 			    "xfer: %d rqpkt_resid: %d\n",
84601e1ddd6cScth 			    uscmdp->uscsi_rqstatus, uscmdp->uscsi_rqresid,
84611e1ddd6cScth 			    uscmdp->uscsi_rqlen, rqlen,
84621e1ddd6cScth 			    arqstat->sts_rqpkt_resid));
84631e1ddd6cScth 		}
84641e1ddd6cScth 	} else if (pkt->pkt_flags & FLAG_SENSING) {
84651e1ddd6cScth 		struct buf *rqbp;
84661e1ddd6cScth 		struct scsi_status *rqstatus;
84671e1ddd6cScth 
84681e1ddd6cScth 		rqstatus = (struct scsi_status *)pkt->pkt_scbp;
84691e1ddd6cScth 		/* a manual request sense was done - get the information */
84701e1ddd6cScth 		if (uscmdp->uscsi_flags & USCSI_RQENABLE) {
84711e1ddd6cScth 			int rqlen = SENSE_LENGTH - pkt->pkt_resid;
84721e1ddd6cScth 
84731e1ddd6cScth 			rqbp = mp_uscmdp->rqbp;
84741e1ddd6cScth 			/* get the amount of data to copy into rqbuf */
84751e1ddd6cScth 			rqlen = min(((int)uscmdp->uscsi_rqlen), rqlen);
84761e1ddd6cScth 			uscmdp->uscsi_rqresid = uscmdp->uscsi_rqlen - rqlen;
84771e1ddd6cScth 			uscmdp->uscsi_rqstatus = *((char *)rqstatus);
84781e1ddd6cScth 			if (uscmdp->uscsi_rqlen && uscmdp->uscsi_rqbuf) {
84791e1ddd6cScth 				bcopy(rqbp->b_un.b_addr, uscmdp->uscsi_rqbuf,
84801e1ddd6cScth 				    rqlen);
84811e1ddd6cScth 			}
84821e1ddd6cScth 			MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_TRANSERR);
84831e1ddd6cScth 			scsi_free_consistent_buf(rqbp);
84841e1ddd6cScth 		}
84851e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_uscsi_iodone: FLAG_SENSING"
84861e1ddd6cScth 		    "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
84871e1ddd6cScth 		    uscmdp->uscsi_rqstatus, uscmdp->uscsi_rqresid));
84881e1ddd6cScth 	} else {
84891e1ddd6cScth 		struct scsi_status *status =
84901e1ddd6cScth 		    (struct scsi_status *)pkt->pkt_scbp;
84911e1ddd6cScth 		/*
84921e1ddd6cScth 		 * Command completed and we're not getting sense. Check for
84931e1ddd6cScth 		 * errors and decide what to do next.
84941e1ddd6cScth 		 */
84951e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
84961e1ddd6cScth 		    "vhci_uscsi_iodone: command appears complete: reason: %x",
84971e1ddd6cScth 		    pkt->pkt_reason));
84981e1ddd6cScth 		if (status->sts_chk) {
84991e1ddd6cScth 			/* need to manually get the request sense */
85001e1ddd6cScth 			if (vhci_uscsi_send_sense(pkt, mp_uscmdp) == 0) {
85011e1ddd6cScth 				scsi_destroy_pkt(pkt);
85021e1ddd6cScth 				return;
85031e1ddd6cScth 			}
85041e1ddd6cScth 		} else {
85051e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
85061e1ddd6cScth 			    "vhci_chk_err: appears complete"));
85071e1ddd6cScth 			err = 0;
85081e1ddd6cScth 			mdi_pi_kstat_iosupdate(mp_uscmdp->pip, bp);
85091e1ddd6cScth 			if (pkt->pkt_resid) {
85101e1ddd6cScth 				bp->b_resid += pkt->pkt_resid;
85111e1ddd6cScth 			}
85121e1ddd6cScth 		}
85131e1ddd6cScth 	}
85141e1ddd6cScth 
85151e1ddd6cScth 	if (err) {
85161e1ddd6cScth 		if (bp->b_resid == 0)
85171e1ddd6cScth 			bp->b_resid = bp->b_bcount;
85181e1ddd6cScth 		bioerror(bp, err);
85191e1ddd6cScth 		bp->b_flags |= B_ERROR;
85201e1ddd6cScth 	}
85211e1ddd6cScth 
85221e1ddd6cScth 	scsi_destroy_pkt(pkt);
85231e1ddd6cScth 	biodone(bp);
85241e1ddd6cScth 
85251e1ddd6cScth 	VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_uscsi_iodone: exit"));
85261e1ddd6cScth }
85271e1ddd6cScth 
85281e1ddd6cScth /*
85291e1ddd6cScth  * start routine for the mpapi uscsi command
85301e1ddd6cScth  */
85311e1ddd6cScth int
vhci_uscsi_iostart(struct buf * bp)85321e1ddd6cScth vhci_uscsi_iostart(struct buf *bp)
85331e1ddd6cScth {
85341e1ddd6cScth 	struct scsi_pkt		*pkt;
85351e1ddd6cScth 	struct uscsi_cmd	*uscmdp;
85361e1ddd6cScth 	mp_uscsi_cmd_t		*mp_uscmdp;
85371e1ddd6cScth 	int			stat_size, rval;
85381e1ddd6cScth 	int			retry = 0;
85391e1ddd6cScth 
85401e1ddd6cScth 	ASSERT(bp->b_private != NULL);
85411e1ddd6cScth 
85421e1ddd6cScth 	mp_uscmdp = (mp_uscsi_cmd_t *)bp->b_private;
85431e1ddd6cScth 	uscmdp = mp_uscmdp->uscmdp;
85441e1ddd6cScth 	if (uscmdp->uscsi_flags & USCSI_RQENABLE) {
85451e1ddd6cScth 		stat_size = SENSE_LENGTH;
85461e1ddd6cScth 	} else {
85471e1ddd6cScth 		stat_size = 1;
85481e1ddd6cScth 	}
85491e1ddd6cScth 
85501e1ddd6cScth 	pkt = scsi_init_pkt(mp_uscmdp->ap, NULL, bp, uscmdp->uscsi_cdblen,
85511e1ddd6cScth 	    stat_size, 0, 0, SLEEP_FUNC, NULL);
85521e1ddd6cScth 	if (pkt == NULL) {
85531e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
85541e1ddd6cScth 		    "vhci_uscsi_iostart: rval: EINVAL"));
85551e1ddd6cScth 		bp->b_resid = bp->b_bcount;
85561e1ddd6cScth 		uscmdp->uscsi_resid = bp->b_bcount;
85571e1ddd6cScth 		bioerror(bp, EINVAL);
85581e1ddd6cScth 		biodone(bp);
85591e1ddd6cScth 		return (EINVAL);
85601e1ddd6cScth 	}
85611e1ddd6cScth 
85621e1ddd6cScth 	pkt->pkt_time = uscmdp->uscsi_timeout;
85631e1ddd6cScth 	bcopy(uscmdp->uscsi_cdb, pkt->pkt_cdbp, (size_t)uscmdp->uscsi_cdblen);
85641e1ddd6cScth 	pkt->pkt_comp = vhci_uscsi_iodone;
85651e1ddd6cScth 	pkt->pkt_private = mp_uscmdp;
85661e1ddd6cScth 	if (uscmdp->uscsi_flags & USCSI_SILENT)
85671e1ddd6cScth 		pkt->pkt_flags |= FLAG_SILENT;
85681e1ddd6cScth 	if (uscmdp->uscsi_flags & USCSI_ISOLATE)
85691e1ddd6cScth 		pkt->pkt_flags |= FLAG_ISOLATE;
85701e1ddd6cScth 	if (uscmdp->uscsi_flags & USCSI_DIAGNOSE)
85711e1ddd6cScth 		pkt->pkt_flags |= FLAG_DIAGNOSE;
85721e1ddd6cScth 	if (uscmdp->uscsi_flags & USCSI_RENEGOT) {
85731e1ddd6cScth 		pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC;
85741e1ddd6cScth 	}
85751e1ddd6cScth 	VHCI_DEBUG(4, (CE_WARN, NULL,
85761e1ddd6cScth 	    "vhci_uscsi_iostart: ap: %p pkt: %p pcdbp: %p uscmdp: %p"
85771e1ddd6cScth 	    " ucdbp: %p pcdblen: %d bp: %p count: %ld pip: %p"
85781e1ddd6cScth 	    " stat_size: %d",
85791e1ddd6cScth 	    (void *)mp_uscmdp->ap, (void *)pkt, (void *)pkt->pkt_cdbp,
85801e1ddd6cScth 	    (void *)uscmdp, (void *)uscmdp->uscsi_cdb, pkt->pkt_cdblen,
85811e1ddd6cScth 	    (void *)bp, bp->b_bcount, (void *)mp_uscmdp->pip, stat_size));
85821e1ddd6cScth 
8583602ca9eaScth 	/*
8584bbf21555SRichard Lowe 	 * NOTE: This code path is related to MPAPI uscsi(4I), so path
8585602ca9eaScth 	 * selection is not based on path_instance.
8586602ca9eaScth 	 */
8587602ca9eaScth 	if (scsi_pkt_allocated_correctly(pkt))
8588602ca9eaScth 		pkt->pkt_path_instance = 0;
8589602ca9eaScth 
85901e1ddd6cScth 	while (((rval = scsi_transport(pkt)) == TRAN_BUSY) &&
85911e1ddd6cScth 	    retry < vhci_uscsi_retry_count) {
85921e1ddd6cScth 		delay(drv_usectohz(vhci_uscsi_delay));
85931e1ddd6cScth 		retry++;
85941e1ddd6cScth 	}
85951e1ddd6cScth 	if (retry >= vhci_uscsi_retry_count) {
85961e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
85971e1ddd6cScth 		    "vhci_uscsi_iostart: tran_busy - retry: %d", retry));
85981e1ddd6cScth 	}
85991e1ddd6cScth 	switch (rval) {
86001e1ddd6cScth 	case TRAN_ACCEPT:
86011e1ddd6cScth 		rval =  0;
86021e1ddd6cScth 		break;
86031e1ddd6cScth 
86041e1ddd6cScth 	default:
86051e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
86061e1ddd6cScth 		    "vhci_uscsi_iostart: rval: %d count: %ld res: %ld",
86071e1ddd6cScth 		    rval, bp->b_bcount, bp->b_resid));
86081e1ddd6cScth 		bp->b_resid = bp->b_bcount;
86091e1ddd6cScth 		uscmdp->uscsi_resid = bp->b_bcount;
86101e1ddd6cScth 		bioerror(bp, EIO);
86111e1ddd6cScth 		scsi_destroy_pkt(pkt);
86121e1ddd6cScth 		biodone(bp);
86131e1ddd6cScth 		rval = EIO;
86141e1ddd6cScth 		MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_TRANSERR);
86151e1ddd6cScth 		break;
86161e1ddd6cScth 	}
86171e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL,
86181e1ddd6cScth 	    "vhci_uscsi_iostart: exit: rval: %d", rval));
86191e1ddd6cScth 	return (rval);
86201e1ddd6cScth }
86210205780bSrralphs 
862255e592a2SRandall Ralphs /* ARGSUSED */
862355e592a2SRandall Ralphs static struct scsi_failover_ops *
vhci_dev_fo(dev_info_t * vdip,struct scsi_device * psd,void ** ctprivp,char ** fo_namep)862455e592a2SRandall Ralphs vhci_dev_fo(dev_info_t *vdip, struct scsi_device *psd,
862555e592a2SRandall Ralphs     void **ctprivp, char **fo_namep)
862655e592a2SRandall Ralphs {
862755e592a2SRandall Ralphs 	struct scsi_failover_ops	*sfo;
862855e592a2SRandall Ralphs 	char				*sfo_name;
862955e592a2SRandall Ralphs 	char				*override;
863055e592a2SRandall Ralphs 	struct scsi_failover		*sf;
86310205780bSrralphs 
863255e592a2SRandall Ralphs 	ASSERT(psd && psd->sd_inq);
863355e592a2SRandall Ralphs 	if ((psd == NULL) || (psd->sd_inq == NULL)) {
863455e592a2SRandall Ralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL,
863555e592a2SRandall Ralphs 		    "!vhci_dev_fo:return NULL no scsi_device or inquiry"));
863655e592a2SRandall Ralphs 		return (NULL);
863755e592a2SRandall Ralphs 	}
863855e592a2SRandall Ralphs 
863955e592a2SRandall Ralphs 	/*
864055e592a2SRandall Ralphs 	 * Determine if device is supported under scsi_vhci, and select
864155e592a2SRandall Ralphs 	 * failover module.
864255e592a2SRandall Ralphs 	 *
864355e592a2SRandall Ralphs 	 * See if there is a scsi_vhci.conf file override for this devices's
864455e592a2SRandall Ralphs 	 * VID/PID. The following values can be returned:
864555e592a2SRandall Ralphs 	 *
864655e592a2SRandall Ralphs 	 * NULL		If the NULL is returned then there is no scsi_vhci.conf
864755e592a2SRandall Ralphs 	 *		override.  For NULL, we determine the failover_ops for
864855e592a2SRandall Ralphs 	 *		this device by checking the sfo_device_probe entry
864955e592a2SRandall Ralphs 	 *		point for each 'fops' module, in order.
865055e592a2SRandall Ralphs 	 *
865155e592a2SRandall Ralphs 	 *		NOTE: Correct operation may depend on module ordering
865255e592a2SRandall Ralphs 	 *		of 'specific' (failover modules that are completely
865355e592a2SRandall Ralphs 	 *		VID/PID table based) to 'generic' (failover modules
865455e592a2SRandall Ralphs 	 *		that based on T10 standards like TPGS).  Currently,
865555e592a2SRandall Ralphs 	 *		the value of 'ddi-forceload' in scsi_vhci.conf is used
865655e592a2SRandall Ralphs 	 *		to establish the module list and probe order.
865755e592a2SRandall Ralphs 	 *
865855e592a2SRandall Ralphs 	 * "NONE"	If value "NONE" is returned then there is a
865955e592a2SRandall Ralphs 	 *		scsi_vhci.conf VID/PID override to indicate the device
866055e592a2SRandall Ralphs 	 *		should not be supported under scsi_vhci (even if there
866155e592a2SRandall Ralphs 	 *		is an 'fops' module supporting the device).
866255e592a2SRandall Ralphs 	 *
866355e592a2SRandall Ralphs 	 * "<other>"	If another value is returned then that value is the
866455e592a2SRandall Ralphs 	 *		name of the 'fops' module that should be used.
866555e592a2SRandall Ralphs 	 */
866655e592a2SRandall Ralphs 	sfo = NULL;	/* "NONE" */
866755e592a2SRandall Ralphs 	override = scsi_get_device_type_string(
866855e592a2SRandall Ralphs 	    "scsi-vhci-failover-override", vdip, psd);
866955e592a2SRandall Ralphs 	if (override == NULL) {
867055e592a2SRandall Ralphs 		/* NULL: default: select based on sfo_device_probe results */
867155e592a2SRandall Ralphs 		for (sf = scsi_failover_table; sf->sf_mod; sf++) {
867255e592a2SRandall Ralphs 			if ((sf->sf_sfo == NULL) ||
867355e592a2SRandall Ralphs 			    sf->sf_sfo->sfo_device_probe(psd, psd->sd_inq,
867455e592a2SRandall Ralphs 			    ctprivp) == SFO_DEVICE_PROBE_PHCI)
867555e592a2SRandall Ralphs 				continue;
867655e592a2SRandall Ralphs 
867755e592a2SRandall Ralphs 			/* found failover module, supported under scsi_vhci */
867855e592a2SRandall Ralphs 			sfo = sf->sf_sfo;
867955e592a2SRandall Ralphs 			if (fo_namep && (*fo_namep == NULL)) {
868055e592a2SRandall Ralphs 				sfo_name = i_ddi_strdup(sfo->sfo_name,
868155e592a2SRandall Ralphs 				    KM_SLEEP);
868255e592a2SRandall Ralphs 				*fo_namep = sfo_name;
868355e592a2SRandall Ralphs 			}
868455e592a2SRandall Ralphs 			break;
868555e592a2SRandall Ralphs 		}
868655e592a2SRandall Ralphs 	} else if (strcasecmp(override, "NONE")) {
868755e592a2SRandall Ralphs 		/* !"NONE": select based on driver.conf specified name */
868855e592a2SRandall Ralphs 		for (sf = scsi_failover_table, sfo = NULL; sf->sf_mod; sf++) {
868955e592a2SRandall Ralphs 			if ((sf->sf_sfo == NULL) ||
869055e592a2SRandall Ralphs 			    (sf->sf_sfo->sfo_name == NULL) ||
869155e592a2SRandall Ralphs 			    strcmp(override, sf->sf_sfo->sfo_name))
869255e592a2SRandall Ralphs 				continue;
869355e592a2SRandall Ralphs 
869455e592a2SRandall Ralphs 			/*
869555e592a2SRandall Ralphs 			 * NOTE: If sfo_device_probe() has side-effects,
869655e592a2SRandall Ralphs 			 * including setting *ctprivp, these are not going
869755e592a2SRandall Ralphs 			 * to occur with override config.
869855e592a2SRandall Ralphs 			 */
869955e592a2SRandall Ralphs 
870055e592a2SRandall Ralphs 			/* found failover module, supported under scsi_vhci */
870155e592a2SRandall Ralphs 			sfo = sf->sf_sfo;
870255e592a2SRandall Ralphs 			if (fo_namep && (*fo_namep == NULL)) {
870355e592a2SRandall Ralphs 				sfo_name = kmem_alloc(strlen("conf ") +
870455e592a2SRandall Ralphs 				    strlen(sfo->sfo_name) + 1, KM_SLEEP);
870555e592a2SRandall Ralphs 				(void) sprintf(sfo_name, "conf %s",
870655e592a2SRandall Ralphs 				    sfo->sfo_name);
870755e592a2SRandall Ralphs 				*fo_namep = sfo_name;
870855e592a2SRandall Ralphs 			}
870955e592a2SRandall Ralphs 			break;
871055e592a2SRandall Ralphs 		}
871155e592a2SRandall Ralphs 	}
871255e592a2SRandall Ralphs 	if (override)
871355e592a2SRandall Ralphs 		kmem_free(override, strlen(override) + 1);
871455e592a2SRandall Ralphs 	return (sfo);
871555e592a2SRandall Ralphs }
871655e592a2SRandall Ralphs 
871755e592a2SRandall Ralphs /*
871855e592a2SRandall Ralphs  * Determine the device described by cinfo should be enumerated under
871955e592a2SRandall Ralphs  * the vHCI or the pHCI - if there is a failover ops then device is
872055e592a2SRandall Ralphs  * supported under vHCI.  By agreement with SCSA cinfo is a pointer
872155e592a2SRandall Ralphs  * to a scsi_device structure associated with a decorated pHCI probe node.
872255e592a2SRandall Ralphs  */
872355e592a2SRandall Ralphs /* ARGSUSED */
872455e592a2SRandall Ralphs int
vhci_is_dev_supported(dev_info_t * vdip,dev_info_t * pdip,void * cinfo)872555e592a2SRandall Ralphs vhci_is_dev_supported(dev_info_t *vdip, dev_info_t *pdip, void *cinfo)
872655e592a2SRandall Ralphs {
872755e592a2SRandall Ralphs 	struct scsi_device	*psd = (struct scsi_device *)cinfo;
872855e592a2SRandall Ralphs 
872955e592a2SRandall Ralphs 	return (vhci_dev_fo(vdip, psd, NULL, NULL) ? MDI_SUCCESS : MDI_FAILURE);
873055e592a2SRandall Ralphs }
873155e592a2SRandall Ralphs 
873255e592a2SRandall Ralphs 
873355e592a2SRandall Ralphs #ifdef DEBUG
87340205780bSrralphs extern struct scsi_key_strings scsi_cmds[];
87350205780bSrralphs 
87360205780bSrralphs static char *
vhci_print_scsi_cmd(char cmd)87370205780bSrralphs vhci_print_scsi_cmd(char cmd)
87380205780bSrralphs {
87390205780bSrralphs 	char tmp[64];
87400205780bSrralphs 	char *cpnt;
87410205780bSrralphs 
87420205780bSrralphs 	cpnt = scsi_cmd_name(cmd, scsi_cmds, tmp);
87430205780bSrralphs 	/* tmp goes out of scope on return and caller sees garbage */
87440205780bSrralphs 	if (cpnt == tmp) {
87450205780bSrralphs 		cpnt = "Unknown Command";
87460205780bSrralphs 	}
87470205780bSrralphs 	return (cpnt);
87480205780bSrralphs }
87490205780bSrralphs 
87500205780bSrralphs extern uchar_t	scsi_cdb_size[];
87510205780bSrralphs 
87520205780bSrralphs static void
vhci_print_cdb(dev_info_t * dip,uint_t level,char * title,uchar_t * cdb)87530205780bSrralphs vhci_print_cdb(dev_info_t *dip, uint_t level, char *title, uchar_t *cdb)
87540205780bSrralphs {
87550205780bSrralphs 	int len = scsi_cdb_size[CDB_GROUPID(cdb[0])];
87560205780bSrralphs 	char buf[256];
87570205780bSrralphs 
87580205780bSrralphs 	if (level == CE_NOTE) {
87590205780bSrralphs 		vhci_log(level, dip, "path cmd %s\n",
87600205780bSrralphs 		    vhci_print_scsi_cmd(*cdb));
87610205780bSrralphs 		return;
87620205780bSrralphs 	}
87630205780bSrralphs 
87640205780bSrralphs 	(void) sprintf(buf, "%s for cmd(%s)", title, vhci_print_scsi_cmd(*cdb));
87650205780bSrralphs 	vhci_clean_print(dip, level, buf, cdb, len);
87660205780bSrralphs }
87670205780bSrralphs 
87680205780bSrralphs static void
vhci_clean_print(dev_info_t * dev,uint_t level,char * title,uchar_t * data,int len)87690205780bSrralphs vhci_clean_print(dev_info_t *dev, uint_t level, char *title, uchar_t *data,
87700205780bSrralphs     int len)
87710205780bSrralphs {
87720205780bSrralphs 	int	i;
87730205780bSrralphs 	int	c;
87740205780bSrralphs 	char	*format;
87750205780bSrralphs 	char	buf[256];
87760205780bSrralphs 	uchar_t	byte;
87770205780bSrralphs 
87780205780bSrralphs 	(void) sprintf(buf, "%s:\n", title);
87790205780bSrralphs 	vhci_log(level, dev, "%s", buf);
87800205780bSrralphs 	level = CE_CONT;
87810205780bSrralphs 	for (i = 0; i < len; ) {
87820205780bSrralphs 		buf[0] = 0;
87830205780bSrralphs 		for (c = 0; c < 8 && i < len; c++, i++) {
87840205780bSrralphs 			byte = (uchar_t)data[i];
87850205780bSrralphs 			if (byte < 0x10)
87860205780bSrralphs 				format = "0x0%x ";
87870205780bSrralphs 			else
87880205780bSrralphs 				format = "0x%x ";
87890205780bSrralphs 			(void) sprintf(&buf[(int)strlen(buf)], format, byte);
87900205780bSrralphs 		}
87910205780bSrralphs 		(void) sprintf(&buf[(int)strlen(buf)], "\n");
87920205780bSrralphs 
87930205780bSrralphs 		vhci_log(level, dev, "%s\n", buf);
87940205780bSrralphs 	}
87950205780bSrralphs }
87960205780bSrralphs #endif
87974c06356bSdh142964 static void
vhci_invalidate_mpapi_lu(struct scsi_vhci * vhci,scsi_vhci_lun_t * vlun)87984c06356bSdh142964 vhci_invalidate_mpapi_lu(struct scsi_vhci *vhci, scsi_vhci_lun_t *vlun)
87994c06356bSdh142964 {
88004c06356bSdh142964 	char			*svl_wwn;
88014c06356bSdh142964 	mpapi_item_list_t	*ilist;
88024c06356bSdh142964 	mpapi_lu_data_t		*ld;
88034c06356bSdh142964 
88044c06356bSdh142964 	if (vlun == NULL) {
88054c06356bSdh142964 		return;
88064c06356bSdh142964 	} else {
88074c06356bSdh142964 		svl_wwn = vlun->svl_lun_wwn;
88084c06356bSdh142964 	}
88094c06356bSdh142964 
88104c06356bSdh142964 	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
88114c06356bSdh142964 
88124c06356bSdh142964 	while (ilist != NULL) {
88134c06356bSdh142964 		ld = (mpapi_lu_data_t *)(ilist->item->idata);
88144c06356bSdh142964 		if ((ld != NULL) && (strncmp(ld->prop.name, svl_wwn,
88154c06356bSdh142964 		    strlen(svl_wwn)) == 0)) {
88164c06356bSdh142964 			ld->valid = 0;
88174c06356bSdh142964 			VHCI_DEBUG(6, (CE_WARN, NULL,
88184c06356bSdh142964 			    "vhci_invalidate_mpapi_lu: "
88194c06356bSdh142964 			    "Invalidated LU(%s)", svl_wwn));
88204c06356bSdh142964 			return;
88214c06356bSdh142964 		}
88224c06356bSdh142964 		ilist = ilist->next;
88234c06356bSdh142964 	}
88244c06356bSdh142964 	VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_invalidate_mpapi_lu: "
88254c06356bSdh142964 	    "Could not find LU(%s) to invalidate.", svl_wwn));
88264c06356bSdh142964 }
8827*28c5054dSJason King 
8828*28c5054dSJason King /*
8829*28c5054dSJason King  * Return the device's block size (as given by the 'device-blksize'
8830*28c5054dSJason King  * property). If the property does not exist, the default DEV_BSIZE
8831*28c5054dSJason King  * is returned.
8832*28c5054dSJason King  */
8833*28c5054dSJason King size_t
vhci_get_blocksize(dev_info_t * dip)8834*28c5054dSJason King vhci_get_blocksize(dev_info_t *dip)
8835*28c5054dSJason King {
8836*28c5054dSJason King 	/*
8837*28c5054dSJason King 	 * Unfortunately, 'device-blksize' is typically implemented in
8838*28c5054dSJason King 	 * a device as a dynamic property managed by cmlb. As a result,
8839*28c5054dSJason King 	 * we cannot merely use ddi_prop_get_int() to get the value.
8840*28c5054dSJason King 	 * Instead, we must call the cb_prop_op on the device.
8841*28c5054dSJason King 	 * If that fails, we will attempt ddi_prop_get_int() in case
8842*28c5054dSJason King 	 * there is a device that defines it as a static property.
8843*28c5054dSJason King 	 * If all else fails, we return DEV_BSIZE.
8844*28c5054dSJason King 	 */
8845*28c5054dSJason King 	struct dev_ops *ops = DEVI(dip)->devi_ops;
8846*28c5054dSJason King 
8847*28c5054dSJason King 	/*
8848*28c5054dSJason King 	 * The DDI property interfaces don't recognize unsigned
8849*28c5054dSJason King 	 * values, so we have to cast it outself when we return the value.
8850*28c5054dSJason King 	 */
8851*28c5054dSJason King 	int blocksize = DEV_BSIZE;
8852*28c5054dSJason King 
8853*28c5054dSJason King 	/*
8854*28c5054dSJason King 	 * According to i_ldi_prop_op(), some nexus drivers apparently do not
8855*28c5054dSJason King 	 * always correctly set cb_prop_op, so we must check for
8856*28c5054dSJason King 	 * nodev, nulldev, and NULL.
8857*28c5054dSJason King 	 */
8858*28c5054dSJason King 	if (ops->devo_cb_ops->cb_prop_op != nodev &&
8859*28c5054dSJason King 	    ops->devo_cb_ops->cb_prop_op != nulldev &&
8860*28c5054dSJason King 	    ops->devo_cb_ops->cb_prop_op != NULL) {
8861*28c5054dSJason King 		int proplen = sizeof (blocksize);
8862*28c5054dSJason King 		int ret;
8863*28c5054dSJason King 
8864*28c5054dSJason King 		ret = cdev_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
8865*28c5054dSJason King 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM | DDI_PROP_DYNAMIC,
8866*28c5054dSJason King 		    "device-blksize", (caddr_t)&blocksize, &proplen);
8867*28c5054dSJason King 		if (ret == DDI_PROP_SUCCESS && proplen == sizeof (blocksize) &&
8868*28c5054dSJason King 		    blocksize > 0)
8869*28c5054dSJason King 			return (blocksize);
8870*28c5054dSJason King 	}
8871*28c5054dSJason King 
8872*28c5054dSJason King 	blocksize = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "device-blksize",
8873*28c5054dSJason King 	    DEV_BSIZE);
8874*28c5054dSJason King 	return ((blocksize > 0) ? blocksize : DEV_BSIZE);
8875*28c5054dSJason King }
8876