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