xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
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 #include <sys/scsi/adapters/scsi_vhci_tpgs.h>
40 
41 /* Supported device table entries.  */
42 char	*std_dev_table[] = { NULL };
43 
44 /* Failover module plumbing. */
45 SCSI_FAILOVER_OP(SFO_NAME_TPGS, std);
46 
47 #define	STD_FO_CMD_RETRY_DELAY	1000000 /* 1 seconds */
48 #define	STD_FO_RETRY_DELAY	2000000 /* 2 seconds */
49 /*
50  * max time for failover to complete is 3 minutes.  Compute
51  * number of retries accordingly, to ensure we wait for at least
52  * 3 minutes
53  */
54 #define	STD_FO_MAX_RETRIES	(3*60*1000000)/STD_FO_RETRY_DELAY
55 
56 
57 /* ARGSUSED */
58 static int
59 std_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq,
60     void **ctpriv)
61 {
62 	int		mode, state, xlf, preferred = 0;
63 
64 	VHCI_DEBUG(6, (CE_NOTE, NULL, "std_device_probe: vidpid %s\n",
65 	    inq->inq_vid));
66 
67 	if (inq->inq_tpgs == TPGS_FAILOVER_NONE) {
68 		VHCI_DEBUG(4, (CE_WARN, NULL,
69 		    "!std_device_probe: not a standard tpgs device"));
70 		return (SFO_DEVICE_PROBE_PHCI);
71 	}
72 
73 	if (inq->inq_dtype == DTYPE_SEQUENTIAL) {
74 		VHCI_DEBUG(4, (CE_NOTE, NULL,
75 		    "!std_device_probe: Detected a "
76 		    "Standard Asymmetric device "
77 		    "not yet supported\n"));
78 		return (SFO_DEVICE_PROBE_PHCI);
79 	}
80 
81 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
82 		VHCI_DEBUG(4, (CE_WARN, NULL, "!unable to fetch fo "
83 		    "mode: sd(%p)", (void *) sd));
84 		return (SFO_DEVICE_PROBE_PHCI);
85 	}
86 
87 	if (inq->inq_tpgs == TPGS_FAILOVER_IMPLICIT) {
88 		VHCI_DEBUG(1, (CE_NOTE, NULL,
89 		    "!std_device_probe: Detected a "
90 		    "Standard Asymmetric device "
91 		    "with implicit failover\n"));
92 		return (SFO_DEVICE_PROBE_VHCI);
93 	}
94 	if (inq->inq_tpgs == TPGS_FAILOVER_EXPLICIT) {
95 		VHCI_DEBUG(1, (CE_NOTE, NULL,
96 		    "!std_device_probe: Detected a "
97 		    "Standard Asymmetric device "
98 		    "with explicit failover\n"));
99 		return (SFO_DEVICE_PROBE_VHCI);
100 	}
101 	if (inq->inq_tpgs == TPGS_FAILOVER_BOTH) {
102 		VHCI_DEBUG(1, (CE_NOTE, NULL,
103 		    "!std_device_probe: Detected a "
104 		    "Standard Asymmetric device "
105 		    "which supports both implicit and explicit failover\n"));
106 		return (SFO_DEVICE_PROBE_VHCI);
107 	}
108 	VHCI_DEBUG(1, (CE_WARN, NULL,
109 	    "!std_device_probe: "
110 	    "Unknown tpgs_bits: %x", inq->inq_tpgs));
111 	return (SFO_DEVICE_PROBE_PHCI);
112 }
113 
114 /* ARGSUSED */
115 static void
116 std_device_unprobe(struct scsi_device *sd, void *ctpriv)
117 {
118 	/*
119 	 * For future use
120 	 */
121 }
122 
123 /* ARGSUSED */
124 static int
125 std_activate_explicit(struct scsi_device *sd, int xlf_capable)
126 {
127 	cmn_err(CE_NOTE, "Explicit Activation is done by "
128 	    "vhci_tpgs_set_target_groups() call from MPAPI");
129 	return (1);
130 }
131 
132 /*
133  * Process the packet reason of CMD_PKT_CMPLT - return 0 if no
134  * retry and 1 if a retry should be done
135  */
136 static int
137 std_process_cmplt_pkt(struct scsi_device *sd, struct scsi_pkt *pkt,
138     int *retry_cnt)
139 {
140 	struct scsi_extended_sense	*sns;
141 
142 	/*
143 	 * Re-initialize retry_cmd_cnt. Allow transport and
144 	 * cmd errors to go through a full retry count when
145 	 * these are encountered.  This way TRAN/CMD errors
146 	 * retry count is not exhausted due to CMD_CMPLTs
147 	 * delay. This allows the system
148 	 * to brave a hick-up on the link at any given time,
149 	 * while waiting for the fo to complete.
150 	 */
151 	if (pkt->pkt_state & STATE_ARQ_DONE) {
152 		sns = &(((struct scsi_arq_status *)(uintptr_t)
153 		    (pkt->pkt_scbp))->sts_sensedata);
154 		if (sns->es_key == KEY_UNIT_ATTENTION) {
155 			/*
156 			 * tpgs access state changed
157 			 */
158 			if (sns->es_add_code == STD_SCSI_ASC_STATE_CHG &&
159 			    sns->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC) {
160 				/* XXX: update path info? */
161 				cmn_err(CE_WARN, "!Device failover"
162 				    " state change");
163 			}
164 			return (1);
165 		} else if (sns->es_key == KEY_NOT_READY) {
166 			if ((*retry_cnt)++ >=
167 			    STD_FO_MAX_RETRIES) {
168 				cmn_err(CE_WARN, "!Device failover"
169 				    " failed: timed out waiting "
170 				    "for path to become active");
171 				return (0);
172 			}
173 			VHCI_DEBUG(6, (CE_NOTE, NULL,
174 			    "!(sd:%p)lun "
175 			    "becoming active...\n", (void *)sd));
176 			drv_usecwait(STD_FO_RETRY_DELAY);
177 			return (1);
178 		}
179 		cmn_err(CE_NOTE, "!Failover failed;"
180 		    " sense key:%x, ASC: %x, "
181 		    "ASCQ:%x", sns->es_key,
182 		    sns->es_add_code, sns->es_qual_code);
183 		return (0);
184 	}
185 	switch (SCBP_C(pkt)) {
186 		case STATUS_GOOD:
187 			break;
188 		case STATUS_CHECK:
189 			VHCI_DEBUG(4, (CE_WARN, NULL,
190 			    "!(sd:%p):"
191 			    " status returned CHECK during std"
192 			    " path activation", (void *)sd));
193 			return (0);
194 		case STATUS_QFULL:
195 			VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL "
196 			    "status returned QFULL during std "
197 			    "path activation for %p\n", (void *)sd));
198 			drv_usecwait(5000);
199 			return (1);
200 		case STATUS_BUSY:
201 			VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY "
202 			    "status returned BUSY during std "
203 			    "path activation for %p\n", (void *)sd));
204 			drv_usecwait(5000);
205 			return (1);
206 		default:
207 			VHCI_DEBUG(4, (CE_WARN, NULL,
208 			    "!(sd:%p) Bad status returned during std "
209 			    "activation (pkt %p, status %x)",
210 			    (void *)sd, (void *)pkt, SCBP_C(pkt)));
211 			return (0);
212 	}
213 	return (0);
214 }
215 
216 /*
217  * For now we are going to use primary/online and secondary/online.
218  * There is no standby path returned by the dsp and we may have
219  * to do something different for other devices that use standby
220  */
221 /* ARGSUSED */
222 static int
223 std_path_activate(struct scsi_device *sd, char *pathclass,
224     void *ctpriv)
225 {
226 	struct buf			*bp;
227 	struct scsi_pkt			*pkt;
228 	struct scsi_address		*ap;
229 	int				err, retry_cnt, retry_cmd_cnt;
230 	int				mode, state, retval, xlf, preferred;
231 
232 	ap = &sd->sd_address;
233 
234 	mode = state = 0;
235 
236 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
237 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_activate:"
238 		    " failed vhci_tpgs_get_target_fo_mode\n"));
239 		return (1);
240 	}
241 	if ((state == STD_ACTIVE_OPTIMIZED) ||
242 	    (state == STD_ACTIVE_NONOPTIMIZED)) {
243 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for %p\n",
244 		    (void *)sd));
245 		return (0);
246 	}
247 
248 	if (mode != SCSI_IMPLICIT_FAILOVER) {
249 		VHCI_DEBUG(4, (CE_NOTE, NULL,
250 		    "!mode is EXPLICIT for %p xlf %x\n",
251 		    (void *)sd, xlf));
252 		retval = std_activate_explicit(sd, xlf);
253 		if (retval != 0) {
254 			VHCI_DEBUG(4, (CE_NOTE, NULL,
255 			    "!(sd:%p)std_path_activate failed(1)\n",
256 			    (void *)sd));
257 			return (1);
258 		}
259 	} else {
260 		VHCI_DEBUG(4, (CE_NOTE, NULL, "STD mode is IMPLICIT for %p\n",
261 		    (void *)sd));
262 	}
263 
264 	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, DEV_BSIZE,
265 	    B_READ, NULL, NULL);
266 	if (!bp) {
267 		VHCI_DEBUG(4, (CE_WARN, NULL,
268 		    "!(sd:%p)std_path_activate failed to alloc buffer",
269 		    (void *)sd));
270 		return (1);
271 	}
272 
273 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1,
274 	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
275 	if (!pkt) {
276 		VHCI_DEBUG(4, (CE_WARN, NULL,
277 		    "!(sd:%p)std_path_activate failed to initialize packet",
278 		    (void *)sd));
279 		scsi_free_consistent_buf(bp);
280 		return (1);
281 	}
282 
283 	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
284 	    SCMD_READ, 1, 1, 0);
285 	pkt->pkt_time = 3*30;
286 	pkt->pkt_flags |= FLAG_NOINTR;
287 
288 	retry_cnt = 0;
289 	retry_cmd_cnt = 0;
290 retry:
291 	err = scsi_transport(pkt);
292 	if (err != TRAN_ACCEPT) {
293 		/*
294 		 * Retry TRAN_BUSY till STD_FO_MAX_RETRIES is exhausted.
295 		 * All other errors are fatal and should not be retried.
296 		 */
297 		if ((err == TRAN_BUSY) &&
298 		    (retry_cnt++ < STD_FO_MAX_RETRIES)) {
299 			drv_usecwait(STD_FO_RETRY_DELAY);
300 			goto retry;
301 		}
302 		cmn_err(CE_WARN, "Failover failed, "
303 		    "couldn't transport packet");
304 		scsi_destroy_pkt(pkt);
305 		scsi_free_consistent_buf(bp);
306 		return (1);
307 	}
308 	switch (pkt->pkt_reason) {
309 		case CMD_CMPLT:
310 			retry_cmd_cnt = 0;
311 			retval = std_process_cmplt_pkt(sd, pkt, &retry_cnt);
312 			if (retval != 0) {
313 				goto retry;
314 			}
315 			break;
316 		case CMD_TIMEOUT:
317 			cmn_err(CE_WARN, "!Failover failed: timed out ");
318 			retval = 1;
319 			break;
320 		case CMD_INCOMPLETE:
321 		case CMD_RESET:
322 		case CMD_ABORTED:
323 		case CMD_TRAN_ERR:
324 			/*
325 			 * Increased the number of retries when these error
326 			 * cases are encountered.  Also added a 1 sec wait
327 			 * before retrying.
328 			 */
329 			if (retry_cmd_cnt++ < STD_FO_MAX_CMD_RETRIES) {
330 				drv_usecwait(STD_FO_CMD_RETRY_DELAY);
331 				VHCI_DEBUG(4, (CE_WARN, NULL,
332 				    "!Retrying path activation due to "
333 				    "pkt reason:%x, retry cnt:%d",
334 				    pkt->pkt_reason, retry_cmd_cnt));
335 				goto retry;
336 			}
337 			/* FALLTHROUGH */
338 		default:
339 			cmn_err(CE_WARN, "!Path activation did not "
340 			    "complete successfully,"
341 			    "(pkt reason %x)", pkt->pkt_reason);
342 			retval = 1;
343 			break;
344 	}
345 
346 
347 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!Path activation success\n"));
348 	scsi_destroy_pkt(pkt);
349 	scsi_free_consistent_buf(bp);
350 	return (retval);
351 }
352 
353 /* ARGSUSED */
354 static int std_path_deactivate(struct scsi_device *sd, char *pathclass,
355     void *ctpriv)
356 {
357 	return (0);
358 }
359 
360 /* ARGSUSED */
361 static int
362 std_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo,
363     void *ctpriv)
364 {
365 	int			mode, preferred, state, xlf;
366 
367 	opinfo->opinfo_rev = OPINFO_REV;
368 
369 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
370 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_getopinfo:"
371 		    " failed vhci_tpgs_get_target_fo_mode\n"));
372 		return (1);
373 	}
374 
375 	if (state == STD_ACTIVE_OPTIMIZED) {
376 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE;
377 	} else if (state == STD_ACTIVE_NONOPTIMIZED) {
378 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE_NONOPT;
379 	} else if (state == STD_STANDBY) {
380 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
381 	} else if (state == STD_UNAVAILABLE) {
382 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
383 	}
384 	if (preferred) {
385 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_PRIMARY);
386 	} else {
387 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_SECONDARY);
388 	}
389 	VHCI_DEBUG(4, (CE_NOTE, NULL, "std_path_get_opinfo: "
390 	    "class: %s state: %s\n", opinfo->opinfo_path_attr,
391 	    opinfo->opinfo_path_state == SCSI_PATH_ACTIVE ?
392 	    "ACTIVE" : "INACTIVE"));
393 	opinfo->opinfo_xlf_capable = 0;
394 	opinfo->opinfo_pswtch_best = 30;
395 	opinfo->opinfo_pswtch_worst = 3*30;
396 	opinfo->opinfo_preferred = (uint16_t)preferred;
397 	opinfo->opinfo_mode = (uint16_t)mode;
398 
399 	return (0);
400 }
401 
402 /* ARGSUSED */
403 static int std_path_ping(struct scsi_device *sd, void *ctpriv)
404 {
405 	/*
406 	 * For future use
407 	 */
408 	return (1);
409 }
410 
411 /*
412  * Analyze the sense code to determine whether failover process
413  */
414 /* ARGSUSED */
415 static int
416 std_analyze_sense(struct scsi_device *sd, struct scsi_extended_sense *sense,
417     void *ctpriv)
418 {
419 	int rval = SCSI_SENSE_UNKNOWN;
420 
421 	if ((sense->es_key == KEY_UNIT_ATTENTION) &&
422 	    (sense->es_add_code == STD_SCSI_ASC_STATE_CHG) &&
423 	    (sense->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
424 		rval = SCSI_SENSE_STATE_CHANGED;
425 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
426 		    " sense_key:%x, add_code: %x, qual_code:%x"
427 		    " sense:%x\n", sense->es_key, sense->es_add_code,
428 		    sense->es_qual_code, rval));
429 	} else if ((sense->es_key == KEY_NOT_READY) &&
430 	    (sense->es_add_code == STD_LOGICAL_UNIT_NOT_ACCESSIBLE) &&
431 	    (sense->es_qual_code == STD_TGT_PORT_UNAVAILABLE)) {
432 		rval = SCSI_SENSE_INACTIVE;
433 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
434 		    " sense_key:%x, add_code: %x, qual_code:%x"
435 		    " sense:%x\n", sense->es_key, sense->es_add_code,
436 		    sense->es_qual_code, rval));
437 	} else if ((sense->es_key == KEY_ILLEGAL_REQUEST) &&
438 	    (sense->es_add_code == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
439 		rval = SCSI_SENSE_NOFAILOVER;
440 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
441 		    " sense_key:%x, add_code: %x, qual_code:%x"
442 		    " sense:%x\n", sense->es_key, sense->es_add_code,
443 		    sense->es_qual_code, rval));
444 	} else if ((sense->es_key == KEY_ILLEGAL_REQUEST) &&
445 	    (sense->es_add_code == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
446 		rval = SCSI_SENSE_NOFAILOVER;
447 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
448 		    " sense_key:%x, add_code: %x, qual_code:%x"
449 		    " sense:%x\n", sense->es_key, sense->es_add_code,
450 		    sense->es_qual_code, rval));
451 	} else {
452 		/*
453 		 * At this point sense data may be for power-on-reset
454 		 * UNIT ATTN hardware errors, vendor unqiue sense data etc.
455 		 * For all these cases, return SCSI_SENSE_UNKNOWN.
456 		 */
457 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Analyze sense UNKNOWN:"
458 		    " sense key:%x, ASC:%x, ASCQ:%x\n", sense->es_key,
459 		    sense->es_add_code, sense->es_qual_code));
460 	}
461 
462 	return (rval);
463 }
464 
465 /* ARGSUSED */
466 static int
467 std_pathclass_next(char *cur, char **nxt, void *ctpriv)
468 {
469 	/*
470 	 * The first phase does not have a standby path so
471 	 * there will be no explicit failover - when standard tpgs.
472 	 * standard defines preferred flag then we should start
473 	 * using this as the selection mechanism - there can be
474 	 * preferred primary standby that we should fail to first and then
475 	 * nonpreferred secondary standby.
476 	 */
477 	if (cur == NULL) {
478 		*nxt = PCLASS_PRIMARY;
479 		return (0);
480 	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
481 		*nxt = PCLASS_SECONDARY;
482 		return (0);
483 	} else if (strcmp(cur, PCLASS_SECONDARY) == 0) {
484 		return (ENOENT);
485 	} else {
486 		return (EINVAL);
487 	}
488 }
489