Home | History | Annotate | Download | only in fops
      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_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 	uint8_t *sns, skey, asc, ascq;
    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 = (uint8_t *)
    153 		    &(((struct scsi_arq_status *)(uintptr_t)
    154 		    (pkt->pkt_scbp))->sts_sensedata);
    155 		skey = scsi_sense_key(sns);
    156 		asc = scsi_sense_asc(sns);
    157 		ascq = scsi_sense_ascq(sns);
    158 		if (skey == KEY_UNIT_ATTENTION) {
    159 			/*
    160 			 * tpgs access state changed
    161 			 */
    162 			if (asc == STD_SCSI_ASC_STATE_CHG &&
    163 			    ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC) {
    164 				/* XXX: update path info? */
    165 				cmn_err(CE_WARN, "!Device failover"
    166 				    " state change");
    167 			}
    168 			return (1);
    169 		} else if (skey == KEY_NOT_READY) {
    170 			if ((*retry_cnt)++ >=
    171 			    STD_FO_MAX_RETRIES) {
    172 				cmn_err(CE_WARN, "!Device failover"
    173 				    " failed: timed out waiting "
    174 				    "for path to become active");
    175 				return (0);
    176 			}
    177 			VHCI_DEBUG(6, (CE_NOTE, NULL,
    178 			    "!(sd:%p)lun "
    179 			    "becoming active...\n", (void *)sd));
    180 			drv_usecwait(STD_FO_RETRY_DELAY);
    181 			return (1);
    182 		}
    183 		cmn_err(CE_NOTE, "!Failover failed;"
    184 		    " sense key:%x, ASC: %x, "
    185 		    "ASCQ:%x", skey, asc, ascq);
    186 		return (0);
    187 	}
    188 	switch (SCBP_C(pkt)) {
    189 		case STATUS_GOOD:
    190 			break;
    191 		case STATUS_CHECK:
    192 			VHCI_DEBUG(4, (CE_WARN, NULL,
    193 			    "!(sd:%p):"
    194 			    " status returned CHECK during std"
    195 			    " path activation", (void *)sd));
    196 			return (0);
    197 		case STATUS_QFULL:
    198 			VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL "
    199 			    "status returned QFULL during std "
    200 			    "path activation for %p\n", (void *)sd));
    201 			drv_usecwait(5000);
    202 			return (1);
    203 		case STATUS_BUSY:
    204 			VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY "
    205 			    "status returned BUSY during std "
    206 			    "path activation for %p\n", (void *)sd));
    207 			drv_usecwait(5000);
    208 			return (1);
    209 		default:
    210 			VHCI_DEBUG(4, (CE_WARN, NULL,
    211 			    "!(sd:%p) Bad status returned during std "
    212 			    "activation (pkt %p, status %x)",
    213 			    (void *)sd, (void *)pkt, SCBP_C(pkt)));
    214 			return (0);
    215 	}
    216 	return (0);
    217 }
    218 
    219 /*
    220  * For now we are going to use primary/online and secondary/online.
    221  * There is no standby path returned by the dsp and we may have
    222  * to do something different for other devices that use standby
    223  */
    224 /* ARGSUSED */
    225 static int
    226 std_path_activate(struct scsi_device *sd, char *pathclass,
    227     void *ctpriv)
    228 {
    229 	struct buf			*bp;
    230 	struct scsi_pkt			*pkt;
    231 	struct scsi_address		*ap;
    232 	int				err, retry_cnt, retry_cmd_cnt;
    233 	int				mode, state, retval, xlf, preferred;
    234 
    235 	ap = &sd->sd_address;
    236 
    237 	mode = state = 0;
    238 
    239 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
    240 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_activate:"
    241 		    " failed vhci_tpgs_get_target_fo_mode\n"));
    242 		return (1);
    243 	}
    244 	if ((state == STD_ACTIVE_OPTIMIZED) ||
    245 	    (state == STD_ACTIVE_NONOPTIMIZED)) {
    246 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for %p\n",
    247 		    (void *)sd));
    248 		return (0);
    249 	}
    250 
    251 	if (mode == SCSI_EXPLICIT_FAILOVER) {
    252 		VHCI_DEBUG(4, (CE_NOTE, NULL,
    253 		    "!mode is EXPLICIT for %p xlf %x\n",
    254 		    (void *)sd, xlf));
    255 		retval = std_activate_explicit(sd, xlf);
    256 		if (retval != 0) {
    257 			VHCI_DEBUG(4, (CE_NOTE, NULL,
    258 			    "!(sd:%p)std_path_activate failed(1)\n",
    259 			    (void *)sd));
    260 			return (1);
    261 		}
    262 	} else {
    263 		VHCI_DEBUG(4, (CE_NOTE, NULL, "STD mode is IMPLICIT for %p\n",
    264 		    (void *)sd));
    265 	}
    266 
    267 	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, DEV_BSIZE,
    268 	    B_READ, NULL, NULL);
    269 	if (!bp) {
    270 		VHCI_DEBUG(4, (CE_WARN, NULL,
    271 		    "!(sd:%p)std_path_activate failed to alloc buffer",
    272 		    (void *)sd));
    273 		return (1);
    274 	}
    275 
    276 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1,
    277 	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
    278 	if (!pkt) {
    279 		VHCI_DEBUG(4, (CE_WARN, NULL,
    280 		    "!(sd:%p)std_path_activate failed to initialize packet",
    281 		    (void *)sd));
    282 		scsi_free_consistent_buf(bp);
    283 		return (1);
    284 	}
    285 
    286 	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
    287 	    SCMD_READ, 1, 1, 0);
    288 	pkt->pkt_time = 3*30;
    289 	pkt->pkt_flags |= FLAG_NOINTR;
    290 
    291 	retry_cnt = 0;
    292 	retry_cmd_cnt = 0;
    293 retry:
    294 	err = scsi_transport(pkt);
    295 	if (err != TRAN_ACCEPT) {
    296 		/*
    297 		 * Retry TRAN_BUSY till STD_FO_MAX_RETRIES is exhausted.
    298 		 * All other errors are fatal and should not be retried.
    299 		 */
    300 		if ((err == TRAN_BUSY) &&
    301 		    (retry_cnt++ < STD_FO_MAX_RETRIES)) {
    302 			drv_usecwait(STD_FO_RETRY_DELAY);
    303 			goto retry;
    304 		}
    305 		cmn_err(CE_WARN, "Failover failed, "
    306 		    "couldn't transport packet");
    307 		scsi_destroy_pkt(pkt);
    308 		scsi_free_consistent_buf(bp);
    309 		return (1);
    310 	}
    311 	switch (pkt->pkt_reason) {
    312 		case CMD_CMPLT:
    313 			retry_cmd_cnt = 0;
    314 			retval = std_process_cmplt_pkt(sd, pkt, &retry_cnt);
    315 			if (retval != 0) {
    316 				goto retry;
    317 			}
    318 			break;
    319 		case CMD_TIMEOUT:
    320 			cmn_err(CE_WARN, "!Failover failed: timed out ");
    321 			retval = 1;
    322 			break;
    323 		case CMD_INCOMPLETE:
    324 		case CMD_RESET:
    325 		case CMD_ABORTED:
    326 		case CMD_TRAN_ERR:
    327 			/*
    328 			 * Increased the number of retries when these error
    329 			 * cases are encountered.  Also added a 1 sec wait
    330 			 * before retrying.
    331 			 */
    332 			if (retry_cmd_cnt++ < STD_FO_MAX_CMD_RETRIES) {
    333 				drv_usecwait(STD_FO_CMD_RETRY_DELAY);
    334 				VHCI_DEBUG(4, (CE_WARN, NULL,
    335 				    "!Retrying path activation due to "
    336 				    "pkt reason:%x, retry cnt:%d",
    337 				    pkt->pkt_reason, retry_cmd_cnt));
    338 				goto retry;
    339 			}
    340 			/* FALLTHROUGH */
    341 		default:
    342 			cmn_err(CE_WARN, "!Path activation did not "
    343 			    "complete successfully,"
    344 			    "(pkt reason %x)", pkt->pkt_reason);
    345 			retval = 1;
    346 			break;
    347 	}
    348 
    349 
    350 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!Path activation success\n"));
    351 	scsi_destroy_pkt(pkt);
    352 	scsi_free_consistent_buf(bp);
    353 	return (retval);
    354 }
    355 
    356 /* ARGSUSED */
    357 static int std_path_deactivate(struct scsi_device *sd, char *pathclass,
    358     void *ctpriv)
    359 {
    360 	return (0);
    361 }
    362 
    363 /* ARGSUSED */
    364 static int
    365 std_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo,
    366     void *ctpriv)
    367 {
    368 	int			mode, preferred, state, xlf;
    369 
    370 	opinfo->opinfo_rev = OPINFO_REV;
    371 
    372 	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
    373 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_getopinfo:"
    374 		    " failed vhci_tpgs_get_target_fo_mode\n"));
    375 		return (1);
    376 	}
    377 
    378 	if (state == STD_ACTIVE_OPTIMIZED) {
    379 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE;
    380 	} else if (state == STD_ACTIVE_NONOPTIMIZED) {
    381 		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE_NONOPT;
    382 	} else if (state == STD_STANDBY) {
    383 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
    384 	} else if (state == STD_UNAVAILABLE) {
    385 		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
    386 	}
    387 	if (preferred) {
    388 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_PRIMARY);
    389 	} else {
    390 		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_SECONDARY);
    391 	}
    392 	VHCI_DEBUG(4, (CE_NOTE, NULL, "std_path_get_opinfo: "
    393 	    "class: %s state: %s\n", opinfo->opinfo_path_attr,
    394 	    opinfo->opinfo_path_state == SCSI_PATH_ACTIVE ?
    395 	    "ACTIVE" : "INACTIVE"));
    396 	opinfo->opinfo_xlf_capable = 0;
    397 	opinfo->opinfo_pswtch_best = 30;
    398 	opinfo->opinfo_pswtch_worst = 3*30;
    399 	opinfo->opinfo_preferred = (uint16_t)preferred;
    400 	opinfo->opinfo_mode = (uint16_t)mode;
    401 
    402 	return (0);
    403 }
    404 
    405 /* ARGSUSED */
    406 static int std_path_ping(struct scsi_device *sd, void *ctpriv)
    407 {
    408 	/*
    409 	 * For future use
    410 	 */
    411 	return (1);
    412 }
    413 
    414 /*
    415  * Analyze the sense code to determine whether failover process
    416  */
    417 /* ARGSUSED */
    418 static int
    419 std_analyze_sense(struct scsi_device *sd, uint8_t *sense,
    420     void *ctpriv)
    421 {
    422 	int rval = SCSI_SENSE_UNKNOWN;
    423 
    424 	uint8_t skey, asc, ascq;
    425 
    426 	skey = scsi_sense_key(sense);
    427 	asc = scsi_sense_asc(sense);
    428 	ascq = scsi_sense_ascq(sense);
    429 
    430 	if ((skey == KEY_UNIT_ATTENTION) &&
    431 	    (asc == STD_SCSI_ASC_STATE_CHG) &&
    432 	    (ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
    433 		rval = SCSI_SENSE_STATE_CHANGED;
    434 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
    435 		    " sense_key:%x, add_code: %x, qual_code:%x"
    436 		    " sense:%x\n", skey, asc, ascq, rval));
    437 	} else if ((skey == KEY_NOT_READY) &&
    438 	    (asc == STD_LOGICAL_UNIT_NOT_ACCESSIBLE) &&
    439 	    ((ascq == STD_TGT_PORT_UNAVAILABLE) ||
    440 	    (ascq == STD_TGT_PORT_STANDBY))) {
    441 		rval = SCSI_SENSE_INACTIVE;
    442 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
    443 		    " sense_key:%x, add_code: %x, qual_code:%x"
    444 		    " sense:%x\n", skey, asc, ascq, rval));
    445 	} else if ((skey == KEY_ILLEGAL_REQUEST) &&
    446 	    (asc == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
    447 		rval = SCSI_SENSE_NOFAILOVER;
    448 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
    449 		    " sense_key:%x, add_code: %x, qual_code:%x"
    450 		    " sense:%x\n", skey, asc, ascq, rval));
    451 	} else if ((skey == KEY_ILLEGAL_REQUEST) &&
    452 	    (asc == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
    453 		rval = SCSI_SENSE_NOFAILOVER;
    454 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
    455 		    " sense_key:%x, add_code: %x, qual_code:%x"
    456 		    " sense:%x\n", skey, asc, ascq, rval));
    457 	} else {
    458 		/*
    459 		 * At this point sense data may be for power-on-reset
    460 		 * UNIT ATTN hardware errors, vendor unqiue sense data etc.
    461 		 * For all these cases, return SCSI_SENSE_UNKNOWN.
    462 		 */
    463 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Analyze sense UNKNOWN:"
    464 		    " sense key:%x, ASC:%x, ASCQ:%x\n", skey, asc, ascq));
    465 	}
    466 
    467 	return (rval);
    468 }
    469 
    470 /* ARGSUSED */
    471 static int
    472 std_pathclass_next(char *cur, char **nxt, void *ctpriv)
    473 {
    474 	/*
    475 	 * The first phase does not have a standby path so
    476 	 * there will be no explicit failover - when standard tpgs.
    477 	 * standard defines preferred flag then we should start
    478 	 * using this as the selection mechanism - there can be
    479 	 * preferred primary standby that we should fail to first and then
    480 	 * nonpreferred secondary standby.
    481 	 */
    482 	if (cur == NULL) {
    483 		*nxt = PCLASS_PRIMARY;
    484 		return (0);
    485 	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
    486 		*nxt = PCLASS_SECONDARY;
    487 		return (0);
    488 	} else if (strcmp(cur, PCLASS_SECONDARY) == 0) {
    489 		return (ENOENT);
    490 	} else {
    491 		return (EINVAL);
    492 	}
    493 }
    494