static void print_buf(char *buf, int buf_size) { int i = 0, j; int loop, left; loop = buf_size / 8; left = buf_size % 8; VHCI_DEBUG(4, (CE_NOTE, NULL, "!buf_size: %x loop: %x left: %x", buf_size, loop, left)); for (j = 0; j < loop; j++) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!buf[%d-%d]: %x %x %x %x %x %x %x %x", i, i + 7, buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7])); i += 8; } if (left) { VHCI_DEBUG(4, (CE_CONT, NULL, "NOTICE: buf[%d-%d]:", i, i + left)); for (j = 0; j < left; j++) { VHCI_DEBUG(4, (CE_CONT, NULL, " %x", buf[i + j])); } VHCI_DEBUG(4, (CE_CONT, NULL, "\n")); } }
/* ARGSUSED */ static int std_analyze_sense(struct scsi_device *sd, uint8_t *sense, void *ctpriv) { int rval = SCSI_SENSE_UNKNOWN; uint8_t skey, asc, ascq; skey = scsi_sense_key(sense); asc = scsi_sense_asc(sense); ascq = scsi_sense_ascq(sense); if ((skey == KEY_UNIT_ATTENTION) && (asc == STD_SCSI_ASC_STATE_CHG) && (ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC)) { rval = SCSI_SENSE_STATE_CHANGED; VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:" " sense_key:%x, add_code: %x, qual_code:%x" " sense:%x\n", skey, asc, ascq, rval)); } else if ((skey == KEY_NOT_READY) && (asc == STD_LOGICAL_UNIT_NOT_ACCESSIBLE) && ((ascq == STD_TGT_PORT_UNAVAILABLE) || (ascq == STD_TGT_PORT_STANDBY))) { rval = SCSI_SENSE_INACTIVE; VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:" " sense_key:%x, add_code: %x, qual_code:%x" " sense:%x\n", skey, asc, ascq, rval)); } else if ((skey == KEY_ILLEGAL_REQUEST) && (asc == STD_SCSI_ASC_INVAL_PARAM_LIST)) { rval = SCSI_SENSE_NOFAILOVER; VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:" " sense_key:%x, add_code: %x, qual_code:%x" " sense:%x\n", skey, asc, ascq, rval)); } else if ((skey == KEY_ILLEGAL_REQUEST) && (asc == STD_SCSI_ASC_INVAL_CMD_OPCODE)) { rval = SCSI_SENSE_NOFAILOVER; VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:" " sense_key:%x, add_code: %x, qual_code:%x" " sense:%x\n", skey, asc, ascq, rval)); } else { /* * At this point sense data may be for power-on-reset * UNIT ATTN hardware errors, vendor unqiue sense data etc. * For all these cases, return SCSI_SENSE_UNKNOWN. */ VHCI_DEBUG(1, (CE_NOTE, NULL, "!Analyze sense UNKNOWN:" " sense key:%x, ASC:%x, ASCQ:%x\n", skey, asc, ascq)); } return (rval); }
static int vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp, int *mode) { struct scsi_pkt *pkt; struct scsi_inquiry inq; int retval; *mode = 0; bp->b_un.b_addr = (caddr_t)&inq; bp->b_flags = B_READ; bp->b_bcount = sizeof (inq); bp->b_resid = 0; pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0, sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC, NULL); pkt->pkt_cdbp[0] = SCMD_INQUIRY; pkt->pkt_cdbp[4] = sizeof (inq); pkt->pkt_time = 60; retval = vhci_do_scsi_cmd(pkt); scsi_destroy_pkt(pkt); if (retval == 0) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_inquiry: Failure" " returned from vhci_do_scsi_cmd")); return (1); } if (inq.inq_tpgs == TPGS_FAILOVER_NONE) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_inquiry: zero tpgs_bits")); return (1); } retval = 0; if (inq.inq_tpgs == TPGS_FAILOVER_IMPLICIT) { *mode = SCSI_IMPLICIT_FAILOVER; } else if (inq.inq_tpgs == TPGS_FAILOVER_EXPLICIT) { *mode = SCSI_EXPLICIT_FAILOVER; } else if (inq.inq_tpgs == TPGS_FAILOVER_BOTH) { *mode = SCSI_BOTH_FAILOVER; } else { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_inquiry: Illegal mode returned: %x mode: %x", inq.inq_tpgs, *mode)); retval = 1; } return (retval); }
/* ARGSUSED */ static int std_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq, void **ctpriv) { int mode, state, xlf, preferred = 0; VHCI_DEBUG(6, (CE_NOTE, NULL, "std_device_probe: vidpid %s\n", inq->inq_vid)); if (inq->inq_tpgs == TPGS_FAILOVER_NONE) { VHCI_DEBUG(4, (CE_WARN, NULL, "!std_device_probe: not a standard tpgs device")); return (SFO_DEVICE_PROBE_PHCI); } if (inq->inq_dtype == DTYPE_SEQUENTIAL) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_device_probe: Detected a " "Standard Asymmetric device " "not yet supported\n")); return (SFO_DEVICE_PROBE_PHCI); } if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) { VHCI_DEBUG(4, (CE_WARN, NULL, "!unable to fetch fo " "mode: sd(%p)", (void *) sd)); return (SFO_DEVICE_PROBE_PHCI); } if (inq->inq_tpgs == TPGS_FAILOVER_IMPLICIT) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_device_probe: Detected a " "Standard Asymmetric device " "with implicit failover\n")); return (SFO_DEVICE_PROBE_VHCI); } if (inq->inq_tpgs == TPGS_FAILOVER_EXPLICIT) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_device_probe: Detected a " "Standard Asymmetric device " "with explicit failover\n")); return (SFO_DEVICE_PROBE_VHCI); } if (inq->inq_tpgs == TPGS_FAILOVER_BOTH) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_device_probe: Detected a " "Standard Asymmetric device " "which supports both implicit and explicit failover\n")); return (SFO_DEVICE_PROBE_VHCI); } VHCI_DEBUG(1, (CE_WARN, NULL, "!std_device_probe: " "Unknown tpgs_bits: %x", inq->inq_tpgs)); return (SFO_DEVICE_PROBE_PHCI); }
/* ARGSUSED */ static int std_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo, void *ctpriv) { int mode, preferred, state, xlf; opinfo->opinfo_rev = OPINFO_REV; if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_getopinfo:" " failed vhci_tpgs_get_target_fo_mode\n")); return (1); } if (state == STD_ACTIVE_OPTIMIZED) { opinfo->opinfo_path_state = SCSI_PATH_ACTIVE; } else if (state == STD_ACTIVE_NONOPTIMIZED) { opinfo->opinfo_path_state = SCSI_PATH_ACTIVE_NONOPT; } else if (state == STD_STANDBY) { opinfo->opinfo_path_state = SCSI_PATH_INACTIVE; } else if (state == STD_UNAVAILABLE) { opinfo->opinfo_path_state = SCSI_PATH_INACTIVE; } if (preferred) { (void) strcpy(opinfo->opinfo_path_attr, PCLASS_PRIMARY); } else { (void) strcpy(opinfo->opinfo_path_attr, PCLASS_SECONDARY); } VHCI_DEBUG(4, (CE_NOTE, NULL, "std_path_get_opinfo: " "class: %s state: %s\n", opinfo->opinfo_path_attr, opinfo->opinfo_path_state == SCSI_PATH_ACTIVE ? "ACTIVE" : "INACTIVE")); opinfo->opinfo_xlf_capable = 0; opinfo->opinfo_pswtch_best = 30; opinfo->opinfo_pswtch_worst = 3*30; opinfo->opinfo_preferred = (uint16_t)preferred; opinfo->opinfo_mode = (uint16_t)mode; return (0); }
/* ARGSUSED */ int vhci_tpgs_get_target_fo_mode(struct scsi_device *sd, int *mode, int *state, int *xlf_capable, int *preferred) { int retval = 0; struct buf *bp; struct scsi_address *ap; int lu = 0, rel_tgt_port = 0, tgt_port = 0x0; VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: enter\n")); *mode = *state = *xlf_capable = 0; bp = getrbuf(KM_NOSLEEP); if (bp == NULL) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: " " failed getrbuf\n")); return (1); } ap = &sd->sd_address; if (vhci_tpgs_inquiry(ap, bp, mode)) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: " " failed vhci_tpgs_inquiry\n")); retval = 1; } else if (vhci_tpgs_page83(ap, bp, &rel_tgt_port, &tgt_port, &lu)) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: " " failed vhci_tpgs_page83\n")); retval = 1; } else if (vhci_tpgs_report_target_groups(ap, bp, rel_tgt_port, tgt_port, state, preferred)) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: " " failed vhci_tpgs_report_target_groups\n")); retval = 1; } freerbuf(bp); if (retval == 0) { VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: " "SUCCESS\n")); } return (retval); }
int vhci_tpgs_set_target_groups(struct scsi_address *ap, int set_state, int tpg_id) { struct scsi_pkt *pkt; struct buf *bp; int len, rval, ss = SCSI_SENSE_UNKNOWN; char *bufp; uint8_t *sns, skey, asc, ascq; len = 8; bp = getrbuf(KM_NOSLEEP); if (bp == NULL) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: " " failed getrbuf")); return (1); } bufp = kmem_zalloc(len, KM_NOSLEEP); if (bufp == NULL) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: " "request packet allocation for %d failed....", len)); freerbuf(bp); return (1); } bp->b_un.b_addr = bufp; bp->b_flags = B_WRITE; bp->b_bcount = len; bp->b_resid = 0; bufp[4] = (0x0f & set_state); bufp[6] = (0xff00 & tpg_id) >> 8; bufp[7] = (0x00ff & tpg_id); pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5, sizeof (struct scsi_arq_status), 0, 0, NULL, NULL); if (pkt == NULL) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups: scsi_init_pkt error\n")); freerbuf(bp); kmem_free((void *)bufp, len); return (1); } /* * Sends 1 TPG descriptor only. Hence Parameter list length pkt_cdbp[9] * is set to 8 bytes - Refer SPC3 for details. */ pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_OUT; pkt->pkt_cdbp[1] = SSVC_ACTION_SET_TARGET_PORT_GROUPS; pkt->pkt_cdbp[9] = 8; pkt->pkt_time = 90; VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups: sending set target port group:" " cdb[0/1/6/7/8/9]: %x/%x/%x/%x/%x/%x\n", pkt->pkt_cdbp[0], pkt->pkt_cdbp[1], pkt->pkt_cdbp[6], pkt->pkt_cdbp[7], pkt->pkt_cdbp[8], pkt->pkt_cdbp[9])); #ifdef DEBUG print_buf(bufp, len); #endif rval = vhci_do_scsi_cmd(pkt); if (rval == 0) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups:" " vhci_do_scsi_cmd failed\n")); freerbuf(bp); kmem_free((void *)bufp, len); scsi_destroy_pkt(pkt); return (-1); } else if ((pkt->pkt_reason == CMD_CMPLT) && (SCBP_C(pkt) == STATUS_CHECK) && (pkt->pkt_state & STATE_ARQ_DONE)) { sns = (uint8_t *) &(((struct scsi_arq_status *)(uintptr_t) (pkt->pkt_scbp))->sts_sensedata); skey = scsi_sense_key(sns); asc = scsi_sense_asc(sns); ascq = scsi_sense_ascq(sns); if ((skey == KEY_UNIT_ATTENTION) && (asc == STD_SCSI_ASC_STATE_CHG) && (ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC)) { ss = SCSI_SENSE_STATE_CHANGED; VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups:" " sense:%x, add_code: %x, qual_code:%x" " sense:%x\n", skey, asc, ascq, ss)); } else if ((skey == KEY_ILLEGAL_REQUEST) && (asc == STD_SCSI_ASC_INVAL_PARAM_LIST)) { ss = SCSI_SENSE_NOFAILOVER; VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups:" " sense:%x, add_code: %x, qual_code:%x" " sense:%x\n", skey, asc, ascq, ss)); } else if ((skey == KEY_ILLEGAL_REQUEST) && (asc == STD_SCSI_ASC_INVAL_CMD_OPCODE)) { ss = SCSI_SENSE_NOFAILOVER; VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups:" " sense_key:%x, add_code: %x, qual_code:%x" " sense:%x\n", skey, asc, ascq, rval)); } else { /* * At this point sns data may be for power-on-reset * UNIT ATTN hardware errors, vendor unqiue sense etc. * For all these cases, sense is unknown. */ ss = SCSI_SENSE_NOFAILOVER; VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups: " " sense UNKNOWN: sense key:%x, ASC:%x, ASCQ:%x\n", skey, asc, ascq)); } if (ss == SCSI_SENSE_STATE_CHANGED) { freerbuf(bp); kmem_free((void *)bufp, len); scsi_destroy_pkt(pkt); return (0); } } else if ((pkt->pkt_reason == CMD_CMPLT) && (SCBP_C(pkt) == STATUS_GOOD)) { freerbuf(bp); kmem_free((void *)bufp, len); scsi_destroy_pkt(pkt); return (0); } freerbuf(bp); kmem_free((void *)bufp, len); scsi_destroy_pkt(pkt); return (1); }
static int vhci_tpgs_report_target_groups(struct scsi_address *ap, struct buf *bp, int rel_tgt_port, int tgt_port, int *pstate, int *preferred) { struct scsi_pkt *pkt; char *ptr, *end, *bufp, *mpapi_ptr; unsigned int rtpg_len = 0; unsigned int l_tgt_port = 0, tpgs_state = 0; unsigned int tgt_port_cnt = 0, lr_tgt_port = 0; int i, len; /* * Start with buffer size of 512. * If this is found to be insufficient, required size * will be allocated and the command will be retried. */ len = 512; try_again: bufp = kmem_zalloc(len, KM_NOSLEEP); if (bufp == NULL) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_report_target_groups:" " request packet allocation for %d failed....", len)); return (1); } bp->b_un.b_addr = bufp; bp->b_flags = B_READ; bp->b_bcount = len; bp->b_resid = 0; pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5, sizeof (struct scsi_arq_status), 0, 0, NULL, NULL); if (pkt == NULL) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups: scsi_init_pkt error\n")); kmem_free((void *)bufp, len); return (1); } pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_IN; pkt->pkt_cdbp[1] = SSVC_ACTION_GET_TARGET_PORT_GROUPS; pkt->pkt_cdbp[6] = ((len >> 24) & 0xff); pkt->pkt_cdbp[7] = ((len >> 16) & 0xff); pkt->pkt_cdbp[8] = ((len >> 8) & 0xff); pkt->pkt_cdbp[9] = len & 0xff; pkt->pkt_time = 90; VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups: sending target port group:" " cdb[6/7/8/9]: %x/%x/%x/%x\n", pkt->pkt_cdbp[6], pkt->pkt_cdbp[7], pkt->pkt_cdbp[8], pkt->pkt_cdbp[9])); if (vhci_do_scsi_cmd(pkt) == 0) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:" " vhci_do_scsi_cmd failed\n")); kmem_free((void *)bufp, len); scsi_destroy_pkt(pkt); return (1); } ptr = bufp; VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:" " returned from target" " port group: buf[0/1/2/3]: %x/%x/%x/%x\n", ptr[0], ptr[1], ptr[2], ptr[3])); rtpg_len = (unsigned int)((0xff & ptr[0]) << 24); rtpg_len |= (unsigned int)((0xff & ptr[1]) << 16); rtpg_len |= (unsigned int)((0xff & ptr[2]) << 8); rtpg_len |= (unsigned int)(0xff & ptr[3]); rtpg_len += 4; if (rtpg_len > len) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:" " bufsize: %d greater than allocated buf: %d\n", rtpg_len, len)); VHCI_DEBUG(4, (CE_NOTE, NULL, "Retrying for size %d\n", rtpg_len)); kmem_free((void *)bufp, len); len = (unsigned int)(rtpg_len + 1); goto try_again; } #ifdef DEBUG print_buf(bufp, rtpg_len); #endif end = ptr + rtpg_len; ptr += 4; while (ptr < end) { mpapi_ptr = ptr; l_tgt_port = ((ptr[2] & 0xff) << 8) + (ptr[3] & 0xff); tpgs_state = ptr[0] & 0x0f; tgt_port_cnt = (ptr[7] & 0xff); VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups:" " tpgs state: %x" " tgt_group: %x count: %x\n", tpgs_state, l_tgt_port, tgt_port_cnt)); ptr += 8; for (i = 0; i < tgt_port_cnt; i++) { lr_tgt_port = 0; lr_tgt_port |= ((ptr[2] & 0Xff) << 8); lr_tgt_port |= (ptr[3] & 0xff); if ((lr_tgt_port == rel_tgt_port) && (l_tgt_port == tgt_port)) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups:" " found tgt_port: %x rel_tgt_port:%x" " tpgs_state: %x\n", tgt_port, rel_tgt_port, tpgs_state)); /* * once we have the preferred flag * and a non-optimized state flag * we will get preferred flag from the * report target groups */ if (tpgs_state == STD_ACTIVE_OPTIMIZED) { *pstate = STD_ACTIVE_OPTIMIZED; *preferred = PCLASS_PREFERRED; } else if (tpgs_state == STD_ACTIVE_NONOPTIMIZED) { *pstate = STD_ACTIVE_NONOPTIMIZED; *preferred = PCLASS_NONPREFERRED; } else if (tpgs_state == STD_STANDBY) { *pstate = STD_STANDBY; *preferred = PCLASS_NONPREFERRED; } else { *pstate = STD_UNAVAILABLE; *preferred = PCLASS_NONPREFERRED; } vhci_mpapi_update_tpg_data(ap, mpapi_ptr, rel_tgt_port); kmem_free((void *)bufp, len); scsi_destroy_pkt(pkt); return (0); } VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups:" " tgt_port: %x rel_tgt_port:%x\n", tgt_port, rel_tgt_port)); ptr += 4; } } *pstate = SCSI_PATH_INACTIVE; *preferred = PCLASS_NONPREFERRED; VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups: " "NO rel_TGTPRT MATCH!!! Assigning Default: state: %x " "preferred: %d\n", *pstate, *preferred)); kmem_free((void *)bufp, len); scsi_destroy_pkt(pkt); return (1); }
static int vhci_tpgs_page83(struct scsi_address *ap, struct buf *bp, int *rel_tgt_port, int *tgt_port, int *lu) { char *ptr, *end; struct scsi_pkt *pkt; char *bufp; unsigned int buf_len, rx_bsize; /* * lets start the buf size with 512 bytes. If this * if found to be insufficient, we can allocate * appropriate size in the next iteration. */ buf_len = 512; once_again: bufp = kmem_zalloc(buf_len, KM_NOSLEEP); if (bufp == NULL) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_page83: " "request packet allocation for %d failed....", buf_len)); return (1); } bp->b_un.b_addr = bufp; bp->b_flags = B_READ; bp->b_bcount = buf_len; bp->b_resid = 0; pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0, sizeof (struct scsi_arq_status), 0, 0, NULL, NULL); if (pkt == NULL) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_page83: Failure returned from scsi_init_pkt")); kmem_free((void *)bufp, buf_len); return (1); } pkt->pkt_cdbp[0] = SCMD_INQUIRY; pkt->pkt_cdbp[1] = 0x1; pkt->pkt_cdbp[2] = 0x83; pkt->pkt_cdbp[3] = (unsigned char)((buf_len >> 8) & 0xff); pkt->pkt_cdbp[4] = (unsigned char)(buf_len & 0xff); pkt->pkt_time = 90; if (vhci_do_scsi_cmd(pkt) == 0) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: vhci_do_scsi_cmd failed\n")); kmem_free((void *)bufp, buf_len); scsi_destroy_pkt(pkt); return (1); } /* * Now lets check if the size that was provided was * sufficient. If not, allocate the appropriate size * and retry the command again. */ rx_bsize = (((bufp[2] & 0xff) << 8) | (bufp[3] & 0xff)); rx_bsize += 4; if (rx_bsize > buf_len) { /* * Need to allocate more buf and retry again */ VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: " "bufsize: %d greater than allocated buf: %d\n", rx_bsize, buf_len)); VHCI_DEBUG(1, (CE_NOTE, NULL, "Retrying for size %d\n", rx_bsize)); kmem_free((void *)bufp, buf_len); buf_len = (unsigned int)(rx_bsize); goto once_again; } ptr = bufp; ptr += 4; /* identification descriptor 0 */ end = bufp + rx_bsize; while (ptr < end) { VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_tpgs_page83: " "desc[1/4/5/6/7]:%x %x %x %x %x\n", ptr[1], ptr[4], ptr[5], ptr[6], ptr[7])); if ((ptr[1] & 0x0f) == 0x04) { *rel_tgt_port = 0; *rel_tgt_port |= ((ptr[6] & 0xff) << 8); *rel_tgt_port |= (ptr[7] & 0xff); VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: relative target port: %x\n", *rel_tgt_port)); } else if ((ptr[1] & 0x0f) == 0x05) { *tgt_port = 0; *tgt_port = ((ptr[6] & 0xff) << 8); *tgt_port |= (ptr[7] & 0xff); VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: target port: %x\n", *tgt_port)); } else if ((ptr[1] & 0x0f) == 0x06) { *lu = 0; *lu |= ((ptr[6] & 0xff)<< 8); *lu |= (ptr[7] & 0xff); VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: logical unit: %x\n", *lu)); } ptr += ptr[3] + 4; /* next identification descriptor */ } kmem_free((void *)bufp, buf_len); scsi_destroy_pkt(pkt); return (0); }
/* ARGSUSED */ static int std_path_activate(struct scsi_device *sd, char *pathclass, void *ctpriv) { struct buf *bp; struct scsi_pkt *pkt; struct scsi_address *ap; int err, retry_cnt, retry_cmd_cnt; int mode, state, retval, xlf, preferred; ap = &sd->sd_address; mode = state = 0; if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_activate:" " failed vhci_tpgs_get_target_fo_mode\n")); return (1); } if ((state == STD_ACTIVE_OPTIMIZED) || (state == STD_ACTIVE_NONOPTIMIZED)) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for %p\n", (void *)sd)); return (0); } if (mode == SCSI_EXPLICIT_FAILOVER) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!mode is EXPLICIT for %p xlf %x\n", (void *)sd, xlf)); retval = std_activate_explicit(sd, xlf); if (retval != 0) { VHCI_DEBUG(4, (CE_NOTE, NULL, "!(sd:%p)std_path_activate failed(1)\n", (void *)sd)); return (1); } } else { VHCI_DEBUG(4, (CE_NOTE, NULL, "STD mode is IMPLICIT for %p\n", (void *)sd)); } bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, DEV_BSIZE, B_READ, NULL, NULL); if (!bp) { VHCI_DEBUG(4, (CE_WARN, NULL, "!(sd:%p)std_path_activate failed to alloc buffer", (void *)sd)); return (1); } pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1, sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL); if (!pkt) { VHCI_DEBUG(4, (CE_WARN, NULL, "!(sd:%p)std_path_activate failed to initialize packet", (void *)sd)); scsi_free_consistent_buf(bp); return (1); } (void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp, SCMD_READ, 1, 1, 0); pkt->pkt_time = 3*30; pkt->pkt_flags |= FLAG_NOINTR; retry_cnt = 0; retry_cmd_cnt = 0; retry: err = scsi_transport(pkt); if (err != TRAN_ACCEPT) { /* * Retry TRAN_BUSY till STD_FO_MAX_RETRIES is exhausted. * All other errors are fatal and should not be retried. */ if ((err == TRAN_BUSY) && (retry_cnt++ < STD_FO_MAX_RETRIES)) { drv_usecwait(STD_FO_RETRY_DELAY); goto retry; } cmn_err(CE_WARN, "Failover failed, " "couldn't transport packet"); scsi_destroy_pkt(pkt); scsi_free_consistent_buf(bp); return (1); } switch (pkt->pkt_reason) { case CMD_CMPLT: /* * Re-initialize retry_cmd_cnt. Allow transport and * cmd errors to go through a full retry count when * these are encountered. This way TRAN/CMD errors * retry count is not exhausted due to CMD_CMPLTs * delay. This allows the system * to brave a hick-up on the link at any given time, * while waiting for the fo to complete. */ retry_cmd_cnt = 0; if (std_process_cmplt_pkt(sd, pkt, &retry_cnt, &retval) != 0) { goto retry; } break; case CMD_TIMEOUT: cmn_err(CE_WARN, "!Failover failed: timed out "); retval = 1; break; case CMD_INCOMPLETE: case CMD_RESET: case CMD_ABORTED: case CMD_TRAN_ERR: /* * Increased the number of retries when these error * cases are encountered. Also added a 1 sec wait * before retrying. */ if (retry_cmd_cnt++ < STD_FO_MAX_CMD_RETRIES) { drv_usecwait(STD_FO_CMD_RETRY_DELAY); VHCI_DEBUG(4, (CE_WARN, NULL, "!Retrying path activation due to " "pkt reason:%x, retry cnt:%d", pkt->pkt_reason, retry_cmd_cnt)); goto retry; } /* FALLTHROUGH */ default: cmn_err(CE_WARN, "!Path activation did not " "complete successfully," "(pkt reason %x)", pkt->pkt_reason); retval = 1; break; } scsi_destroy_pkt(pkt); scsi_free_consistent_buf(bp); return (retval); }
/* * Process the packet reason of CMD_PKT_CMPLT - return 0 if no * retry and 1 if a retry should be done */ static int std_process_cmplt_pkt(struct scsi_device *sd, struct scsi_pkt *pkt, int *retry_cnt, int *retval) { *retval = 1; /* fail */ switch (SCBP_C(pkt)) { case STATUS_GOOD: *retval = 0; break; case STATUS_CHECK: if (pkt->pkt_state & STATE_ARQ_DONE) { uint8_t *sns, skey, asc, ascq; sns = (uint8_t *) &(((struct scsi_arq_status *)(uintptr_t) (pkt->pkt_scbp))->sts_sensedata); skey = scsi_sense_key(sns); asc = scsi_sense_asc(sns); ascq = scsi_sense_ascq(sns); if (skey == KEY_UNIT_ATTENTION) { /* * tpgs access state changed */ if (asc == STD_SCSI_ASC_STATE_CHG && ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC) { /* XXX: update path info? */ cmn_err(CE_WARN, "!Device failover" " state change"); } return (1); } else if (skey == KEY_NOT_READY) { if (asc == STD_LOGICAL_UNIT_NOT_ACCESSIBLE && ascq == STD_TGT_PORT_STANDBY) { /* * Don't retry on the path * which is indicated as * standby, return failure. */ return (0); } else if ((*retry_cnt)++ >= STD_FO_MAX_RETRIES) { cmn_err(CE_WARN, "!Device failover failed: " "timed out waiting for " "path to become active"); return (0); } VHCI_DEBUG(6, (CE_NOTE, NULL, "!(sd:%p)lun becoming active...\n", (void *)sd)); drv_usecwait(STD_FO_RETRY_DELAY); return (1); } cmn_err(CE_NOTE, "!Failover failed;" " sense key:%x, ASC: %x, " "ASCQ:%x", skey, asc, ascq); return (0); } VHCI_DEBUG(4, (CE_WARN, NULL, "!(sd:%p):" " status returned CHECK during std" " path activation", (void *)sd)); return (0); case STATUS_QFULL: VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL " "status returned QFULL during std " "path activation for %p\n", (void *)sd)); drv_usecwait(5000); return (1); case STATUS_BUSY: VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY " "status returned BUSY during std " "path activation for %p\n", (void *)sd)); drv_usecwait(5000); return (1); default: VHCI_DEBUG(4, (CE_WARN, NULL, "!(sd:%p) Bad status returned during std " "activation (pkt %p, status %x)", (void *)sd, (void *)pkt, SCBP_C(pkt))); return (0); } return (0); }