xref: /onnv-gate/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/asym_sun.c (revision 9304:ff96a286a5d4)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Implementation of "scsi_vhci_f_asym_sun" asymmetric failover_ops.
29  *
30  * Note : f_asym_sun method is the same as the one originally used by SUN's
31  * T3 (Purple) device.
32  */
33 
34 #include <sys/conf.h>
35 #include <sys/file.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/scsi/scsi.h>
39 #include <sys/scsi/adapters/scsi_vhci.h>
40 
41 /* Supported device table entries.  */
42 char	*purple_dev_table[] = {
43 /*	"                  111111" */
44 /*	"012345670123456789012345" */
45 /*	"|-VID--||-----PID------|" */
46 
47 	"SUN     T300            ",
48 	"SUN     T4              ",
49 	NULL,
50 };
51 
52 /* Failover module plumbing. */
53 SCSI_FAILOVER_OP("f_asym_sun", purple);
54 
55 #define	PURPLE_FO_CMD_RETRY_DELAY	1000000 /* 1 seconds */
56 #define	PURPLE_FO_RETRY_DELAY		2000000 /* 2 seconds */
57 /*
58  * max time for failover to complete is 3 minutes.  Compute
59  * number of retries accordingly, to ensure we wait for at least
60  * 3 minutes
61  */
62 #define	PURPLE_FO_MAX_RETRIES	(3*60*1000000)/PURPLE_FO_RETRY_DELAY
63 
64 /*
65  * max number of retries for purple failover to complete where the ping
66  * command is failing due to transport errors or commands being rejected by
67  * purple.
68  * PURPLE_FO_MAX_RETRIES takes into account the case where CMD_CMPLTs but
69  * purple takes time to complete the failover.
70  */
71 #define	PURPLE_FO_MAX_CMD_RETRIES	3
72 
73 #define	T3_SCSI_ASC_FO_IN_PROGRESS	0x90
74 #define	T3_SCSI_ASCQ_PATH_ACT2INACT	0x00
75 #define	T3_SCSI_ASCQ_PATH_INACT2ACT	0x01
76 #define	T3_SCSI_ASC_PATH_INACTIVE	0x04
77 #define	T3_SCSI_ASCQ_PATH_INACTIVE	0x88
78 
79 static void purple_get_fo_mode(struct scsi_device *sd,
80 		int *mode, int *ownership, int *xlf_capable);
81 
82 /* ARGSUSED */
83 static int
purple_device_probe(struct scsi_device * sd,struct scsi_inquiry * stdinq,void ** ctpriv)84 purple_device_probe(struct scsi_device *sd, struct scsi_inquiry *stdinq,
85 void **ctpriv)
86 {
87 	char	**dt;
88 	int	xlf = 0, mode = 0, ownership = 0;
89 
90 	VHCI_DEBUG(6, (CE_NOTE, NULL, "purple_device_probe: vidpid %s\n",
91 	    stdinq->inq_vid));
92 
93 	for (dt = purple_dev_table; *dt; dt++) {
94 		if (strncmp(stdinq->inq_vid, *dt, strlen(*dt)))
95 			continue;
96 
97 		/* match */
98 		purple_get_fo_mode(sd, &mode, &ownership, &xlf);
99 		if (mode == SCSI_EXPLICIT_FAILOVER)
100 			return (SFO_DEVICE_PROBE_VHCI);
101 		else
102 			return (SFO_DEVICE_PROBE_PHCI);
103 	}
104 	return (SFO_DEVICE_PROBE_PHCI);
105 }
106 
107 /* ARGSUSED */
108 static void
purple_device_unprobe(struct scsi_device * sd,void * ctpriv)109 purple_device_unprobe(struct scsi_device *sd, void *ctpriv)
110 {
111 	/*
112 	 * For future use
113 	 */
114 }
115 
116 /* ARGSUSED */
117 static void
purple_get_fo_mode(struct scsi_device * sd,int * mode,int * ownership,int * xlf_capable)118 purple_get_fo_mode(struct scsi_device *sd, int *mode,
119 int *ownership, int *xlf_capable)
120 {
121 	char		inqbuf[0xff], *ptr, *end;
122 	int		retval = 0;
123 	struct buf	*bp;
124 	struct scsi_pkt	*pkt;
125 	struct scsi_address	*ap;
126 
127 	*mode = *ownership = *xlf_capable = 0;
128 	bp = getrbuf(KM_NOSLEEP);
129 	if (bp == NULL)
130 		return;
131 	bp->b_un.b_addr = inqbuf;
132 	bp->b_flags = B_READ;
133 	bp->b_bcount = 0xff;
134 	bp->b_resid = 0;
135 
136 	ap = &sd->sd_address;
137 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
138 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
139 	if (pkt == NULL) {
140 		freerbuf(bp);
141 		return;
142 	}
143 
144 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
145 	pkt->pkt_cdbp[1] = 0x1;
146 	pkt->pkt_cdbp[2] = 0x83;
147 	pkt->pkt_cdbp[4] = 0xff;
148 	pkt->pkt_time = 90;
149 
150 	retval = vhci_do_scsi_cmd(pkt);
151 	scsi_destroy_pkt(pkt);
152 	freerbuf(bp);
153 	if (retval == 0) {
154 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!(sd:%p)failed to get mode"
155 		    " and ownership info\n", (void *)sd));
156 		return;
157 	}
158 
159 	ptr = inqbuf;
160 	ptr += 4; /* identification descriptor 0 */
161 	end = inqbuf + 4 + inqbuf[3];
162 	while (((ptr[1] & 0x0f) != 0xf) && (ptr < end))
163 		ptr += ptr[3] + 4;  /* next identification descriptor */
164 	if (ptr >= end) {
165 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!(sd:%p)p_g_m_a_o:assuming"
166 		    " implicit mode\n", (void *)sd));
167 		*mode = SCSI_IMPLICIT_FAILOVER;
168 		*ownership = 0;
169 		return;
170 	}
171 	ptr += 4; /* Port Failover Identifier */
172 	*mode = ptr[0];
173 	if ((ptr[1] & 0x3) == 0x01)
174 		*ownership = 0;
175 	else if ((ptr[1] & 0x3) == 0x00)
176 		*ownership = 1;
177 	if (ptr[1] & 0x4) {
178 		*xlf_capable = 1;
179 	} else {
180 		*xlf_capable = 0;
181 	}
182 }
183 
184 static int
purple_activate_explicit(struct scsi_device * sd,int xlf_capable)185 purple_activate_explicit(struct scsi_device *sd, int xlf_capable)
186 {
187 	char			cdb[CDB_GROUP1];
188 	struct scsi_address	*ap;
189 	struct scsi_pkt		*pkt;
190 	int			retval;
191 
192 	bzero(cdb, CDB_GROUP1);
193 
194 	ap = &sd->sd_address;
195 	pkt = scsi_init_pkt(ap, NULL, NULL, CDB_GROUP1,
196 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
197 	if (pkt == NULL)
198 		return (0);
199 
200 	pkt->pkt_cdbp[0] = 0xD0;
201 	if (xlf_capable) {
202 		/*
203 		 * Bit 2/1: 1/0: implicitly drop any reservation
204 		 * Bit 0: Grab bit - 1 means an explicit failover will be
205 		 * triggered
206 		 */
207 		pkt->pkt_cdbp[1] = 0x05;
208 	} else {
209 		pkt->pkt_cdbp[1] = 0x01; /* no reservation check, "grab" lun */
210 	}
211 
212 	retval = vhci_do_scsi_cmd(pkt);
213 	scsi_destroy_pkt(pkt);
214 
215 	return (retval);
216 }
217 
218 /* ARGSUSED */
219 static int
purple_path_activate(struct scsi_device * sd,char * pathclass,void * ctpriv)220 purple_path_activate(struct scsi_device *sd, char *pathclass,
221 void *ctpriv)
222 {
223 	struct buf		*bp;
224 	struct scsi_pkt		*pkt;
225 	struct scsi_address	*ap;
226 	int			err, retry_cnt, retry_cmd_cnt;
227 	int			mode, ownership, retval, xlf;
228 	uint8_t			*sns, skey, asc, ascq;
229 
230 	ap = &sd->sd_address;
231 
232 	mode = ownership = 0;
233 
234 	purple_get_fo_mode(sd, &mode, &ownership, &xlf);
235 	if (ownership == 1) {
236 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for 0x%p\n",
237 		    (void *)sd));
238 		return (0);
239 	}
240 
241 	if (mode != SCSI_IMPLICIT_FAILOVER) {
242 		VHCI_DEBUG(4, (CE_NOTE, NULL,
243 		    "!mode is EXPLICIT for 0x%p xlf %x\n",
244 		    (void *)sd, xlf));
245 		retval = purple_activate_explicit(sd, xlf);
246 		if (retval == 0) {
247 			VHCI_DEBUG(4, (CE_NOTE, NULL,
248 			    "!(sd:%p)purple_path_activate failed(1)\n",
249 			    (void *)sd));
250 			return (1);
251 		}
252 	} else {
253 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!mode is IMPLICIT for 0x%p\n",
254 		    (void *)sd));
255 	}
256 
257 	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, DEV_BSIZE,
258 	    B_READ, NULL, NULL);
259 	if (!bp) {
260 		cmn_err(CE_WARN, "!No resources (buf) to initiate T3 path "
261 		    "activation");
262 		return (1);
263 	}
264 
265 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1,
266 	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
267 	if (!pkt) {
268 		cmn_err(CE_WARN, "!Packet alloc failure during T3 "
269 		    "path activation");
270 		scsi_free_consistent_buf(bp);
271 		return (1);
272 	}
273 
274 	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
275 	    SCMD_READ, 1, 1, 0);
276 	pkt->pkt_time = 3*30;
277 	pkt->pkt_flags |= FLAG_NOINTR;
278 
279 	retry_cnt = 0;
280 	retry_cmd_cnt = 0;
281 retry:
282 	err = scsi_transport(pkt);
283 	if (err != TRAN_ACCEPT) {
284 		/*
285 		 * Retry TRAN_BUSY till PURPLE_FO_MAX_RETRIES is exhausted.
286 		 * All other errors are fatal and should not be retried.
287 		 */
288 		if ((err == TRAN_BUSY) &&
289 		    (retry_cnt++ < PURPLE_FO_MAX_RETRIES)) {
290 			drv_usecwait(PURPLE_FO_RETRY_DELAY);
291 			goto retry;
292 		}
293 		cmn_err(CE_WARN, "T3 failover failed, "
294 		    "couldn't transport packet");
295 		scsi_destroy_pkt(pkt);
296 		scsi_free_consistent_buf(bp);
297 		return (1);
298 	}
299 
300 	switch (pkt->pkt_reason) {
301 		case CMD_TIMEOUT:
302 			cmn_err(CE_WARN, "!T3 failover failed: timed out ");
303 			scsi_destroy_pkt(pkt);
304 			scsi_free_consistent_buf(bp);
305 			return (1);
306 		case CMD_CMPLT:
307 			/*
308 			 * Re-initialize retry_cmd_cnt. Allow transport and
309 			 * cmd errors to go through a full retry count when
310 			 * these are encountered.  This way TRAN/CMD errors
311 			 * retry count is not exhausted due to CMD_CMPLTs
312 			 * delay for a T3 fo to finish. This allows the system
313 			 * to brave a hick-up on the link at any given time,
314 			 * while waiting for the fo to complete.
315 			 */
316 			retry_cmd_cnt = 0;
317 			if (pkt->pkt_state & STATE_ARQ_DONE) {
318 				sns = (uint8_t *)
319 				    &(((struct scsi_arq_status *)(uintptr_t)
320 				    (pkt->pkt_scbp))->sts_sensedata);
321 				skey = scsi_sense_key(sns);
322 				asc = scsi_sense_asc(sns);
323 				ascq = scsi_sense_ascq(sns);
324 				if (skey == KEY_UNIT_ATTENTION) {
325 					/*
326 					 * swallow unit attention
327 					 */
328 					goto retry;
329 				} else if ((skey == KEY_NOT_READY) &&
330 				    (asc == T3_SCSI_ASC_FO_IN_PROGRESS) &&
331 				    (ascq == T3_SCSI_ASCQ_PATH_INACT2ACT)) {
332 					if (retry_cnt++ >=
333 					    PURPLE_FO_MAX_RETRIES) {
334 						cmn_err(CE_WARN, "!T3 failover"
335 						    " failed: timed out "
336 						    "waiting for path to "
337 						    "become active");
338 						scsi_destroy_pkt(pkt);
339 						scsi_free_consistent_buf(bp);
340 						return (1);
341 					}
342 					VHCI_DEBUG(6, (CE_NOTE, NULL,
343 					    "!(sd:%p)lun becoming active...\n",
344 					    (void *)sd));
345 					drv_usecwait(PURPLE_FO_RETRY_DELAY);
346 					goto retry;
347 				}
348 				cmn_err(CE_NOTE, "!T3 failover failed;"
349 				    " sense key:%x, ASC: %x, "
350 				    "ASCQ:%x", skey, asc, ascq);
351 				scsi_destroy_pkt(pkt);
352 				scsi_free_consistent_buf(bp);
353 				return (1);
354 			}
355 			switch (SCBP_C(pkt)) {
356 				case STATUS_GOOD:
357 					break;
358 				case STATUS_CHECK:
359 					VHCI_DEBUG(4, (CE_WARN, NULL,
360 					    "!(sd:%p)T3:"
361 					    " cont allegiance during purple "
362 					    "activation", (void *)sd));
363 					scsi_destroy_pkt(pkt);
364 					scsi_free_consistent_buf(bp);
365 					return (1);
366 				case STATUS_QFULL:
367 					VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL "
368 					    "status returned during purple "
369 					    "path activation for 0x%p\n",
370 					    (void *)sd));
371 					drv_usecwait(5000);
372 					goto retry;
373 				case STATUS_BUSY:
374 					VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY "
375 					    "status returned during purple "
376 					    "path activation for 0x%p\n",
377 					    (void *)sd));
378 					drv_usecwait(5000);
379 					goto retry;
380 				default:
381 					VHCI_DEBUG(4, (CE_WARN, NULL,
382 					    "!(sd:%p) Bad status "
383 					    "returned during purple "
384 					    "activation (pkt 0x%p, "
385 					    "status %x)",
386 					    (void *)sd, (void *)pkt,
387 					    SCBP_C(pkt)));
388 					scsi_destroy_pkt(pkt);
389 					scsi_free_consistent_buf(bp);
390 					return (1);
391 			}
392 			break;
393 		case CMD_INCOMPLETE:
394 		case CMD_RESET:
395 		case CMD_ABORTED:
396 		case CMD_TRAN_ERR:
397 			/*
398 			 * Increased the number of retries when these error
399 			 * cases are encountered.  Also added a 1 sec wait
400 			 * before retrying.
401 			 */
402 			if (retry_cmd_cnt++ < PURPLE_FO_MAX_CMD_RETRIES) {
403 				drv_usecwait(PURPLE_FO_CMD_RETRY_DELAY);
404 				VHCI_DEBUG(4, (CE_WARN, NULL,
405 				    "!Retrying T3 path activation due to "
406 				    "pkt reason:%x, retry cnt:%d",
407 				    pkt->pkt_reason, retry_cmd_cnt));
408 				goto retry;
409 			}
410 			/* FALLTHROUGH */
411 		default:
412 			cmn_err(CE_WARN, "!T3 path activation did not "
413 			    "complete successfully,"
414 			    "(pkt reason %x)", pkt->pkt_reason);
415 			scsi_destroy_pkt(pkt);
416 			scsi_free_consistent_buf(bp);
417 			return (1);
418 	}
419 
420 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!T3 path activation success\n"));
421 	scsi_destroy_pkt(pkt);
422 	scsi_free_consistent_buf(bp);
423 	return (0);
424 }
425 
426 /* ARGSUSED */
purple_path_deactivate(struct scsi_device * sd,char * pathclass,void * ctpriv)427 static int purple_path_deactivate(struct scsi_device *sd, char *pathclass,
428 void *ctpriv)
429 {
430 	return (0);
431 }
432 
433 /* ARGSUSED */
434 static int
purple_path_get_opinfo(struct scsi_device * sd,struct scsi_path_opinfo * opinfo,void * ctpriv)435 purple_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo
436 *opinfo, void *ctpriv)
437 {
438 	struct scsi_inquiry	*inq;
439 	struct buf		*bp;
440 	struct scsi_pkt		*pkt;
441 	struct scsi_address	*ap;
442 	int			retval, mode, ownership, xlf;
443 
444 	ap = &sd->sd_address;
445 
446 	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, SUN_INQSIZE,
447 	    B_READ, NULL, NULL);
448 	if (!bp)
449 		return (1);
450 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
451 	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
452 	if (!pkt) {
453 		scsi_free_consistent_buf(bp);
454 		return (1);
455 	}
456 	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
457 	    SCMD_INQUIRY, 0, SUN_INQSIZE, 0);
458 	pkt->pkt_time = 60;
459 
460 	retval = vhci_do_scsi_cmd(pkt);
461 	if (retval == 0) {
462 		scsi_destroy_pkt(pkt);
463 		scsi_free_consistent_buf(bp);
464 		return (1);
465 	}
466 
467 	inq = (struct scsi_inquiry *)bp->b_un.b_addr;
468 
469 	opinfo->opinfo_rev = OPINFO_REV;
470 
471 	/*
472 	 * Ignore to check inquiry dual port bit.
473 	 * T3 can return this bit as 0 when one of its controller goes down.
474 	 * Instead relying on inquiry port bit only.
475 	 */
476 	if (inq->inq_port == 0) {
477 		(void) strcpy(opinfo->opinfo_path_attr, "primary");
478 	} else {
479 		(void) strcpy(opinfo->opinfo_path_attr, "secondary");
480 	}
481 
482 	scsi_destroy_pkt(pkt);
483 	scsi_free_consistent_buf(bp);
484 
485 	purple_get_fo_mode(sd, &mode, &ownership, &xlf);
486 
487 	if (ownership == 1)
488 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE;
489 	else
490 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
491 	opinfo->opinfo_xlf_capable = xlf;
492 	opinfo->opinfo_pswtch_best = 30;
493 	opinfo->opinfo_pswtch_worst = 3*30;
494 	opinfo->opinfo_mode = (uint16_t)mode;
495 	opinfo->opinfo_preferred = 1;
496 
497 	return (0);
498 }
499 
500 /* ARGSUSED */
purple_path_ping(struct scsi_device * sd,void * ctpriv)501 static int purple_path_ping(struct scsi_device *sd, void *ctpriv)
502 {
503 	/*
504 	 * For future use
505 	 */
506 	return (1);
507 }
508 
509 /* ARGSUSED */
510 static int
purple_analyze_sense(struct scsi_device * sd,uint8_t * sense,void * ctpriv)511 purple_analyze_sense(struct scsi_device *sd, uint8_t *sense,
512 void *ctpriv)
513 {
514 	uint8_t skey, asc, ascq;
515 
516 	skey = scsi_sense_key(sense);
517 	asc = scsi_sense_asc(sense);
518 	ascq = scsi_sense_ascq(sense);
519 
520 	if (skey == KEY_NOT_READY) {
521 		if (asc == T3_SCSI_ASC_FO_IN_PROGRESS) {
522 			if (ascq == T3_SCSI_ASCQ_PATH_INACT2ACT)
523 				return (SCSI_SENSE_INACT2ACT);
524 			else if (ascq == T3_SCSI_ASCQ_PATH_ACT2INACT)
525 				return (SCSI_SENSE_ACT2INACT);
526 		} else if ((asc == T3_SCSI_ASC_PATH_INACTIVE) &&
527 		    (ascq == T3_SCSI_ASCQ_PATH_INACTIVE)) {
528 			return (SCSI_SENSE_INACTIVE);
529 		}
530 	}
531 
532 	/*
533 	 * At this point sense data may be for power-on-reset UNIT ATTN or
534 	 * hardware errors, vendor unique sense data etc.  For all these cases
535 	 * return SCSI_SENSE_UNKNOWN.
536 	 */
537 	VHCI_DEBUG(6, (CE_NOTE, NULL, "!T3 analyze sense UNKNOWN:"
538 	    " sense key:%x, ASC: %x, ASCQ:%x\n", skey, asc, ascq));
539 	return (SCSI_SENSE_UNKNOWN);
540 }
541 
542 /* ARGSUSED */
543 static int
purple_pathclass_next(char * cur,char ** nxt,void * ctpriv)544 purple_pathclass_next(char *cur, char **nxt, void *ctpriv)
545 {
546 	if (cur == NULL) {
547 		*nxt = PCLASS_PRIMARY;
548 		return (0);
549 	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
550 		*nxt = PCLASS_SECONDARY;
551 		return (0);
552 	} else if (strcmp(cur, PCLASS_SECONDARY) == 0) {
553 		return (ENOENT);
554 	} else {
555 		return (EINVAL);
556 	}
557 }
558