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  */
219a70fc3bSMark J. Nelson 
221e1ddd6cScth /*
2340764c95SVictor Li  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24*28c5054dSJason King  * Copyright 2024 RackTop Systems, Inc.
251e1ddd6cScth  */
261e1ddd6cScth 
271e1ddd6cScth /*
281e1ddd6cScth  * Implementation of "scsi_vhci_f_tpgs" T10 standard based failover_ops.
291e1ddd6cScth  *
301e1ddd6cScth  * NOTE: for non-sequential devices only.
311e1ddd6cScth  */
321e1ddd6cScth 
331e1ddd6cScth #include <sys/conf.h>
341e1ddd6cScth #include <sys/file.h>
351e1ddd6cScth #include <sys/ddi.h>
361e1ddd6cScth #include <sys/sunddi.h>
371e1ddd6cScth #include <sys/scsi/scsi.h>
381e1ddd6cScth #include <sys/scsi/adapters/scsi_vhci.h>
390205780bSrralphs #include <sys/scsi/adapters/scsi_vhci_tpgs.h>
401e1ddd6cScth 
411e1ddd6cScth /* Supported device table entries.  */
421e1ddd6cScth char	*std_dev_table[] = { NULL };
431e1ddd6cScth 
441e1ddd6cScth /* Failover module plumbing. */
459a70fc3bSMark J. Nelson SCSI_FAILOVER_OP(SFO_NAME_TPGS, std);
461e1ddd6cScth 
471e1ddd6cScth #define	STD_FO_CMD_RETRY_DELAY	1000000 /* 1 seconds */
481e1ddd6cScth #define	STD_FO_RETRY_DELAY	2000000 /* 2 seconds */
491e1ddd6cScth /*
501e1ddd6cScth  * max time for failover to complete is 3 minutes.  Compute
511e1ddd6cScth  * number of retries accordingly, to ensure we wait for at least
521e1ddd6cScth  * 3 minutes
531e1ddd6cScth  */
541e1ddd6cScth #define	STD_FO_MAX_RETRIES	(3*60*1000000)/STD_FO_RETRY_DELAY
551e1ddd6cScth 
561e1ddd6cScth 
571e1ddd6cScth /* ARGSUSED */
581e1ddd6cScth static int
std_device_probe(struct scsi_device * sd,struct scsi_inquiry * inq,void ** ctpriv)591e1ddd6cScth std_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq,
600205780bSrralphs     void **ctpriv)
611e1ddd6cScth {
621e1ddd6cScth 	int		mode, state, xlf, preferred = 0;
631e1ddd6cScth 
641e1ddd6cScth 	VHCI_DEBUG(6, (CE_NOTE, NULL, "std_device_probe: vidpid %s\n",
651e1ddd6cScth 	    inq->inq_vid));
661e1ddd6cScth 
67d91393a8SChris Horne 	if (inq->inq_tpgs == TPGS_FAILOVER_NONE) {
681e1ddd6cScth 		VHCI_DEBUG(4, (CE_WARN, NULL,
691e1ddd6cScth 		    "!std_device_probe: not a standard tpgs device"));
701e1ddd6cScth 		return (SFO_DEVICE_PROBE_PHCI);
711e1ddd6cScth 	}
721e1ddd6cScth 
730205780bSrralphs 	if (inq->inq_dtype == DTYPE_SEQUENTIAL) {
741e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
751e1ddd6cScth 		    "!std_device_probe: Detected a "
761e1ddd6cScth 		    "Standard Asymmetric device "
771e1ddd6cScth 		    "not yet supported\n"));
781e1ddd6cScth 		return (SFO_DEVICE_PROBE_PHCI);
791e1ddd6cScth 	}
801e1ddd6cScth 
810205780bSrralphs 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
821e1ddd6cScth 		VHCI_DEBUG(4, (CE_WARN, NULL, "!unable to fetch fo "
831e1ddd6cScth 		    "mode: sd(%p)", (void *) sd));
841e1ddd6cScth 		return (SFO_DEVICE_PROBE_PHCI);
851e1ddd6cScth 	}
861e1ddd6cScth 
87d91393a8SChris Horne 	if (inq->inq_tpgs == TPGS_FAILOVER_IMPLICIT) {
881e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL,
891e1ddd6cScth 		    "!std_device_probe: Detected a "
901e1ddd6cScth 		    "Standard Asymmetric device "
911e1ddd6cScth 		    "with implicit failover\n"));
921e1ddd6cScth 		return (SFO_DEVICE_PROBE_VHCI);
931e1ddd6cScth 	}
94d91393a8SChris Horne 	if (inq->inq_tpgs == TPGS_FAILOVER_EXPLICIT) {
951e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL,
961e1ddd6cScth 		    "!std_device_probe: Detected a "
971e1ddd6cScth 		    "Standard Asymmetric device "
981e1ddd6cScth 		    "with explicit failover\n"));
991e1ddd6cScth 		return (SFO_DEVICE_PROBE_VHCI);
1001e1ddd6cScth 	}
101d91393a8SChris Horne 	if (inq->inq_tpgs == TPGS_FAILOVER_BOTH) {
1021e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL,
1031e1ddd6cScth 		    "!std_device_probe: Detected a "
1041e1ddd6cScth 		    "Standard Asymmetric device "
1051e1ddd6cScth 		    "which supports both implicit and explicit failover\n"));
1061e1ddd6cScth 		return (SFO_DEVICE_PROBE_VHCI);
1071e1ddd6cScth 	}
1081e1ddd6cScth 	VHCI_DEBUG(1, (CE_WARN, NULL,
1091e1ddd6cScth 	    "!std_device_probe: "
1100205780bSrralphs 	    "Unknown tpgs_bits: %x", inq->inq_tpgs));
1111e1ddd6cScth 	return (SFO_DEVICE_PROBE_PHCI);
1121e1ddd6cScth }
1131e1ddd6cScth 
1141e1ddd6cScth /* ARGSUSED */
1151e1ddd6cScth static void
std_device_unprobe(struct scsi_device * sd,void * ctpriv)1161e1ddd6cScth std_device_unprobe(struct scsi_device *sd, void *ctpriv)
1171e1ddd6cScth {
1181e1ddd6cScth 	/*
1191e1ddd6cScth 	 * For future use
1201e1ddd6cScth 	 */
1211e1ddd6cScth }
1221e1ddd6cScth 
1231e1ddd6cScth /* ARGSUSED */
1241e1ddd6cScth static int
std_activate_explicit(struct scsi_device * sd,int xlf_capable)1251e1ddd6cScth std_activate_explicit(struct scsi_device *sd, int xlf_capable)
1261e1ddd6cScth {
1271e1ddd6cScth 	cmn_err(CE_NOTE, "Explicit Activation is done by "
1280205780bSrralphs 	    "vhci_tpgs_set_target_groups() call from MPAPI");
1291e1ddd6cScth 	return (1);
1301e1ddd6cScth }
1311e1ddd6cScth 
1321e1ddd6cScth /*
1331e1ddd6cScth  * Process the packet reason of CMD_PKT_CMPLT - return 0 if no
1341e1ddd6cScth  * retry and 1 if a retry should be done
1351e1ddd6cScth  */
1361e1ddd6cScth static int
std_process_cmplt_pkt(struct scsi_device * sd,struct scsi_pkt * pkt,int * retry_cnt,int * retval)1371e1ddd6cScth std_process_cmplt_pkt(struct scsi_device *sd, struct scsi_pkt *pkt,
13840764c95SVictor Li     int *retry_cnt, int *retval)
1391e1ddd6cScth {
14040764c95SVictor Li 	*retval = 1; /* fail */
1411e1ddd6cScth 
1421e1ddd6cScth 	switch (SCBP_C(pkt)) {
1431e1ddd6cScth 		case STATUS_GOOD:
14440764c95SVictor Li 			*retval = 0;
1451e1ddd6cScth 			break;
1461e1ddd6cScth 		case STATUS_CHECK:
14740764c95SVictor Li 			if (pkt->pkt_state & STATE_ARQ_DONE) {
14840764c95SVictor Li 				uint8_t *sns, skey, asc, ascq;
14940764c95SVictor Li 				sns = (uint8_t *)
15040764c95SVictor Li 				    &(((struct scsi_arq_status *)(uintptr_t)
15140764c95SVictor Li 				    (pkt->pkt_scbp))->sts_sensedata);
15240764c95SVictor Li 				skey = scsi_sense_key(sns);
15340764c95SVictor Li 				asc = scsi_sense_asc(sns);
15440764c95SVictor Li 				ascq = scsi_sense_ascq(sns);
15540764c95SVictor Li 				if (skey == KEY_UNIT_ATTENTION) {
15640764c95SVictor Li 					/*
15740764c95SVictor Li 					 * tpgs access state changed
15840764c95SVictor Li 					 */
15940764c95SVictor Li 					if (asc == STD_SCSI_ASC_STATE_CHG &&
16040764c95SVictor Li 					    ascq ==
16140764c95SVictor Li 					    STD_SCSI_ASCQ_STATE_CHG_SUCC) {
16240764c95SVictor Li 						/* XXX: update path info? */
16340764c95SVictor Li 						cmn_err(CE_WARN,
16440764c95SVictor Li 						    "!Device failover"
16540764c95SVictor Li 						    " state change");
16640764c95SVictor Li 					}
16740764c95SVictor Li 					return (1);
16840764c95SVictor Li 				} else if (skey == KEY_NOT_READY) {
16940764c95SVictor Li 					if (asc ==
17040764c95SVictor Li 					    STD_LOGICAL_UNIT_NOT_ACCESSIBLE &&
17140764c95SVictor Li 					    ascq == STD_TGT_PORT_STANDBY) {
17240764c95SVictor Li 						/*
17340764c95SVictor Li 						 * Don't retry on the path
17440764c95SVictor Li 						 * which is indicated as
17540764c95SVictor Li 						 * standby, return failure.
17640764c95SVictor Li 						 */
17740764c95SVictor Li 						return (0);
17840764c95SVictor Li 					} else if ((*retry_cnt)++ >=
17940764c95SVictor Li 					    STD_FO_MAX_RETRIES) {
18040764c95SVictor Li 						cmn_err(CE_WARN,
18140764c95SVictor Li 						    "!Device failover failed: "
18240764c95SVictor Li 						    "timed out waiting for "
18340764c95SVictor Li 						    "path to become active");
18440764c95SVictor Li 						return (0);
18540764c95SVictor Li 					}
18640764c95SVictor Li 					VHCI_DEBUG(6, (CE_NOTE, NULL,
18740764c95SVictor Li 					    "!(sd:%p)lun becoming active...\n",
18840764c95SVictor Li 					    (void *)sd));
18940764c95SVictor Li 					drv_usecwait(STD_FO_RETRY_DELAY);
19040764c95SVictor Li 					return (1);
19140764c95SVictor Li 				}
19240764c95SVictor Li 				cmn_err(CE_NOTE, "!Failover failed;"
19340764c95SVictor Li 				    " sense key:%x, ASC: %x, "
19440764c95SVictor Li 				    "ASCQ:%x", skey, asc, ascq);
19540764c95SVictor Li 				return (0);
19640764c95SVictor Li 			}
1971e1ddd6cScth 			VHCI_DEBUG(4, (CE_WARN, NULL,
1981e1ddd6cScth 			    "!(sd:%p):"
1991e1ddd6cScth 			    " status returned CHECK during std"
2001e1ddd6cScth 			    " path activation", (void *)sd));
2011e1ddd6cScth 			return (0);
2021e1ddd6cScth 		case STATUS_QFULL:
2031e1ddd6cScth 			VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL "
2041e1ddd6cScth 			    "status returned QFULL during std "
2051e1ddd6cScth 			    "path activation for %p\n", (void *)sd));
2061e1ddd6cScth 			drv_usecwait(5000);
2071e1ddd6cScth 			return (1);
2081e1ddd6cScth 		case STATUS_BUSY:
2091e1ddd6cScth 			VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY "
2101e1ddd6cScth 			    "status returned BUSY during std "
2111e1ddd6cScth 			    "path activation for %p\n", (void *)sd));
2121e1ddd6cScth 			drv_usecwait(5000);
2131e1ddd6cScth 			return (1);
2141e1ddd6cScth 		default:
2151e1ddd6cScth 			VHCI_DEBUG(4, (CE_WARN, NULL,
2161e1ddd6cScth 			    "!(sd:%p) Bad status returned during std "
2171e1ddd6cScth 			    "activation (pkt %p, status %x)",
2181e1ddd6cScth 			    (void *)sd, (void *)pkt, SCBP_C(pkt)));
2191e1ddd6cScth 			return (0);
2201e1ddd6cScth 	}
2211e1ddd6cScth 	return (0);
2221e1ddd6cScth }
2231e1ddd6cScth 
2241e1ddd6cScth /*
2251e1ddd6cScth  * For now we are going to use primary/online and secondary/online.
2261e1ddd6cScth  * There is no standby path returned by the dsp and we may have
2271e1ddd6cScth  * to do something different for other devices that use standby
2281e1ddd6cScth  */
2291e1ddd6cScth /* ARGSUSED */
2301e1ddd6cScth static int
std_path_activate(struct scsi_device * sd,char * pathclass,void * ctpriv)2311e1ddd6cScth std_path_activate(struct scsi_device *sd, char *pathclass,
2320205780bSrralphs     void *ctpriv)
2331e1ddd6cScth {
2341e1ddd6cScth 	struct buf			*bp;
2351e1ddd6cScth 	struct scsi_pkt			*pkt;
2361e1ddd6cScth 	struct scsi_address		*ap;
2371e1ddd6cScth 	int				err, retry_cnt, retry_cmd_cnt;
2381e1ddd6cScth 	int				mode, state, retval, xlf, preferred;
239*28c5054dSJason King 	size_t				blksize;
2401e1ddd6cScth 
2411e1ddd6cScth 	ap = &sd->sd_address;
2421e1ddd6cScth 
2431e1ddd6cScth 	mode = state = 0;
2441e1ddd6cScth 
245*28c5054dSJason King 	blksize = vhci_get_blocksize(sd->sd_dev);
246*28c5054dSJason King 
2470205780bSrralphs 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
2481e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_activate:"
2490205780bSrralphs 		    " failed vhci_tpgs_get_target_fo_mode\n"));
2501e1ddd6cScth 		return (1);
2511e1ddd6cScth 	}
2521e1ddd6cScth 	if ((state == STD_ACTIVE_OPTIMIZED) ||
2531e1ddd6cScth 	    (state == STD_ACTIVE_NONOPTIMIZED)) {
2541e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for %p\n",
2551e1ddd6cScth 		    (void *)sd));
2561e1ddd6cScth 		return (0);
2571e1ddd6cScth 	}
2581e1ddd6cScth 
259cc25db92SChris Liu 	if (mode == SCSI_EXPLICIT_FAILOVER) {
2601e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL,
2611e1ddd6cScth 		    "!mode is EXPLICIT for %p xlf %x\n",
2621e1ddd6cScth 		    (void *)sd, xlf));
2631e1ddd6cScth 		retval = std_activate_explicit(sd, xlf);
2641e1ddd6cScth 		if (retval != 0) {
2651e1ddd6cScth 			VHCI_DEBUG(4, (CE_NOTE, NULL,
2661e1ddd6cScth 			    "!(sd:%p)std_path_activate failed(1)\n",
2671e1ddd6cScth 			    (void *)sd));
2681e1ddd6cScth 			return (1);
2691e1ddd6cScth 		}
2701e1ddd6cScth 	} else {
2711e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL, "STD mode is IMPLICIT for %p\n",
2721e1ddd6cScth 		    (void *)sd));
2731e1ddd6cScth 	}
2741e1ddd6cScth 
275*28c5054dSJason King 	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, blksize, B_READ,
276*28c5054dSJason King 	    NULL, NULL);
2771e1ddd6cScth 	if (!bp) {
2781e1ddd6cScth 		VHCI_DEBUG(4, (CE_WARN, NULL,
2791e1ddd6cScth 		    "!(sd:%p)std_path_activate failed to alloc buffer",
2801e1ddd6cScth 		    (void *)sd));
2811e1ddd6cScth 		return (1);
2821e1ddd6cScth 	}
2831e1ddd6cScth 
2841e1ddd6cScth 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1,
2851e1ddd6cScth 	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
2861e1ddd6cScth 	if (!pkt) {
2871e1ddd6cScth 		VHCI_DEBUG(4, (CE_WARN, NULL,
2881e1ddd6cScth 		    "!(sd:%p)std_path_activate failed to initialize packet",
2891e1ddd6cScth 		    (void *)sd));
2901e1ddd6cScth 		scsi_free_consistent_buf(bp);
2911e1ddd6cScth 		return (1);
2921e1ddd6cScth 	}
2931e1ddd6cScth 
2941e1ddd6cScth 	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
295*28c5054dSJason King 	    SCMD_READ_G1, 1, 1, 0);
2961e1ddd6cScth 	pkt->pkt_time = 3*30;
2971e1ddd6cScth 	pkt->pkt_flags |= FLAG_NOINTR;
2981e1ddd6cScth 
2991e1ddd6cScth 	retry_cnt = 0;
3001e1ddd6cScth 	retry_cmd_cnt = 0;
3011e1ddd6cScth retry:
3021e1ddd6cScth 	err = scsi_transport(pkt);
3031e1ddd6cScth 	if (err != TRAN_ACCEPT) {
3041e1ddd6cScth 		/*
3051e1ddd6cScth 		 * Retry TRAN_BUSY till STD_FO_MAX_RETRIES is exhausted.
3061e1ddd6cScth 		 * All other errors are fatal and should not be retried.
3071e1ddd6cScth 		 */
3081e1ddd6cScth 		if ((err == TRAN_BUSY) &&
3091e1ddd6cScth 		    (retry_cnt++ < STD_FO_MAX_RETRIES)) {
3101e1ddd6cScth 			drv_usecwait(STD_FO_RETRY_DELAY);
3111e1ddd6cScth 			goto retry;
3121e1ddd6cScth 		}
3131e1ddd6cScth 		cmn_err(CE_WARN, "Failover failed, "
3141e1ddd6cScth 		    "couldn't transport packet");
3151e1ddd6cScth 		scsi_destroy_pkt(pkt);
3161e1ddd6cScth 		scsi_free_consistent_buf(bp);
3171e1ddd6cScth 		return (1);
3181e1ddd6cScth 	}
3191e1ddd6cScth 	switch (pkt->pkt_reason) {
3201e1ddd6cScth 		case CMD_CMPLT:
32140764c95SVictor Li 			/*
32240764c95SVictor Li 			 * Re-initialize retry_cmd_cnt. Allow transport and
32340764c95SVictor Li 			 * cmd errors to go through a full retry count when
32440764c95SVictor Li 			 * these are encountered.  This way TRAN/CMD errors
32540764c95SVictor Li 			 * retry count is not exhausted due to CMD_CMPLTs
32640764c95SVictor Li 			 * delay. This allows the system
32740764c95SVictor Li 			 * to brave a hick-up on the link at any given time,
32840764c95SVictor Li 			 * while waiting for the fo to complete.
32940764c95SVictor Li 			 */
3301e1ddd6cScth 			retry_cmd_cnt = 0;
33140764c95SVictor Li 			if (std_process_cmplt_pkt(sd, pkt, &retry_cnt,
33240764c95SVictor Li 			    &retval) != 0) {
3331e1ddd6cScth 				goto retry;
3341e1ddd6cScth 			}
3351e1ddd6cScth 			break;
3361e1ddd6cScth 		case CMD_TIMEOUT:
3371e1ddd6cScth 			cmn_err(CE_WARN, "!Failover failed: timed out ");
3381e1ddd6cScth 			retval = 1;
3391e1ddd6cScth 			break;
3401e1ddd6cScth 		case CMD_INCOMPLETE:
3411e1ddd6cScth 		case CMD_RESET:
3421e1ddd6cScth 		case CMD_ABORTED:
3431e1ddd6cScth 		case CMD_TRAN_ERR:
3441e1ddd6cScth 			/*
3451e1ddd6cScth 			 * Increased the number of retries when these error
3461e1ddd6cScth 			 * cases are encountered.  Also added a 1 sec wait
3471e1ddd6cScth 			 * before retrying.
3481e1ddd6cScth 			 */
3491e1ddd6cScth 			if (retry_cmd_cnt++ < STD_FO_MAX_CMD_RETRIES) {
3501e1ddd6cScth 				drv_usecwait(STD_FO_CMD_RETRY_DELAY);
3511e1ddd6cScth 				VHCI_DEBUG(4, (CE_WARN, NULL,
3521e1ddd6cScth 				    "!Retrying path activation due to "
3531e1ddd6cScth 				    "pkt reason:%x, retry cnt:%d",
3541e1ddd6cScth 				    pkt->pkt_reason, retry_cmd_cnt));
3551e1ddd6cScth 				goto retry;
3561e1ddd6cScth 			}
3571e1ddd6cScth 			/* FALLTHROUGH */
3581e1ddd6cScth 		default:
3591e1ddd6cScth 			cmn_err(CE_WARN, "!Path activation did not "
3601e1ddd6cScth 			    "complete successfully,"
3611e1ddd6cScth 			    "(pkt reason %x)", pkt->pkt_reason);
3621e1ddd6cScth 			retval = 1;
3631e1ddd6cScth 			break;
3641e1ddd6cScth 	}
3651e1ddd6cScth 
3661e1ddd6cScth 	scsi_destroy_pkt(pkt);
3671e1ddd6cScth 	scsi_free_consistent_buf(bp);
3681e1ddd6cScth 	return (retval);
3691e1ddd6cScth }
3701e1ddd6cScth 
3711e1ddd6cScth /* ARGSUSED */
std_path_deactivate(struct scsi_device * sd,char * pathclass,void * ctpriv)3721e1ddd6cScth static int std_path_deactivate(struct scsi_device *sd, char *pathclass,
3730205780bSrralphs     void *ctpriv)
3741e1ddd6cScth {
3751e1ddd6cScth 	return (0);
3761e1ddd6cScth }
3771e1ddd6cScth 
3781e1ddd6cScth /* ARGSUSED */
3791e1ddd6cScth static int
std_path_get_opinfo(struct scsi_device * sd,struct scsi_path_opinfo * opinfo,void * ctpriv)3800205780bSrralphs std_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo,
3810205780bSrralphs     void *ctpriv)
3821e1ddd6cScth {
3831e1ddd6cScth 	int			mode, preferred, state, xlf;
3841e1ddd6cScth 
3851e1ddd6cScth 	opinfo->opinfo_rev = OPINFO_REV;
3861e1ddd6cScth 
3870205780bSrralphs 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
3881e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_getopinfo:"
3890205780bSrralphs 		    " failed vhci_tpgs_get_target_fo_mode\n"));
3901e1ddd6cScth 		return (1);
3911e1ddd6cScth 	}
3921e1ddd6cScth 
3931e1ddd6cScth 	if (state == STD_ACTIVE_OPTIMIZED) {
3941e1ddd6cScth 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE;
3951e1ddd6cScth 	} else if (state == STD_ACTIVE_NONOPTIMIZED) {
3961e1ddd6cScth 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE_NONOPT;
3971e1ddd6cScth 	} else if (state == STD_STANDBY) {
3981e1ddd6cScth 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
3991e1ddd6cScth 	} else if (state == STD_UNAVAILABLE) {
4001e1ddd6cScth 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
4011e1ddd6cScth 	}
4021e1ddd6cScth 	if (preferred) {
4031e1ddd6cScth 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_PRIMARY);
4041e1ddd6cScth 	} else {
4051e1ddd6cScth 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_SECONDARY);
4061e1ddd6cScth 	}
4071e1ddd6cScth 	VHCI_DEBUG(4, (CE_NOTE, NULL, "std_path_get_opinfo: "
4081e1ddd6cScth 	    "class: %s state: %s\n", opinfo->opinfo_path_attr,
4091e1ddd6cScth 	    opinfo->opinfo_path_state == SCSI_PATH_ACTIVE ?
4101e1ddd6cScth 	    "ACTIVE" : "INACTIVE"));
4111e1ddd6cScth 	opinfo->opinfo_xlf_capable = 0;
4121e1ddd6cScth 	opinfo->opinfo_pswtch_best = 30;
4131e1ddd6cScth 	opinfo->opinfo_pswtch_worst = 3*30;
4141e1ddd6cScth 	opinfo->opinfo_preferred = (uint16_t)preferred;
4151e1ddd6cScth 	opinfo->opinfo_mode = (uint16_t)mode;
4161e1ddd6cScth 
4171e1ddd6cScth 	return (0);
4181e1ddd6cScth }
4191e1ddd6cScth 
4201e1ddd6cScth /* ARGSUSED */
std_path_ping(struct scsi_device * sd,void * ctpriv)4211e1ddd6cScth static int std_path_ping(struct scsi_device *sd, void *ctpriv)
4221e1ddd6cScth {
4231e1ddd6cScth 	/*
4241e1ddd6cScth 	 * For future use
4251e1ddd6cScth 	 */
4261e1ddd6cScth 	return (1);
4271e1ddd6cScth }
4281e1ddd6cScth 
4291e1ddd6cScth /*
4301e1ddd6cScth  * Analyze the sense code to determine whether failover process
4311e1ddd6cScth  */
4321e1ddd6cScth /* ARGSUSED */
4331e1ddd6cScth static int
std_analyze_sense(struct scsi_device * sd,uint8_t * sense,void * ctpriv)4340c45178bSwl std_analyze_sense(struct scsi_device *sd, uint8_t *sense,
4350205780bSrralphs     void *ctpriv)
4361e1ddd6cScth {
4371e1ddd6cScth 	int rval = SCSI_SENSE_UNKNOWN;
4381e1ddd6cScth 
4390c45178bSwl 	uint8_t skey, asc, ascq;
4400c45178bSwl 
4410c45178bSwl 	skey = scsi_sense_key(sense);
4420c45178bSwl 	asc = scsi_sense_asc(sense);
4430c45178bSwl 	ascq = scsi_sense_ascq(sense);
4440c45178bSwl 
4450c45178bSwl 	if ((skey == KEY_UNIT_ATTENTION) &&
4460c45178bSwl 	    (asc == STD_SCSI_ASC_STATE_CHG) &&
4470c45178bSwl 	    (ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
4481e1ddd6cScth 		rval = SCSI_SENSE_STATE_CHANGED;
4491e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
4501e1ddd6cScth 		    " sense_key:%x, add_code: %x, qual_code:%x"
4510c45178bSwl 		    " sense:%x\n", skey, asc, ascq, rval));
4520c45178bSwl 	} else if ((skey == KEY_NOT_READY) &&
4530c45178bSwl 	    (asc == STD_LOGICAL_UNIT_NOT_ACCESSIBLE) &&
45437711685Swl 	    ((ascq == STD_TGT_PORT_UNAVAILABLE) ||
45537711685Swl 	    (ascq == STD_TGT_PORT_STANDBY))) {
4561e1ddd6cScth 		rval = SCSI_SENSE_INACTIVE;
4571e1ddd6cScth 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
4581e1ddd6cScth 		    " sense_key:%x, add_code: %x, qual_code:%x"
4590c45178bSwl 		    " sense:%x\n", skey, asc, ascq, rval));
4600c45178bSwl 	} else if ((skey == KEY_ILLEGAL_REQUEST) &&
4610c45178bSwl 	    (asc == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
4621e1ddd6cScth 		rval = SCSI_SENSE_NOFAILOVER;
4631e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
4641e1ddd6cScth 		    " sense_key:%x, add_code: %x, qual_code:%x"
4650c45178bSwl 		    " sense:%x\n", skey, asc, ascq, rval));
4660c45178bSwl 	} else if ((skey == KEY_ILLEGAL_REQUEST) &&
4670c45178bSwl 	    (asc == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
4681e1ddd6cScth 		rval = SCSI_SENSE_NOFAILOVER;
4691e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
4701e1ddd6cScth 		    " sense_key:%x, add_code: %x, qual_code:%x"
4710c45178bSwl 		    " sense:%x\n", skey, asc, ascq, rval));
4721e1ddd6cScth 	} else {
4731e1ddd6cScth 		/*
4741e1ddd6cScth 		 * At this point sense data may be for power-on-reset
4751e1ddd6cScth 		 * UNIT ATTN hardware errors, vendor unqiue sense data etc.
4761e1ddd6cScth 		 * For all these cases, return SCSI_SENSE_UNKNOWN.
4771e1ddd6cScth 		 */
4781e1ddd6cScth 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Analyze sense UNKNOWN:"
4790c45178bSwl 		    " sense key:%x, ASC:%x, ASCQ:%x\n", skey, asc, ascq));
4801e1ddd6cScth 	}
4811e1ddd6cScth 
4821e1ddd6cScth 	return (rval);
4831e1ddd6cScth }
4841e1ddd6cScth 
4851e1ddd6cScth /* ARGSUSED */
4861e1ddd6cScth static int
std_pathclass_next(char * cur,char ** nxt,void * ctpriv)4871e1ddd6cScth std_pathclass_next(char *cur, char **nxt, void *ctpriv)
4881e1ddd6cScth {
4891e1ddd6cScth 	/*
4901e1ddd6cScth 	 * The first phase does not have a standby path so
4911e1ddd6cScth 	 * there will be no explicit failover - when standard tpgs.
4921e1ddd6cScth 	 * standard defines preferred flag then we should start
4931e1ddd6cScth 	 * using this as the selection mechanism - there can be
4941e1ddd6cScth 	 * preferred primary standby that we should fail to first and then
4951e1ddd6cScth 	 * nonpreferred secondary standby.
4961e1ddd6cScth 	 */
4971e1ddd6cScth 	if (cur == NULL) {
4981e1ddd6cScth 		*nxt = PCLASS_PRIMARY;
4991e1ddd6cScth 		return (0);
5001e1ddd6cScth 	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
5011e1ddd6cScth 		*nxt = PCLASS_SECONDARY;
5021e1ddd6cScth 		return (0);
5031e1ddd6cScth 	} else if (strcmp(cur, PCLASS_SECONDARY) == 0) {
5041e1ddd6cScth 		return (ENOENT);
5051e1ddd6cScth 	} else {
5061e1ddd6cScth 		return (EINVAL);
5071e1ddd6cScth 	}
5081e1ddd6cScth }
509