xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c (revision 437220cd296f6d8b6654d6d52508b40b1e2d1ac7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 /*
28  * Implementation of "scsi_vhci_f_tpgs" T10 standard based failover_ops.
29  *
30  * NOTE: for non-sequential devices only.
31  */
32 
33 #include <sys/conf.h>
34 #include <sys/file.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/scsi/scsi.h>
38 #include <sys/scsi/adapters/scsi_vhci.h>
39 
40 /* Supported device table entries.  */
41 char	*std_dev_table[] = { NULL };
42 
43 /* Failover module plumbing. */
44 SCSI_FAILOVER_OP(SFO_NAME_TPGS, std, "%I%");
45 
46 #define	STD_SCSI_CMD_LEN 0xff
47 
48 #define	STD_FO_CMD_RETRY_DELAY	1000000 /* 1 seconds */
49 #define	STD_FO_RETRY_DELAY	2000000 /* 2 seconds */
50 /*
51  * max time for failover to complete is 3 minutes.  Compute
52  * number of retries accordingly, to ensure we wait for at least
53  * 3 minutes
54  */
55 #define	STD_FO_MAX_RETRIES	(3*60*1000000)/STD_FO_RETRY_DELAY
56 
57 /*
58  * max number of retries for std failover to complete where the ping
59  * command is failing due to transport errors or commands being rejected by
60  * std.
61  * STD_FO_MAX_RETRIES takes into account the case where CMD_CMPLTs but
62  * std takes time to complete the failover.
63  */
64 #define	STD_FO_MAX_CMD_RETRIES	3
65 
66 #define	STD_ACTIVE_OPTIMIZED    0x0
67 #define	STD_ACTIVE_NONOPTIMIZED 0x1
68 #define	STD_STANDBY		0x2
69 #define	STD_UNAVAILABLE		0x3
70 #define	STD_TRANSITIONING	0xf
71 
72 #define	STD_SCSI_TPG_SERVICE_ACTION	0x0A
73 #define	STD_SCSI_ASC_STATE_TRANS	0x04
74 #define	STD_SCSI_ASCQ_STATE_TRANS_FAIL  0x0A
75 #define	STD_SCSI_ASC_STATE_CHG		0x2A
76 #define	STD_SCSI_ASCQ_STATE_CHG_SUCC	0x06
77 #define	STD_SCSI_ASCQ_STATE_CHG_FAILED	0x07
78 #define	STD_SCSI_ASC_INVAL_PARAM_LIST	0x26
79 #define	STD_SCSI_ASC_INVAL_CMD_OPCODE	0x20
80 #define	STD_LOGICAL_UNIT_NOT_ACCESSIBLE	0x04
81 #define	STD_TGT_PORT_UNAVAILABLE	0x0C
82 
83 #define	SCMD_REPORT_TARGET_PORT_GROUP	0xA3;
84 #define	SCMD_SET_TARGET_PORT_GROUP	0xA4;
85 
86 /* Special exported for direct use by MP-API */
87 int std_set_target_groups(struct scsi_address *, int, int);
88 
89 /*
90  * External function definitions
91  */
92 extern void vhci_mpapi_update_tpg_data(struct scsi_address *, char *);
93 
94 static int std_get_fo_mode(struct scsi_device *,
95 		int *, int *, int *, int *);
96 static int std_report_target_groups(struct scsi_address *, struct buf *,
97 		int, int, int *, int *);
98 
99 /* ARGSUSED */
100 static int
101 std_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq,
102 void **ctpriv)
103 {
104 	unsigned int	tpgs_bits;
105 	unsigned char	*inqbuf = (unsigned char *)inq;
106 	unsigned char	dtype = (inq->inq_dtype & DTYPE_MASK);
107 
108 	int		mode, state, xlf, preferred = 0;
109 
110 	VHCI_DEBUG(6, (CE_NOTE, NULL, "std_device_probe: vidpid %s\n",
111 	    inq->inq_vid));
112 
113 	tpgs_bits = ((inqbuf[5] & 0x30) >> 4);
114 
115 	if (tpgs_bits == 0) {
116 		VHCI_DEBUG(4, (CE_WARN, NULL,
117 		    "!std_device_probe: not a standard tpgs device"));
118 		return (SFO_DEVICE_PROBE_PHCI);
119 	}
120 
121 	if (dtype == DTYPE_SEQUENTIAL) {
122 		VHCI_DEBUG(4, (CE_NOTE, NULL,
123 		    "!std_device_probe: Detected a "
124 		    "Standard Asymmetric device "
125 		    "not yet supported\n"));
126 		return (SFO_DEVICE_PROBE_PHCI);
127 	}
128 
129 	if (std_get_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
130 		VHCI_DEBUG(4, (CE_WARN, NULL, "!unable to fetch fo "
131 		    "mode: sd(%p)", (void *) sd));
132 		return (SFO_DEVICE_PROBE_PHCI);
133 	}
134 
135 	if (tpgs_bits == SCSI_IMPLICIT_FAILOVER) {
136 		VHCI_DEBUG(1, (CE_NOTE, NULL,
137 		    "!std_device_probe: Detected a "
138 		    "Standard Asymmetric device "
139 		    "with implicit failover\n"));
140 		return (SFO_DEVICE_PROBE_VHCI);
141 	}
142 	if (tpgs_bits == SCSI_EXPLICIT_FAILOVER) {
143 		VHCI_DEBUG(1, (CE_NOTE, NULL,
144 		    "!std_device_probe: Detected a "
145 		    "Standard Asymmetric device "
146 		    "with explicit failover\n"));
147 		return (SFO_DEVICE_PROBE_VHCI);
148 	}
149 	if (tpgs_bits == SCSI_BOTH_FAILOVER) {
150 		VHCI_DEBUG(1, (CE_NOTE, NULL,
151 		    "!std_device_probe: Detected a "
152 		    "Standard Asymmetric device "
153 		    "which supports both implicit and explicit failover\n"));
154 		return (SFO_DEVICE_PROBE_VHCI);
155 	}
156 	VHCI_DEBUG(1, (CE_WARN, NULL,
157 	    "!std_device_probe: "
158 	    "Unknown tpgs_bits: %x", tpgs_bits));
159 	return (SFO_DEVICE_PROBE_PHCI);
160 }
161 
162 /* ARGSUSED */
163 static void
164 std_device_unprobe(struct scsi_device *sd, void *ctpriv)
165 {
166 	/*
167 	 * For future use
168 	 */
169 }
170 
171 static int
172 std_inquiry(struct scsi_address *ap, struct buf *bp, int *mode)
173 {
174 	struct scsi_pkt		*pkt;
175 	char			buf[STD_SCSI_CMD_LEN];
176 	int			buf_size = sizeof (buf);
177 	unsigned int		tpgs_bits;
178 	int			retval;
179 
180 	*mode = 0;
181 	bp->b_un.b_addr = (caddr_t)&buf;
182 	bp->b_flags = B_READ;
183 	bp->b_bcount = buf_size;
184 	bp->b_resid = 0;
185 
186 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
187 	    sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC, NULL);
188 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
189 	pkt->pkt_cdbp[4] = (unsigned char)buf_size;
190 	pkt->pkt_time = 60;
191 
192 	retval = vhci_do_scsi_cmd(pkt);
193 	scsi_destroy_pkt(pkt);
194 	if (retval == 0) {
195 		VHCI_DEBUG(1, (CE_WARN, NULL,
196 		    "!std_inquiry: Failure returned from vhci_do_scsi_cmd"));
197 		return (1);
198 	}
199 
200 	tpgs_bits = ((buf[5] & 0x30) >> 4);
201 	if (tpgs_bits == 0) {
202 		VHCI_DEBUG(1, (CE_WARN, NULL,
203 		    "!std_inquiry: zero tpgs_bits"));
204 		return (1);
205 	}
206 	retval = 0;
207 	if (tpgs_bits == SCSI_IMPLICIT_FAILOVER) {
208 		*mode = SCSI_IMPLICIT_FAILOVER;
209 	} else if (tpgs_bits == SCSI_EXPLICIT_FAILOVER) {
210 		*mode = SCSI_EXPLICIT_FAILOVER;
211 	} else if (tpgs_bits == SCSI_BOTH_FAILOVER) {
212 		*mode = SCSI_BOTH_FAILOVER;
213 	} else {
214 		VHCI_DEBUG(1, (CE_WARN, NULL,
215 		    "!std_inquiry: Illegal mode returned: %x mode: %x",
216 		    tpgs_bits, *mode));
217 		retval = 1;
218 	}
219 
220 	return (retval);
221 }
222 
223 static int
224 std_page83(struct scsi_address *ap, struct buf *bp,
225 	int *rel_tgt_port, int *tgt_port, int *lu)
226 {
227 	char			*ptr, *end;
228 	struct scsi_pkt		*pkt;
229 	char			*bufp;
230 	unsigned int		buf_len, rx_bsize;
231 
232 	/*
233 	 * lets start the buf size with 512 bytes. If this
234 	 * if found to be insufficient, we can allocate
235 	 * appropriate size in the next iteration.
236 	 */
237 	buf_len = 512;
238 
239 once_again:
240 	bufp = kmem_zalloc(buf_len, KM_NOSLEEP);
241 	if (bufp == NULL) {
242 		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_page83: "
243 		    "request packet allocation for %d failed....",
244 		    buf_len));
245 		return (1);
246 	}
247 
248 
249 	bp->b_un.b_addr = bufp;
250 	bp->b_flags = B_READ;
251 	bp->b_bcount = buf_len;
252 	bp->b_resid = 0;
253 
254 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
255 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
256 	if (pkt == NULL) {
257 		VHCI_DEBUG(1, (CE_WARN, NULL,
258 		    "!std_page83: Failure returned from scsi_init_pkt"));
259 		kmem_free((void *)bufp, buf_len);
260 		return (1);
261 	}
262 
263 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
264 	pkt->pkt_cdbp[1] = 0x1;
265 	pkt->pkt_cdbp[2] = 0x83;
266 	pkt->pkt_cdbp[3] = (unsigned char)((buf_len >> 8) & 0xff);
267 	pkt->pkt_cdbp[4] = (unsigned char)(buf_len & 0xff);
268 	pkt->pkt_time = 90;
269 
270 	if (vhci_do_scsi_cmd(pkt) == 0) {
271 		VHCI_DEBUG(1, (CE_NOTE, NULL,
272 		    "!std_page83: vhci_do_scsi_cmd failed\n"));
273 		kmem_free((void *)bufp, buf_len);
274 		scsi_destroy_pkt(pkt);
275 		return (1);
276 	}
277 
278 	/*
279 	 * Now lets check if the size that was provided was
280 	 * sufficient. If not, allocate the appropriate size
281 	 * and retry the command again.
282 	 */
283 	rx_bsize = (((bufp[2] & 0xff) << 8) | (bufp[3] & 0xff));
284 	rx_bsize += 4;
285 	if (rx_bsize > buf_len) {
286 		/*
287 		 * Need to allocate more buf and retry again
288 		 */
289 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_page83: "
290 		    "bufsize: %d greater than allocated buf: %d\n",
291 		    rx_bsize, buf_len));
292 		VHCI_DEBUG(1, (CE_NOTE, NULL, "Retrying for size %d\n",
293 		    rx_bsize));
294 		kmem_free((void *)bufp, buf_len);
295 		buf_len = (unsigned int)(rx_bsize);
296 		goto once_again;
297 	}
298 
299 	ptr = bufp;
300 	ptr += 4; /* identification descriptor 0 */
301 	end = bufp + rx_bsize;
302 	while (ptr < end) {
303 		VHCI_DEBUG(1, (CE_NOTE, NULL, "std_page83: desc[1/4/5/6/7]:"
304 		    "%x %x %x %x %x\n",
305 		    ptr[1], ptr[4], ptr[5], ptr[6], ptr[7]));
306 		if ((ptr[1] & 0x0f) == 0x04) {
307 			*rel_tgt_port = 0;
308 			*rel_tgt_port |= ((ptr[6] & 0xff) << 8);
309 			*rel_tgt_port |= (ptr[7] & 0xff);
310 			VHCI_DEBUG(1, (CE_NOTE, NULL,
311 			    "!std_page83: relative target port: %x\n",
312 			    *rel_tgt_port));
313 		} else if ((ptr[1] & 0x0f) == 0x05) {
314 			*tgt_port = 0;
315 			*tgt_port = ((ptr[6] & 0xff) << 8);
316 			*tgt_port |= (ptr[7] & 0xff);
317 			VHCI_DEBUG(1, (CE_NOTE, NULL,
318 			    "!std_page83: target port: %x\n", *tgt_port));
319 		} else if ((ptr[1] & 0x0f) == 0x06) {
320 			*lu = 0;
321 			*lu |= ((ptr[6] & 0xff)<< 8);
322 			*lu |= (ptr[7] & 0xff);
323 			VHCI_DEBUG(1, (CE_NOTE, NULL,
324 			    "!std_page83: logical unit: %x\n", *lu));
325 		}
326 		ptr += ptr[3] + 4;  /* next identification descriptor */
327 	}
328 	kmem_free((void *)bufp, buf_len);
329 	scsi_destroy_pkt(pkt);
330 	return (0);
331 }
332 
333 #ifdef DEBUG
334 static void
335 print_buf(char *buf, int buf_size)
336 {
337 	int		i = 0, j;
338 	int		loop, left;
339 
340 	loop = buf_size / 8;
341 	left = buf_size % 8;
342 
343 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!buf_size: %x loop: %x left: %x",
344 	    buf_size, loop, left));
345 
346 	for (j = 0; j < loop; j++) {
347 		VHCI_DEBUG(4, (CE_NOTE, NULL,
348 		    "!buf[%d-%d]: %x %x %x %x %x %x %x %x",
349 		    i, i + 7, buf[i], buf[i+1], buf[i+2], buf[i+3],
350 		    buf[i+4], buf[i+5], buf[i+6], buf[i+7]));
351 		i += 8;
352 	}
353 
354 	if (left) {
355 		VHCI_DEBUG(4, (CE_CONT, NULL,
356 		    "NOTICE: buf[%d-%d]:", i, i + left));
357 		for (j = 0; j < left; j++) {
358 			VHCI_DEBUG(4, (CE_CONT, NULL, " %x", buf[i + j]));
359 		}
360 		VHCI_DEBUG(4, (CE_CONT, NULL, "\n"));
361 	}
362 }
363 #endif
364 
365 static int
366 std_report_target_groups(struct scsi_address *ap, struct buf *bp,
367 	int rel_tgt_port, int tgt_port, int *pstate, int *preferred)
368 {
369 	struct scsi_pkt		*pkt;
370 	char			*ptr, *end, *bufp, *mpapi_ptr;
371 	unsigned int		rtpg_len = 0;
372 	unsigned int		l_tgt_port = 0, tpgs_state = 0;
373 	unsigned int		tgt_port_cnt = 0, lr_tgt_port = 0;
374 	int			i, len;
375 
376 	/*
377 	 * Start with buffer size of 512.
378 	 * If this is found to be insufficient, required size
379 	 * will be allocated and the command will be retried.
380 	 */
381 	len = 512;
382 
383 try_again:
384 	bufp = kmem_zalloc(len, KM_NOSLEEP);
385 	if (bufp == NULL) {
386 		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_report_target_groups: "
387 		    "request packet allocation for %d failed....",
388 		    len));
389 		return (1);
390 	}
391 
392 	bp->b_un.b_addr = bufp;
393 	bp->b_flags = B_READ;
394 	bp->b_bcount = len;
395 	bp->b_resid = 0;
396 
397 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
398 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
399 
400 	if (pkt == NULL) {
401 		VHCI_DEBUG(1, (CE_NOTE, NULL,
402 		    "!std_report_target_groups: scsi_init_pkt error\n"));
403 		kmem_free((void *)bufp, len);
404 		return (1);
405 	}
406 
407 	pkt->pkt_cdbp[0] = SCMD_REPORT_TARGET_PORT_GROUP;
408 	pkt->pkt_cdbp[1] = STD_SCSI_TPG_SERVICE_ACTION;
409 	pkt->pkt_cdbp[6] = ((len >>  24) & 0xff);
410 	pkt->pkt_cdbp[7] = ((len >> 16) & 0xff);
411 	pkt->pkt_cdbp[8] = ((len >> 8) & 0xff);
412 	pkt->pkt_cdbp[9] = len & 0xff;
413 	pkt->pkt_time = 90;
414 
415 	VHCI_DEBUG(6, (CE_NOTE, NULL,
416 	    "!std_report_target_groups: sending target port group:"
417 	    " cdb[6/7/8/9]: %x/%x/%x/%x\n", pkt->pkt_cdbp[6],
418 	    pkt->pkt_cdbp[7], pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
419 	if (vhci_do_scsi_cmd(pkt) == 0) {
420 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_target_groups:"
421 		    " vhci_do_scsi_cmd failed\n"));
422 		kmem_free((void *)bufp, len);
423 		scsi_destroy_pkt(pkt);
424 		return (1);
425 	}
426 	ptr = bufp;
427 	VHCI_DEBUG(6, (CE_NOTE, NULL, "!std_report_target_groups:"
428 	    " returned from target"
429 	    " port group: buf[0/1/2/3]: %x/%x/%x/%x\n",
430 	    ptr[0], ptr[1], ptr[2], ptr[3]));
431 	rtpg_len = (unsigned int)((0xff & ptr[0]) << 24);
432 	rtpg_len |= (unsigned int)((0xff & ptr[1]) << 16);
433 	rtpg_len |= (unsigned int)((0xff & ptr[2]) << 8);
434 	rtpg_len |= (unsigned int)(0xff & ptr[3]);
435 	rtpg_len += 4;
436 	if (rtpg_len > len) {
437 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_target_groups: "
438 		    "bufsize: %d greater than allocated buf: %d\n",
439 		    rtpg_len, len));
440 		VHCI_DEBUG(4, (CE_NOTE, NULL, "Retrying for size %d\n",
441 		    rtpg_len));
442 		kmem_free((void *)bufp, len);
443 		len = (unsigned int)(rtpg_len + 1);
444 		goto try_again;
445 	}
446 #ifdef DEBUG
447 	print_buf(bufp, rtpg_len);
448 #endif
449 	end = ptr + rtpg_len;
450 	ptr += 4;
451 	while (ptr < end) {
452 		mpapi_ptr = ptr;
453 		l_tgt_port = ((ptr[2] & 0xff) << 8) + (ptr[3] & 0xff);
454 		tpgs_state = ptr[0] & 0x0f;
455 		tgt_port_cnt = (ptr[7] & 0xff);
456 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_tgt_groups:"
457 		    " tpgs state: %x"
458 		    " tgt_group: %x count: %x\n", tpgs_state,
459 		    l_tgt_port, tgt_port_cnt));
460 		ptr += 8;
461 		for (i = 0; i < tgt_port_cnt; i++) {
462 			lr_tgt_port = 0;
463 			lr_tgt_port |= ((ptr[2] & 0Xff) << 8);
464 			lr_tgt_port |= (ptr[3] & 0xff);
465 
466 			if ((lr_tgt_port == rel_tgt_port) &&
467 			    (l_tgt_port == tgt_port)) {
468 				VHCI_DEBUG(4, (CE_NOTE, NULL,
469 				    "!std_report_tgt_groups:"
470 				    " found tgt_port: %x rel_tgt_port:%x"
471 				    " tpgs_state: %x\n", tgt_port, rel_tgt_port,
472 				    tpgs_state));
473 				/*
474 				 * once we have the preferred flag
475 				 * and a non-optimized state flag
476 				 * we will get preferred flag  from the
477 				 * report target groups
478 				 */
479 				if (tpgs_state == STD_ACTIVE_OPTIMIZED) {
480 					*pstate = STD_ACTIVE_OPTIMIZED;
481 					*preferred = PCLASS_PREFERRED;
482 				} else if (tpgs_state ==
483 				    STD_ACTIVE_NONOPTIMIZED) {
484 					*pstate = STD_ACTIVE_NONOPTIMIZED;
485 					*preferred = PCLASS_NONPREFERRED;
486 				} else if (tpgs_state == STD_STANDBY) {
487 					*pstate = STD_STANDBY;
488 					*preferred = PCLASS_NONPREFERRED;
489 				} else {
490 					*pstate = STD_UNAVAILABLE;
491 					*preferred = PCLASS_NONPREFERRED;
492 				}
493 				vhci_mpapi_update_tpg_data(ap, mpapi_ptr);
494 				kmem_free((void *)bufp, len);
495 				scsi_destroy_pkt(pkt);
496 				return (0);
497 			}
498 			VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_tgt_groups:"
499 			    " tgt_port: %x rel_tgt_port:%x\n", tgt_port,
500 			    rel_tgt_port));
501 			ptr += 4;
502 		}
503 	}
504 	*pstate = SCSI_PATH_INACTIVE;
505 	*preferred = PCLASS_NONPREFERRED;
506 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_report_tgt_groups: "
507 	    "NO rel_TGTPRT MATCH!!! Assigning Default: state: %x "
508 	    "preferred: %d\n", *pstate, *preferred));
509 	kmem_free((void *)bufp, len);
510 	scsi_destroy_pkt(pkt);
511 	return (1);
512 }
513 
514 /*
515  * get the failover mode, ownership and if it has extended failover
516  * capability. The mode(bits5-4/byte5) is defined as implicit, explicit, or
517  * both.  The state is defined as online-optimized(0h),
518  * online-nonoptimized(1h), standby(2h), offline(3h),
519  * and transitioning(fh). Currently, there is online,
520  * standby, and offline(defined in sunmdi.h).
521  * Online-nonoptimized will be a mode of secondary
522  * and an ownership of online. Thought about using a different mode but
523  * it appears the states are really for the states for secondary mode.
524  * We currently have IS_ONLINING, IS_OFFLINING - should we have TRANSITIONING
525  * to mean from online-optimized to online-nonoptimized or does onlining
526  * cover this?
527  */
528 /* ARGSUSED */
529 static int
530 std_get_fo_mode(struct scsi_device *sd, int *mode,
531     int *state, int *xlf_capable, int *preferred)
532 {
533 	int			retval = 0;
534 	struct buf		*bp;
535 	struct scsi_address	*ap;
536 	int			lu = 0, rel_tgt_port = 0, tgt_port = 0x0;
537 
538 	VHCI_DEBUG(6, (CE_NOTE, NULL, "!std_get_fo_mode: enter\n"));
539 	*mode = *state = *xlf_capable = 0;
540 	bp = getrbuf(KM_NOSLEEP);
541 	if (bp == NULL) {
542 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
543 		    " failed getrbuf\n"));
544 		return (1);
545 	}
546 
547 	ap = &sd->sd_address;
548 	if (std_inquiry(ap, bp, mode)) {
549 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
550 		    " failed std_inquiry\n"));
551 		retval = 1;
552 	} else if (std_page83(ap, bp, &rel_tgt_port, &tgt_port, &lu)) {
553 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
554 		    " failed std_page83\n"));
555 		retval = 1;
556 	} else if (std_report_target_groups(ap, bp, rel_tgt_port, tgt_port,
557 	    state, preferred)) {
558 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
559 		    " failed std_report_target_groups\n"));
560 		retval = 1;
561 	}
562 
563 	freerbuf(bp);
564 	if (retval == 0) {
565 		VHCI_DEBUG(6, (CE_NOTE, NULL, "!std_get_fo_mode: "
566 		    "SUCCESS\n"));
567 	}
568 	return (retval);
569 }
570 
571 /* ARGSUSED */
572 static int
573 std_activate_explicit(struct scsi_device *sd, int xlf_capable)
574 {
575 	cmn_err(CE_NOTE, "Explicit Activation is done by "
576 	    "std_set_target_groups() call from MPAPI");
577 	return (1);
578 }
579 
580 /*
581  * Process the packet reason of CMD_PKT_CMPLT - return 0 if no
582  * retry and 1 if a retry should be done
583  */
584 static int
585 std_process_cmplt_pkt(struct scsi_device *sd, struct scsi_pkt *pkt,
586 	int *retry_cnt)
587 {
588 	struct scsi_extended_sense	*sns;
589 
590 	/*
591 	 * Re-initialize retry_cmd_cnt. Allow transport and
592 	 * cmd errors to go through a full retry count when
593 	 * these are encountered.  This way TRAN/CMD errors
594 	 * retry count is not exhausted due to CMD_CMPLTs
595 	 * delay. This allows the system
596 	 * to brave a hick-up on the link at any given time,
597 	 * while waiting for the fo to complete.
598 	 */
599 	if (pkt->pkt_state & STATE_ARQ_DONE) {
600 		sns = &(((struct scsi_arq_status *)(uintptr_t)
601 		    (pkt->pkt_scbp))->sts_sensedata);
602 		if (sns->es_key == KEY_UNIT_ATTENTION) {
603 			/*
604 			 * tpgs access state changed
605 			 */
606 			if (sns->es_add_code == STD_SCSI_ASC_STATE_CHG &&
607 			    sns->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC) {
608 				/* XXX: update path info? */
609 				cmn_err(CE_WARN, "!Device failover"
610 				    " state change");
611 			}
612 			return (1);
613 		} else if (sns->es_key == KEY_NOT_READY) {
614 			if ((*retry_cnt)++ >=
615 			    STD_FO_MAX_RETRIES) {
616 				cmn_err(CE_WARN, "!Device failover"
617 				    " failed: timed out waiting "
618 				    "for path to become active");
619 				return (0);
620 			}
621 			VHCI_DEBUG(6, (CE_NOTE, NULL,
622 			    "!(sd:%p)lun "
623 			    "becoming active...\n", (void *)sd));
624 			drv_usecwait(STD_FO_RETRY_DELAY);
625 			return (1);
626 		}
627 		cmn_err(CE_NOTE, "!Failover failed;"
628 		    " sense key:%x, ASC: %x, "
629 		    "ASCQ:%x", sns->es_key,
630 		    sns->es_add_code, sns->es_qual_code);
631 		return (0);
632 	}
633 	switch (SCBP_C(pkt)) {
634 		case STATUS_GOOD:
635 			break;
636 		case STATUS_CHECK:
637 			VHCI_DEBUG(4, (CE_WARN, NULL,
638 			    "!(sd:%p):"
639 			    " status returned CHECK during std"
640 			    " path activation", (void *)sd));
641 			return (0);
642 		case STATUS_QFULL:
643 			VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL "
644 			    "status returned QFULL during std "
645 			    "path activation for %p\n", (void *)sd));
646 			drv_usecwait(5000);
647 			return (1);
648 		case STATUS_BUSY:
649 			VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY "
650 			    "status returned BUSY during std "
651 			    "path activation for %p\n", (void *)sd));
652 			drv_usecwait(5000);
653 			return (1);
654 		default:
655 			VHCI_DEBUG(4, (CE_WARN, NULL,
656 			    "!(sd:%p) Bad status returned during std "
657 			    "activation (pkt %p, status %x)",
658 			    (void *)sd, (void *)pkt, SCBP_C(pkt)));
659 			return (0);
660 	}
661 	return (0);
662 }
663 
664 /*
665  * For now we are going to use primary/online and secondary/online.
666  * There is no standby path returned by the dsp and we may have
667  * to do something different for other devices that use standby
668  */
669 /* ARGSUSED */
670 static int
671 std_path_activate(struct scsi_device *sd, char *pathclass,
672 void *ctpriv)
673 {
674 	struct buf			*bp;
675 	struct scsi_pkt			*pkt;
676 	struct scsi_address		*ap;
677 	int				err, retry_cnt, retry_cmd_cnt;
678 	int				mode, state, retval, xlf, preferred;
679 
680 	ap = &sd->sd_address;
681 
682 	mode = state = 0;
683 
684 	if (std_get_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
685 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_activate:"
686 		    " failed std_get_fo_mode\n"));
687 		return (1);
688 	}
689 	if ((state == STD_ACTIVE_OPTIMIZED) ||
690 	    (state == STD_ACTIVE_NONOPTIMIZED)) {
691 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for %p\n",
692 		    (void *)sd));
693 		return (0);
694 	}
695 
696 	if (mode != SCSI_IMPLICIT_FAILOVER) {
697 		VHCI_DEBUG(4, (CE_NOTE, NULL,
698 		    "!mode is EXPLICIT for %p xlf %x\n",
699 		    (void *)sd, xlf));
700 		retval = std_activate_explicit(sd, xlf);
701 		if (retval != 0) {
702 			VHCI_DEBUG(4, (CE_NOTE, NULL,
703 			    "!(sd:%p)std_path_activate failed(1)\n",
704 			    (void *)sd));
705 			return (1);
706 		}
707 	} else {
708 		VHCI_DEBUG(4, (CE_NOTE, NULL, "STD mode is IMPLICIT for %p\n",
709 		    (void *)sd));
710 	}
711 
712 	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, DEV_BSIZE,
713 	    B_READ, NULL, NULL);
714 	if (!bp) {
715 		VHCI_DEBUG(4, (CE_WARN, NULL,
716 		    "!(sd:%p)std_path_activate failed to alloc buffer",
717 		    (void *)sd));
718 		return (1);
719 	}
720 
721 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1,
722 	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
723 	if (!pkt) {
724 		VHCI_DEBUG(4, (CE_WARN, NULL,
725 		    "!(sd:%p)std_path_activate failed to initialize packet",
726 		    (void *)sd));
727 		scsi_free_consistent_buf(bp);
728 		return (1);
729 	}
730 
731 	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
732 	    SCMD_READ, 1, 1, 0);
733 	pkt->pkt_time = 3*30;
734 	pkt->pkt_flags |= FLAG_NOINTR;
735 
736 	retry_cnt = 0;
737 	retry_cmd_cnt = 0;
738 retry:
739 	err = scsi_transport(pkt);
740 	if (err != TRAN_ACCEPT) {
741 		/*
742 		 * Retry TRAN_BUSY till STD_FO_MAX_RETRIES is exhausted.
743 		 * All other errors are fatal and should not be retried.
744 		 */
745 		if ((err == TRAN_BUSY) &&
746 		    (retry_cnt++ < STD_FO_MAX_RETRIES)) {
747 			drv_usecwait(STD_FO_RETRY_DELAY);
748 			goto retry;
749 		}
750 		cmn_err(CE_WARN, "Failover failed, "
751 		    "couldn't transport packet");
752 		scsi_destroy_pkt(pkt);
753 		scsi_free_consistent_buf(bp);
754 		return (1);
755 	}
756 	switch (pkt->pkt_reason) {
757 		case CMD_CMPLT:
758 			retry_cmd_cnt = 0;
759 			retval = std_process_cmplt_pkt(sd, pkt, &retry_cnt);
760 			if (retval != 0) {
761 				goto retry;
762 			}
763 			break;
764 		case CMD_TIMEOUT:
765 			cmn_err(CE_WARN, "!Failover failed: timed out ");
766 			retval = 1;
767 			break;
768 		case CMD_INCOMPLETE:
769 		case CMD_RESET:
770 		case CMD_ABORTED:
771 		case CMD_TRAN_ERR:
772 			/*
773 			 * Increased the number of retries when these error
774 			 * cases are encountered.  Also added a 1 sec wait
775 			 * before retrying.
776 			 */
777 			if (retry_cmd_cnt++ < STD_FO_MAX_CMD_RETRIES) {
778 				drv_usecwait(STD_FO_CMD_RETRY_DELAY);
779 				VHCI_DEBUG(4, (CE_WARN, NULL,
780 				    "!Retrying path activation due to "
781 				    "pkt reason:%x, retry cnt:%d",
782 				    pkt->pkt_reason, retry_cmd_cnt));
783 				goto retry;
784 			}
785 			/* FALLTHROUGH */
786 		default:
787 			cmn_err(CE_WARN, "!Path activation did not "
788 			    "complete successfully,"
789 			    "(pkt reason %x)", pkt->pkt_reason);
790 			retval = 1;
791 			break;
792 	}
793 
794 
795 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!Path activation success\n"));
796 	scsi_destroy_pkt(pkt);
797 	scsi_free_consistent_buf(bp);
798 	return (retval);
799 }
800 
801 /* ARGSUSED */
802 static int std_path_deactivate(struct scsi_device *sd, char *pathclass,
803 void *ctpriv)
804 {
805 	return (0);
806 }
807 
808 /* ARGSUSED */
809 static int
810 std_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo
811 *opinfo, void *ctpriv)
812 {
813 	int			mode, preferred, state, xlf;
814 
815 	opinfo->opinfo_rev = OPINFO_REV;
816 
817 	if (std_get_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
818 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_getopinfo:"
819 		    " failed std_get_fo_mode\n"));
820 		return (1);
821 	}
822 
823 	if (state == STD_ACTIVE_OPTIMIZED) {
824 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE;
825 	} else if (state == STD_ACTIVE_NONOPTIMIZED) {
826 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE_NONOPT;
827 	} else if (state == STD_STANDBY) {
828 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
829 	} else if (state == STD_UNAVAILABLE) {
830 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
831 	}
832 	if (preferred) {
833 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_PRIMARY);
834 	} else {
835 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_SECONDARY);
836 	}
837 	VHCI_DEBUG(4, (CE_NOTE, NULL, "std_path_get_opinfo: "
838 	    "class: %s state: %s\n", opinfo->opinfo_path_attr,
839 	    opinfo->opinfo_path_state == SCSI_PATH_ACTIVE ?
840 	    "ACTIVE" : "INACTIVE"));
841 	opinfo->opinfo_xlf_capable = 0;
842 	opinfo->opinfo_pswtch_best = 30;
843 	opinfo->opinfo_pswtch_worst = 3*30;
844 	opinfo->opinfo_preferred = (uint16_t)preferred;
845 	opinfo->opinfo_mode = (uint16_t)mode;
846 
847 	return (0);
848 }
849 
850 /* ARGSUSED */
851 static int std_path_ping(struct scsi_device *sd, void *ctpriv)
852 {
853 	/*
854 	 * For future use
855 	 */
856 	return (1);
857 }
858 
859 /*
860  * Analyze the sense code to determine whether failover process
861  */
862 /* ARGSUSED */
863 static int
864 std_analyze_sense(struct scsi_device *sd, struct scsi_extended_sense
865 *sense, void *ctpriv)
866 {
867 	int rval = SCSI_SENSE_UNKNOWN;
868 
869 	if ((sense->es_key == KEY_UNIT_ATTENTION) &&
870 	    (sense->es_add_code == STD_SCSI_ASC_STATE_CHG) &&
871 	    (sense->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
872 		rval = SCSI_SENSE_STATE_CHANGED;
873 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
874 		    " sense_key:%x, add_code: %x, qual_code:%x"
875 		    " sense:%x\n", sense->es_key, sense->es_add_code,
876 		    sense->es_qual_code, rval));
877 	} else if ((sense->es_key == KEY_NOT_READY) &&
878 	    (sense->es_add_code == STD_LOGICAL_UNIT_NOT_ACCESSIBLE) &&
879 	    (sense->es_qual_code == STD_TGT_PORT_UNAVAILABLE)) {
880 		rval = SCSI_SENSE_INACTIVE;
881 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
882 		    " sense_key:%x, add_code: %x, qual_code:%x"
883 		    " sense:%x\n", sense->es_key, sense->es_add_code,
884 		    sense->es_qual_code, rval));
885 	} else if ((sense->es_key == KEY_ILLEGAL_REQUEST) &&
886 	    (sense->es_add_code == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
887 		rval = SCSI_SENSE_NOFAILOVER;
888 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
889 		    " sense_key:%x, add_code: %x, qual_code:%x"
890 		    " sense:%x\n", sense->es_key, sense->es_add_code,
891 		    sense->es_qual_code, rval));
892 	} else if ((sense->es_key == KEY_ILLEGAL_REQUEST) &&
893 	    (sense->es_add_code == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
894 		rval = SCSI_SENSE_NOFAILOVER;
895 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
896 		    " sense_key:%x, add_code: %x, qual_code:%x"
897 		    " sense:%x\n", sense->es_key, sense->es_add_code,
898 		    sense->es_qual_code, rval));
899 	} else {
900 		/*
901 		 * At this point sense data may be for power-on-reset
902 		 * UNIT ATTN hardware errors, vendor unqiue sense data etc.
903 		 * For all these cases, return SCSI_SENSE_UNKNOWN.
904 		 */
905 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Analyze sense UNKNOWN:"
906 		    " sense key:%x, ASC:%x, ASCQ:%x\n", sense->es_key,
907 		    sense->es_add_code, sense->es_qual_code));
908 	}
909 
910 	return (rval);
911 }
912 
913 /* ARGSUSED */
914 static int
915 std_pathclass_next(char *cur, char **nxt, void *ctpriv)
916 {
917 	/*
918 	 * The first phase does not have a standby path so
919 	 * there will be no explicit failover - when standard tpgs.
920 	 * standard defines preferred flag then we should start
921 	 * using this as the selection mechanism - there can be
922 	 * preferred primary standby that we should fail to first and then
923 	 * nonpreferred secondary standby.
924 	 */
925 	if (cur == NULL) {
926 		*nxt = PCLASS_PRIMARY;
927 		return (0);
928 	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
929 		*nxt = PCLASS_SECONDARY;
930 		return (0);
931 	} else if (strcmp(cur, PCLASS_SECONDARY) == 0) {
932 		return (ENOENT);
933 	} else {
934 		return (EINVAL);
935 	}
936 }
937 
938 int
939 std_set_target_groups(struct scsi_address *ap, int set_state, int tpg_id)
940 {
941 	struct scsi_pkt			*pkt;
942 	struct buf			*bp;
943 	int				len, rval, ss = SCSI_SENSE_UNKNOWN;
944 	char				*bufp;
945 	struct scsi_extended_sense	*sns;
946 
947 	len = 8;
948 
949 	bp = getrbuf(KM_NOSLEEP);
950 	if (bp == NULL) {
951 		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_set_target_groups: "
952 		    " failed getrbuf"));
953 		return (1);
954 	}
955 
956 	bufp = kmem_zalloc(len, KM_NOSLEEP);
957 	if (bufp == NULL) {
958 		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_set_target_groups: "
959 		    "request packet allocation for %d failed....", len));
960 		freerbuf(bp);
961 		return (1);
962 	}
963 
964 	bp->b_un.b_addr = bufp;
965 	bp->b_flags = B_READ;
966 	bp->b_bcount = len;
967 	bp->b_resid = 0;
968 
969 	bufp[4] = (0x0f & set_state);
970 	bufp[6] = (0xff00 & tpg_id) >> 8;
971 	bufp[7] = (0x00ff & tpg_id);
972 
973 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
974 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
975 
976 	if (pkt == NULL) {
977 		VHCI_DEBUG(1, (CE_NOTE, NULL,
978 		    "!std_set_target_groups: scsi_init_pkt error\n"));
979 		freerbuf(bp);
980 		kmem_free((void *)bufp, len);
981 		return (1);
982 	}
983 
984 	/*
985 	 * Sends 1 TPG descriptor only. Hence Parameter list length pkt_cdbp[9]
986 	 * is set to 8 bytes - Refer SPC3 for details.
987 	 */
988 	pkt->pkt_cdbp[0] = SCMD_SET_TARGET_PORT_GROUP;
989 	pkt->pkt_cdbp[1] = STD_SCSI_TPG_SERVICE_ACTION;
990 	pkt->pkt_cdbp[9] = 8;
991 	pkt->pkt_time = 90;
992 
993 	VHCI_DEBUG(1, (CE_NOTE, NULL,
994 	    "!std_set_target_groups: sending set target port group:"
995 	    " cdb[0/1/6/7/8/9]: %x/%x/%x/%x/%x/%x\n", pkt->pkt_cdbp[0],
996 	    pkt->pkt_cdbp[1], pkt->pkt_cdbp[6], pkt->pkt_cdbp[7],
997 	    pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
998 
999 #ifdef DEBUG
1000 	print_buf(bufp, len);
1001 #endif
1002 	rval = vhci_do_scsi_cmd(pkt);
1003 
1004 	if (rval == 0) {
1005 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups:"
1006 		    " vhci_do_scsi_cmd failed\n"));
1007 		freerbuf(bp);
1008 		kmem_free((void *)bufp, len);
1009 		scsi_destroy_pkt(pkt);
1010 		return (-1);
1011 	} else if ((pkt->pkt_reason == CMD_CMPLT) &&
1012 	    (SCBP_C(pkt) == STATUS_CHECK) &&
1013 	    (pkt->pkt_state & STATE_ARQ_DONE)) {
1014 		sns = &(((struct scsi_arq_status *)(uintptr_t)
1015 		    (pkt->pkt_scbp))->sts_sensedata);
1016 
1017 		if ((sns->es_key == KEY_UNIT_ATTENTION) &&
1018 		    (sns->es_add_code == STD_SCSI_ASC_STATE_CHG) &&
1019 		    (sns->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
1020 			ss = SCSI_SENSE_STATE_CHANGED;
1021 			VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_set_target_groups:"
1022 			    " sense:%x, add_code: %x, qual_code:%x"
1023 			    " sense:%x\n", sns->es_key, sns->es_add_code,
1024 			    sns->es_qual_code, ss));
1025 		} else if ((sns->es_key == KEY_ILLEGAL_REQUEST) &&
1026 		    (sns->es_add_code == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
1027 			ss = SCSI_SENSE_NOFAILOVER;
1028 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups:"
1029 			    " sense:%x, add_code: %x, qual_code:%x"
1030 			    " sense:%x\n", sns->es_key, sns->es_add_code,
1031 			    sns->es_qual_code, ss));
1032 		} else if ((sns->es_key == KEY_ILLEGAL_REQUEST) &&
1033 		    (sns->es_add_code == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
1034 			ss = SCSI_SENSE_NOFAILOVER;
1035 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups:"
1036 			    " sense_key:%x, add_code: %x, qual_code:%x"
1037 			    " sense:%x\n", sns->es_key, sns->es_add_code,
1038 			    sns->es_qual_code, rval));
1039 		} else {
1040 			/*
1041 			 * At this point sns data may be for power-on-reset
1042 			 * UNIT ATTN hardware errors, vendor unqiue sense etc.
1043 			 * For all these cases, sense is unknown.
1044 			 */
1045 			ss = SCSI_SENSE_NOFAILOVER;
1046 			VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups: "
1047 			    " sense UNKNOWN: sense key:%x, ASC:%x, ASCQ:%x\n",
1048 			    sns->es_key, sns->es_add_code, sns->es_qual_code));
1049 		}
1050 
1051 		if (ss == SCSI_SENSE_STATE_CHANGED) {
1052 			freerbuf(bp);
1053 			kmem_free((void *)bufp, len);
1054 			scsi_destroy_pkt(pkt);
1055 			return (0);
1056 		}
1057 	}
1058 
1059 	freerbuf(bp);
1060 	kmem_free((void *)bufp, len);
1061 	scsi_destroy_pkt(pkt);
1062 	return (1);
1063 }
1064