10205780bSrralphs /*
20205780bSrralphs  * CDDL HEADER START
30205780bSrralphs  *
40205780bSrralphs  * The contents of this file are subject to the terms of the
50205780bSrralphs  * Common Development and Distribution License (the "License").
60205780bSrralphs  * You may not use this file except in compliance with the License.
70205780bSrralphs  *
80205780bSrralphs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90205780bSrralphs  * or http://www.opensolaris.org/os/licensing.
100205780bSrralphs  * See the License for the specific language governing permissions
110205780bSrralphs  * and limitations under the License.
120205780bSrralphs  *
130205780bSrralphs  * When distributing Covered Code, include this CDDL HEADER in each
140205780bSrralphs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150205780bSrralphs  * If applicable, add the following below this CDDL HEADER, with the
160205780bSrralphs  * fields enclosed by brackets "[]" replaced with your own identifying
170205780bSrralphs  * information: Portions Copyright [yyyy] [name of copyright owner]
180205780bSrralphs  *
190205780bSrralphs  * CDDL HEADER END
200205780bSrralphs  */
210205780bSrralphs /*
220c45178bSwl  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230205780bSrralphs  * Use is subject to license terms.
240205780bSrralphs  */
250205780bSrralphs 
260205780bSrralphs #include <sys/conf.h>
270205780bSrralphs #include <sys/file.h>
280205780bSrralphs #include <sys/ddi.h>
290205780bSrralphs #include <sys/sunddi.h>
300205780bSrralphs #include <sys/scsi/scsi.h>
310205780bSrralphs #include <sys/scsi/adapters/scsi_vhci.h>
320205780bSrralphs #include <sys/scsi/adapters/scsi_vhci_tpgs.h>
330205780bSrralphs 
340205780bSrralphs /*
350205780bSrralphs  * External function definitions
360205780bSrralphs  */
37b1a03ab1SVictor Li extern void vhci_mpapi_update_tpg_data(struct scsi_address *, char *, int);
380205780bSrralphs 
390205780bSrralphs 
400205780bSrralphs 
410205780bSrralphs static int vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp,
420205780bSrralphs     int *mode);
430205780bSrralphs static int vhci_tpgs_page83(struct scsi_address *ap, struct buf *bp,
440205780bSrralphs     int *rel_tgt_port, int *tgt_port, int *lu);
450205780bSrralphs static void print_buf(char *buf, int buf_size);
460205780bSrralphs static int vhci_tpgs_report_target_groups(struct scsi_address *ap,
470205780bSrralphs     struct buf *bp, int rel_tgt_port, int tgt_port, int *pstate,
480205780bSrralphs     int *preferred);
490205780bSrralphs 
500205780bSrralphs int
vhci_tpgs_set_target_groups(struct scsi_address * ap,int set_state,int tpg_id)510205780bSrralphs vhci_tpgs_set_target_groups(struct scsi_address *ap, int set_state,
520205780bSrralphs     int tpg_id)
530205780bSrralphs {
540205780bSrralphs 	struct scsi_pkt			*pkt;
550205780bSrralphs 	struct buf			*bp;
560205780bSrralphs 	int				len, rval, ss = SCSI_SENSE_UNKNOWN;
570205780bSrralphs 	char				*bufp;
580c45178bSwl 	uint8_t				*sns, skey, asc, ascq;
590205780bSrralphs 
600205780bSrralphs 	len = 8;
610205780bSrralphs 
620205780bSrralphs 	bp = getrbuf(KM_NOSLEEP);
630205780bSrralphs 	if (bp == NULL) {
640205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: "
650205780bSrralphs 		    " failed getrbuf"));
660205780bSrralphs 		return (1);
670205780bSrralphs 	}
680205780bSrralphs 
690205780bSrralphs 	bufp = kmem_zalloc(len, KM_NOSLEEP);
700205780bSrralphs 	if (bufp == NULL) {
710205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: "
720205780bSrralphs 		    "request packet allocation for %d failed....", len));
730205780bSrralphs 		freerbuf(bp);
740205780bSrralphs 		return (1);
750205780bSrralphs 	}
760205780bSrralphs 
770205780bSrralphs 	bp->b_un.b_addr = bufp;
78cc25db92SChris Liu 	bp->b_flags = B_WRITE;
790205780bSrralphs 	bp->b_bcount = len;
800205780bSrralphs 	bp->b_resid = 0;
810205780bSrralphs 
820205780bSrralphs 	bufp[4] = (0x0f & set_state);
830205780bSrralphs 	bufp[6] = (0xff00 & tpg_id) >> 8;
840205780bSrralphs 	bufp[7] = (0x00ff & tpg_id);
850205780bSrralphs 
860205780bSrralphs 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
870205780bSrralphs 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
880205780bSrralphs 
890205780bSrralphs 	if (pkt == NULL) {
900205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL,
910205780bSrralphs 		    "!vhci_tpgs_set_target_groups: scsi_init_pkt error\n"));
920205780bSrralphs 		freerbuf(bp);
930205780bSrralphs 		kmem_free((void *)bufp, len);
940205780bSrralphs 		return (1);
950205780bSrralphs 	}
960205780bSrralphs 
970205780bSrralphs 	/*
980205780bSrralphs 	 * Sends 1 TPG descriptor only. Hence Parameter list length pkt_cdbp[9]
990205780bSrralphs 	 * is set to 8 bytes - Refer SPC3 for details.
1000205780bSrralphs 	 */
1010205780bSrralphs 	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_OUT;
1020205780bSrralphs 	pkt->pkt_cdbp[1] = SSVC_ACTION_SET_TARGET_PORT_GROUPS;
1030205780bSrralphs 	pkt->pkt_cdbp[9] = 8;
1040205780bSrralphs 	pkt->pkt_time = 90;
1050205780bSrralphs 
1060205780bSrralphs 	VHCI_DEBUG(1, (CE_NOTE, NULL,
1070205780bSrralphs 	    "!vhci_tpgs_set_target_groups: sending set target port group:"
1080205780bSrralphs 	    " cdb[0/1/6/7/8/9]: %x/%x/%x/%x/%x/%x\n", pkt->pkt_cdbp[0],
1090205780bSrralphs 	    pkt->pkt_cdbp[1], pkt->pkt_cdbp[6], pkt->pkt_cdbp[7],
1100205780bSrralphs 	    pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
1110205780bSrralphs 
1120205780bSrralphs #ifdef DEBUG
1130205780bSrralphs 	print_buf(bufp, len);
1140205780bSrralphs #endif
1150205780bSrralphs 	rval = vhci_do_scsi_cmd(pkt);
1160205780bSrralphs 
1170205780bSrralphs 	if (rval == 0) {
1180205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups:"
1190205780bSrralphs 		    " vhci_do_scsi_cmd failed\n"));
1200205780bSrralphs 		freerbuf(bp);
1210205780bSrralphs 		kmem_free((void *)bufp, len);
1220205780bSrralphs 		scsi_destroy_pkt(pkt);
1230205780bSrralphs 		return (-1);
1240205780bSrralphs 	} else if ((pkt->pkt_reason == CMD_CMPLT) &&
1250205780bSrralphs 	    (SCBP_C(pkt) == STATUS_CHECK) &&
1260205780bSrralphs 	    (pkt->pkt_state & STATE_ARQ_DONE)) {
1270c45178bSwl 		sns = (uint8_t *)
1280c45178bSwl 		    &(((struct scsi_arq_status *)(uintptr_t)
1290205780bSrralphs 		    (pkt->pkt_scbp))->sts_sensedata);
1300c45178bSwl 		skey = scsi_sense_key(sns);
1310c45178bSwl 		asc = scsi_sense_asc(sns);
1320c45178bSwl 		ascq = scsi_sense_ascq(sns);
1330205780bSrralphs 
1340c45178bSwl 		if ((skey == KEY_UNIT_ATTENTION) &&
1350c45178bSwl 		    (asc == STD_SCSI_ASC_STATE_CHG) &&
1360c45178bSwl 		    (ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
1370205780bSrralphs 			ss = SCSI_SENSE_STATE_CHANGED;
1380205780bSrralphs 			VHCI_DEBUG(4, (CE_NOTE, NULL,
1390205780bSrralphs 			    "!vhci_tpgs_set_target_groups:"
1400205780bSrralphs 			    " sense:%x, add_code: %x, qual_code:%x"
1410c45178bSwl 			    " sense:%x\n", skey, asc, ascq, ss));
1420c45178bSwl 		} else if ((skey == KEY_ILLEGAL_REQUEST) &&
1430c45178bSwl 		    (asc == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
1440205780bSrralphs 			ss = SCSI_SENSE_NOFAILOVER;
1450205780bSrralphs 			VHCI_DEBUG(1, (CE_NOTE, NULL,
1460205780bSrralphs 			    "!vhci_tpgs_set_target_groups:"
1470205780bSrralphs 			    " sense:%x, add_code: %x, qual_code:%x"
1480c45178bSwl 			    " sense:%x\n", skey, asc, ascq, ss));
1490c45178bSwl 		} else if ((skey == KEY_ILLEGAL_REQUEST) &&
1500c45178bSwl 		    (asc == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
1510205780bSrralphs 			ss = SCSI_SENSE_NOFAILOVER;
1520205780bSrralphs 			VHCI_DEBUG(1, (CE_NOTE, NULL,
1530205780bSrralphs 			    "!vhci_tpgs_set_target_groups:"
1540205780bSrralphs 			    " sense_key:%x, add_code: %x, qual_code:%x"
1550c45178bSwl 			    " sense:%x\n", skey, asc, ascq, rval));
1560205780bSrralphs 		} else {
1570205780bSrralphs 			/*
1580205780bSrralphs 			 * At this point sns data may be for power-on-reset
1590205780bSrralphs 			 * UNIT ATTN hardware errors, vendor unqiue sense etc.
1600205780bSrralphs 			 * For all these cases, sense is unknown.
1610205780bSrralphs 			 */
1620205780bSrralphs 			ss = SCSI_SENSE_NOFAILOVER;
1630205780bSrralphs 			VHCI_DEBUG(1, (CE_NOTE, NULL,
1640205780bSrralphs 			    "!vhci_tpgs_set_target_groups: "
1650205780bSrralphs 			    " sense UNKNOWN: sense key:%x, ASC:%x, ASCQ:%x\n",
1660c45178bSwl 			    skey, asc, ascq));
1670205780bSrralphs 		}
1680205780bSrralphs 
1690205780bSrralphs 		if (ss == SCSI_SENSE_STATE_CHANGED) {
1700205780bSrralphs 			freerbuf(bp);
1710205780bSrralphs 			kmem_free((void *)bufp, len);
1720205780bSrralphs 			scsi_destroy_pkt(pkt);
1730205780bSrralphs 			return (0);
1740205780bSrralphs 		}
175cc25db92SChris Liu 	} else if ((pkt->pkt_reason == CMD_CMPLT) &&
176cc25db92SChris Liu 	    (SCBP_C(pkt) == STATUS_GOOD)) {
177cc25db92SChris Liu 		freerbuf(bp);
178cc25db92SChris Liu 		kmem_free((void *)bufp, len);
179cc25db92SChris Liu 		scsi_destroy_pkt(pkt);
180cc25db92SChris Liu 		return (0);
1810205780bSrralphs 	}
1820205780bSrralphs 
1830205780bSrralphs 	freerbuf(bp);
1840205780bSrralphs 	kmem_free((void *)bufp, len);
1850205780bSrralphs 	scsi_destroy_pkt(pkt);
1860205780bSrralphs 	return (1);
1870205780bSrralphs }
1880205780bSrralphs 
1890205780bSrralphs /*
1900205780bSrralphs  * get the failover mode, ownership and if it has extended failover
1910205780bSrralphs  * capability. The mode(bits5-4/byte5) is defined as implicit, explicit, or
1920205780bSrralphs  * both.  The state is defined as online-optimized(0h),
1930205780bSrralphs  * online-nonoptimized(1h), standby(2h), offline(3h),
1940205780bSrralphs  * and transitioning(fh). Currently, there is online,
1950205780bSrralphs  * standby, and offline(defined in sunmdi.h).
1960205780bSrralphs  * Online-nonoptimized will be a mode of secondary
1970205780bSrralphs  * and an ownership of online. Thought about using a different mode but
1980205780bSrralphs  * it appears the states are really for the states for secondary mode.
1990205780bSrralphs  * We currently have IS_ONLINING, IS_OFFLINING - should we have TRANSITIONING
2000205780bSrralphs  * to mean from online-optimized to online-nonoptimized or does onlining
2010205780bSrralphs  * cover this?
2020205780bSrralphs  */
2030205780bSrralphs /* ARGSUSED */
2040205780bSrralphs int
vhci_tpgs_get_target_fo_mode(struct scsi_device * sd,int * mode,int * state,int * xlf_capable,int * preferred)2050205780bSrralphs vhci_tpgs_get_target_fo_mode(struct scsi_device *sd, int *mode,
2060205780bSrralphs     int *state, int *xlf_capable, int *preferred)
2070205780bSrralphs {
2080205780bSrralphs 	int			retval = 0;
2090205780bSrralphs 	struct buf		*bp;
2100205780bSrralphs 	struct scsi_address	*ap;
2110205780bSrralphs 	int			lu = 0, rel_tgt_port = 0, tgt_port = 0x0;
2120205780bSrralphs 
2130205780bSrralphs 	VHCI_DEBUG(6, (CE_NOTE, NULL,
2140205780bSrralphs 	    "!vhci_tpgs_get_target_fo_mode: enter\n"));
2150205780bSrralphs 	*mode = *state = *xlf_capable = 0;
2160205780bSrralphs 	bp = getrbuf(KM_NOSLEEP);
2170205780bSrralphs 	if (bp == NULL) {
2180205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
2190205780bSrralphs 		    " failed getrbuf\n"));
2200205780bSrralphs 		return (1);
2210205780bSrralphs 	}
2220205780bSrralphs 
2230205780bSrralphs 	ap = &sd->sd_address;
2240205780bSrralphs 	if (vhci_tpgs_inquiry(ap, bp, mode)) {
2250205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
2260205780bSrralphs 		    " failed vhci_tpgs_inquiry\n"));
2270205780bSrralphs 		retval = 1;
2280205780bSrralphs 	} else if (vhci_tpgs_page83(ap, bp, &rel_tgt_port, &tgt_port, &lu)) {
2290205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
2300205780bSrralphs 		    " failed vhci_tpgs_page83\n"));
2310205780bSrralphs 		retval = 1;
2320205780bSrralphs 	} else if (vhci_tpgs_report_target_groups(ap, bp, rel_tgt_port,
2330205780bSrralphs 	    tgt_port, state, preferred)) {
2340205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
2350205780bSrralphs 		    " failed vhci_tpgs_report_target_groups\n"));
2360205780bSrralphs 		retval = 1;
2370205780bSrralphs 	}
2380205780bSrralphs 
2390205780bSrralphs 	freerbuf(bp);
2400205780bSrralphs 	if (retval == 0) {
2410205780bSrralphs 		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
2420205780bSrralphs 		    "SUCCESS\n"));
2430205780bSrralphs 	}
2440205780bSrralphs 	return (retval);
2450205780bSrralphs }
2460205780bSrralphs 
2470205780bSrralphs static int
vhci_tpgs_inquiry(struct scsi_address * ap,struct buf * bp,int * mode)2480205780bSrralphs vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp, int *mode)
2490205780bSrralphs {
2500205780bSrralphs 	struct scsi_pkt		*pkt;
2510205780bSrralphs 	struct scsi_inquiry	inq;
2520205780bSrralphs 	int			retval;
2530205780bSrralphs 
2540205780bSrralphs 	*mode = 0;
2550205780bSrralphs 	bp->b_un.b_addr = (caddr_t)&inq;
2560205780bSrralphs 	bp->b_flags = B_READ;
2570205780bSrralphs 	bp->b_bcount = sizeof (inq);
2580205780bSrralphs 	bp->b_resid = 0;
2590205780bSrralphs 
2600205780bSrralphs 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
2610205780bSrralphs 	    sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC, NULL);
262*d35e9352SChris Liu 	if (pkt == NULL) {
263*d35e9352SChris Liu 		VHCI_DEBUG(1, (CE_WARN, NULL,
264*d35e9352SChris Liu 		    "!vhci_tpgs_inquiry: Failure returned from scsi_init_pkt"));
265*d35e9352SChris Liu 		return (1);
266*d35e9352SChris Liu 	}
2670205780bSrralphs 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
2680205780bSrralphs 	pkt->pkt_cdbp[4] = sizeof (inq);
2690205780bSrralphs 	pkt->pkt_time = 60;
2700205780bSrralphs 
2710205780bSrralphs 	retval = vhci_do_scsi_cmd(pkt);
2720205780bSrralphs 	scsi_destroy_pkt(pkt);
2730205780bSrralphs 	if (retval == 0) {
2740205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_inquiry: Failure"
2750205780bSrralphs 		    " returned from vhci_do_scsi_cmd"));
2760205780bSrralphs 		return (1);
2770205780bSrralphs 	}
2780205780bSrralphs 
279d91393a8SChris Horne 	if (inq.inq_tpgs == TPGS_FAILOVER_NONE) {
2800205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL,
2810205780bSrralphs 		    "!vhci_tpgs_inquiry: zero tpgs_bits"));
2820205780bSrralphs 		return (1);
2830205780bSrralphs 	}
2840205780bSrralphs 	retval = 0;
285d91393a8SChris Horne 	if (inq.inq_tpgs == TPGS_FAILOVER_IMPLICIT) {
2860205780bSrralphs 		*mode = SCSI_IMPLICIT_FAILOVER;
287d91393a8SChris Horne 	} else if (inq.inq_tpgs == TPGS_FAILOVER_EXPLICIT) {
2880205780bSrralphs 		*mode = SCSI_EXPLICIT_FAILOVER;
289d91393a8SChris Horne 	} else if (inq.inq_tpgs == TPGS_FAILOVER_BOTH) {
2900205780bSrralphs 		*mode = SCSI_BOTH_FAILOVER;
2910205780bSrralphs 	} else {
2920205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL,
2930205780bSrralphs 		    "!vhci_tpgs_inquiry: Illegal mode returned: %x mode: %x",
2940205780bSrralphs 		    inq.inq_tpgs, *mode));
2950205780bSrralphs 		retval = 1;
2960205780bSrralphs 	}
2970205780bSrralphs 
2980205780bSrralphs 	return (retval);
2990205780bSrralphs }
3000205780bSrralphs 
3010205780bSrralphs static int
vhci_tpgs_page83(struct scsi_address * ap,struct buf * bp,int * rel_tgt_port,int * tgt_port,int * lu)3020205780bSrralphs vhci_tpgs_page83(struct scsi_address *ap, struct buf *bp,
3030205780bSrralphs 	int *rel_tgt_port, int *tgt_port, int *lu)
3040205780bSrralphs {
3050205780bSrralphs 	char			*ptr, *end;
3060205780bSrralphs 	struct scsi_pkt		*pkt;
3070205780bSrralphs 	char			*bufp;
3080205780bSrralphs 	unsigned int		buf_len, rx_bsize;
3090205780bSrralphs 
3100205780bSrralphs 	/*
3110205780bSrralphs 	 * lets start the buf size with 512 bytes. If this
3120205780bSrralphs 	 * if found to be insufficient, we can allocate
3130205780bSrralphs 	 * appropriate size in the next iteration.
3140205780bSrralphs 	 */
3150205780bSrralphs 	buf_len = 512;
3160205780bSrralphs 
3170205780bSrralphs once_again:
3180205780bSrralphs 	bufp = kmem_zalloc(buf_len, KM_NOSLEEP);
3190205780bSrralphs 	if (bufp == NULL) {
3200205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_page83: "
3210205780bSrralphs 		    "request packet allocation for %d failed....",
3220205780bSrralphs 		    buf_len));
3230205780bSrralphs 		return (1);
3240205780bSrralphs 	}
3250205780bSrralphs 
3260205780bSrralphs 
3270205780bSrralphs 	bp->b_un.b_addr = bufp;
3280205780bSrralphs 	bp->b_flags = B_READ;
3290205780bSrralphs 	bp->b_bcount = buf_len;
3300205780bSrralphs 	bp->b_resid = 0;
3310205780bSrralphs 
3320205780bSrralphs 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
3330205780bSrralphs 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
3340205780bSrralphs 	if (pkt == NULL) {
3350205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL,
3360205780bSrralphs 		    "!vhci_tpgs_page83: Failure returned from scsi_init_pkt"));
3370205780bSrralphs 		kmem_free((void *)bufp, buf_len);
3380205780bSrralphs 		return (1);
3390205780bSrralphs 	}
3400205780bSrralphs 
3410205780bSrralphs 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
3420205780bSrralphs 	pkt->pkt_cdbp[1] = 0x1;
3430205780bSrralphs 	pkt->pkt_cdbp[2] = 0x83;
3440205780bSrralphs 	pkt->pkt_cdbp[3] = (unsigned char)((buf_len >> 8) & 0xff);
3450205780bSrralphs 	pkt->pkt_cdbp[4] = (unsigned char)(buf_len & 0xff);
3460205780bSrralphs 	pkt->pkt_time = 90;
3470205780bSrralphs 
3480205780bSrralphs 	if (vhci_do_scsi_cmd(pkt) == 0) {
3490205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL,
3500205780bSrralphs 		    "!vhci_tpgs_page83: vhci_do_scsi_cmd failed\n"));
3510205780bSrralphs 		kmem_free((void *)bufp, buf_len);
3520205780bSrralphs 		scsi_destroy_pkt(pkt);
3530205780bSrralphs 		return (1);
3540205780bSrralphs 	}
3550205780bSrralphs 
3560205780bSrralphs 	/*
3570205780bSrralphs 	 * Now lets check if the size that was provided was
3580205780bSrralphs 	 * sufficient. If not, allocate the appropriate size
3590205780bSrralphs 	 * and retry the command again.
3600205780bSrralphs 	 */
3610205780bSrralphs 	rx_bsize = (((bufp[2] & 0xff) << 8) | (bufp[3] & 0xff));
3620205780bSrralphs 	rx_bsize += 4;
3630205780bSrralphs 	if (rx_bsize > buf_len) {
3640205780bSrralphs 		/*
3650205780bSrralphs 		 * Need to allocate more buf and retry again
3660205780bSrralphs 		 */
3670205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: "
3680205780bSrralphs 		    "bufsize: %d greater than allocated buf: %d\n",
3690205780bSrralphs 		    rx_bsize, buf_len));
3700205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "Retrying for size %d\n",
3710205780bSrralphs 		    rx_bsize));
3720205780bSrralphs 		kmem_free((void *)bufp, buf_len);
3730205780bSrralphs 		buf_len = (unsigned int)(rx_bsize);
3740205780bSrralphs 		goto once_again;
3750205780bSrralphs 	}
3760205780bSrralphs 
3770205780bSrralphs 	ptr = bufp;
3780205780bSrralphs 	ptr += 4; /* identification descriptor 0 */
3790205780bSrralphs 	end = bufp + rx_bsize;
3800205780bSrralphs 	while (ptr < end) {
3810205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_tpgs_page83: "
3820205780bSrralphs 		    "desc[1/4/5/6/7]:%x %x %x %x %x\n",
3830205780bSrralphs 		    ptr[1], ptr[4], ptr[5], ptr[6], ptr[7]));
3840205780bSrralphs 		if ((ptr[1] & 0x0f) == 0x04) {
3850205780bSrralphs 			*rel_tgt_port = 0;
3860205780bSrralphs 			*rel_tgt_port |= ((ptr[6] & 0xff) << 8);
3870205780bSrralphs 			*rel_tgt_port |= (ptr[7] & 0xff);
3880205780bSrralphs 			VHCI_DEBUG(1, (CE_NOTE, NULL,
3890205780bSrralphs 			    "!vhci_tpgs_page83: relative target port: %x\n",
3900205780bSrralphs 			    *rel_tgt_port));
3910205780bSrralphs 		} else if ((ptr[1] & 0x0f) == 0x05) {
3920205780bSrralphs 			*tgt_port = 0;
3930205780bSrralphs 			*tgt_port = ((ptr[6] & 0xff) << 8);
3940205780bSrralphs 			*tgt_port |= (ptr[7] & 0xff);
3950205780bSrralphs 			VHCI_DEBUG(1, (CE_NOTE, NULL,
3960205780bSrralphs 			    "!vhci_tpgs_page83: target port: %x\n", *tgt_port));
3970205780bSrralphs 		} else if ((ptr[1] & 0x0f) == 0x06) {
3980205780bSrralphs 			*lu = 0;
3990205780bSrralphs 			*lu |= ((ptr[6] & 0xff)<< 8);
4000205780bSrralphs 			*lu |= (ptr[7] & 0xff);
4010205780bSrralphs 			VHCI_DEBUG(1, (CE_NOTE, NULL,
4020205780bSrralphs 			    "!vhci_tpgs_page83: logical unit: %x\n", *lu));
4030205780bSrralphs 		}
4040205780bSrralphs 		ptr += ptr[3] + 4;  /* next identification descriptor */
4050205780bSrralphs 	}
4060205780bSrralphs 	kmem_free((void *)bufp, buf_len);
4070205780bSrralphs 	scsi_destroy_pkt(pkt);
4080205780bSrralphs 	return (0);
4090205780bSrralphs }
4100205780bSrralphs 
4110205780bSrralphs #ifdef DEBUG
4120205780bSrralphs static void
print_buf(char * buf,int buf_size)4130205780bSrralphs print_buf(char *buf, int buf_size)
4140205780bSrralphs {
4150205780bSrralphs 	int		i = 0, j;
4160205780bSrralphs 	int		loop, left;
4170205780bSrralphs 
4180205780bSrralphs 	loop = buf_size / 8;
4190205780bSrralphs 	left = buf_size % 8;
4200205780bSrralphs 
4210205780bSrralphs 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!buf_size: %x loop: %x left: %x",
4220205780bSrralphs 	    buf_size, loop, left));
4230205780bSrralphs 
4240205780bSrralphs 	for (j = 0; j < loop; j++) {
4250205780bSrralphs 		VHCI_DEBUG(4, (CE_NOTE, NULL,
4260205780bSrralphs 		    "!buf[%d-%d]: %x %x %x %x %x %x %x %x",
4270205780bSrralphs 		    i, i + 7, buf[i], buf[i+1], buf[i+2], buf[i+3],
4280205780bSrralphs 		    buf[i+4], buf[i+5], buf[i+6], buf[i+7]));
4290205780bSrralphs 		i += 8;
4300205780bSrralphs 	}
4310205780bSrralphs 
4320205780bSrralphs 	if (left) {
4330205780bSrralphs 		VHCI_DEBUG(4, (CE_CONT, NULL,
4340205780bSrralphs 		    "NOTICE: buf[%d-%d]:", i, i + left));
4350205780bSrralphs 		for (j = 0; j < left; j++) {
4360205780bSrralphs 			VHCI_DEBUG(4, (CE_CONT, NULL, " %x", buf[i + j]));
4370205780bSrralphs 		}
4380205780bSrralphs 		VHCI_DEBUG(4, (CE_CONT, NULL, "\n"));
4390205780bSrralphs 	}
4400205780bSrralphs }
4410205780bSrralphs #endif
4420205780bSrralphs 
4430205780bSrralphs 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)4440205780bSrralphs vhci_tpgs_report_target_groups(struct scsi_address *ap, struct buf *bp,
4450205780bSrralphs 	int rel_tgt_port, int tgt_port, int *pstate, int *preferred)
4460205780bSrralphs {
4470205780bSrralphs 	struct scsi_pkt		*pkt;
4480205780bSrralphs 	char			*ptr, *end, *bufp, *mpapi_ptr;
4490205780bSrralphs 	unsigned int		rtpg_len = 0;
4500205780bSrralphs 	unsigned int		l_tgt_port = 0, tpgs_state = 0;
4510205780bSrralphs 	unsigned int		tgt_port_cnt = 0, lr_tgt_port = 0;
4520205780bSrralphs 	int			i, len;
4530205780bSrralphs 
4540205780bSrralphs 	/*
4550205780bSrralphs 	 * Start with buffer size of 512.
4560205780bSrralphs 	 * If this is found to be insufficient, required size
4570205780bSrralphs 	 * will be allocated and the command will be retried.
4580205780bSrralphs 	 */
4590205780bSrralphs 	len = 512;
4600205780bSrralphs 
4610205780bSrralphs try_again:
4620205780bSrralphs 	bufp = kmem_zalloc(len, KM_NOSLEEP);
4630205780bSrralphs 	if (bufp == NULL) {
4640205780bSrralphs 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_report_target_groups:"
4650205780bSrralphs 		    " request packet allocation for %d failed....", len));
4660205780bSrralphs 		return (1);
4670205780bSrralphs 	}
4680205780bSrralphs 
4690205780bSrralphs 	bp->b_un.b_addr = bufp;
4700205780bSrralphs 	bp->b_flags = B_READ;
4710205780bSrralphs 	bp->b_bcount = len;
4720205780bSrralphs 	bp->b_resid = 0;
4730205780bSrralphs 
4740205780bSrralphs 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
4750205780bSrralphs 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
4760205780bSrralphs 
4770205780bSrralphs 	if (pkt == NULL) {
4780205780bSrralphs 		VHCI_DEBUG(1, (CE_NOTE, NULL,
4790205780bSrralphs 		    "!vhci_tpgs_report_target_groups: scsi_init_pkt error\n"));
4800205780bSrralphs 		kmem_free((void *)bufp, len);
4810205780bSrralphs 		return (1);
4820205780bSrralphs 	}
4830205780bSrralphs 
4840205780bSrralphs 	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_IN;
4850205780bSrralphs 	pkt->pkt_cdbp[1] = SSVC_ACTION_GET_TARGET_PORT_GROUPS;
4860205780bSrralphs 	pkt->pkt_cdbp[6] = ((len >>  24) & 0xff);
4870205780bSrralphs 	pkt->pkt_cdbp[7] = ((len >> 16) & 0xff);
4880205780bSrralphs 	pkt->pkt_cdbp[8] = ((len >> 8) & 0xff);
4890205780bSrralphs 	pkt->pkt_cdbp[9] = len & 0xff;
4900205780bSrralphs 	pkt->pkt_time = 90;
4910205780bSrralphs 
4920205780bSrralphs 	VHCI_DEBUG(6, (CE_NOTE, NULL,
4930205780bSrralphs 	    "!vhci_tpgs_report_target_groups: sending target port group:"
4940205780bSrralphs 	    " cdb[6/7/8/9]: %x/%x/%x/%x\n", pkt->pkt_cdbp[6],
4950205780bSrralphs 	    pkt->pkt_cdbp[7], pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
4960205780bSrralphs 	if (vhci_do_scsi_cmd(pkt) == 0) {
4970205780bSrralphs 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
4980205780bSrralphs 		    " vhci_do_scsi_cmd failed\n"));
4990205780bSrralphs 		kmem_free((void *)bufp, len);
5000205780bSrralphs 		scsi_destroy_pkt(pkt);
5010205780bSrralphs 		return (1);
5020205780bSrralphs 	}
5030205780bSrralphs 	ptr = bufp;
5040205780bSrralphs 	VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
5050205780bSrralphs 	    " returned from target"
5060205780bSrralphs 	    " port group: buf[0/1/2/3]: %x/%x/%x/%x\n",
5070205780bSrralphs 	    ptr[0], ptr[1], ptr[2], ptr[3]));
5080205780bSrralphs 	rtpg_len = (unsigned int)((0xff & ptr[0]) << 24);
5090205780bSrralphs 	rtpg_len |= (unsigned int)((0xff & ptr[1]) << 16);
5100205780bSrralphs 	rtpg_len |= (unsigned int)((0xff & ptr[2]) << 8);
5110205780bSrralphs 	rtpg_len |= (unsigned int)(0xff & ptr[3]);
5120205780bSrralphs 	rtpg_len += 4;
5130205780bSrralphs 	if (rtpg_len > len) {
5140205780bSrralphs 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
5150205780bSrralphs 		    " bufsize: %d greater than allocated buf: %d\n",
5160205780bSrralphs 		    rtpg_len, len));
5170205780bSrralphs 		VHCI_DEBUG(4, (CE_NOTE, NULL, "Retrying for size %d\n",
5180205780bSrralphs 		    rtpg_len));
5190205780bSrralphs 		kmem_free((void *)bufp, len);
5200205780bSrralphs 		len = (unsigned int)(rtpg_len + 1);
5210205780bSrralphs 		goto try_again;
5220205780bSrralphs 	}
5230205780bSrralphs #ifdef DEBUG
5240205780bSrralphs 	print_buf(bufp, rtpg_len);
5250205780bSrralphs #endif
5260205780bSrralphs 	end = ptr + rtpg_len;
5270205780bSrralphs 	ptr += 4;
5280205780bSrralphs 	while (ptr < end) {
5290205780bSrralphs 		mpapi_ptr = ptr;
5300205780bSrralphs 		l_tgt_port = ((ptr[2] & 0xff) << 8) + (ptr[3] & 0xff);
5310205780bSrralphs 		tpgs_state = ptr[0] & 0x0f;
5320205780bSrralphs 		tgt_port_cnt = (ptr[7] & 0xff);
5330205780bSrralphs 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups:"
5340205780bSrralphs 		    " tpgs state: %x"
5350205780bSrralphs 		    " tgt_group: %x count: %x\n", tpgs_state,
5360205780bSrralphs 		    l_tgt_port, tgt_port_cnt));
5370205780bSrralphs 		ptr += 8;
5380205780bSrralphs 		for (i = 0; i < tgt_port_cnt; i++) {
5390205780bSrralphs 			lr_tgt_port = 0;
5400205780bSrralphs 			lr_tgt_port |= ((ptr[2] & 0Xff) << 8);
5410205780bSrralphs 			lr_tgt_port |= (ptr[3] & 0xff);
5420205780bSrralphs 
5430205780bSrralphs 			if ((lr_tgt_port == rel_tgt_port) &&
5440205780bSrralphs 			    (l_tgt_port == tgt_port)) {
5450205780bSrralphs 				VHCI_DEBUG(4, (CE_NOTE, NULL,
5460205780bSrralphs 				    "!vhci_tpgs_report_tgt_groups:"
5470205780bSrralphs 				    " found tgt_port: %x rel_tgt_port:%x"
5480205780bSrralphs 				    " tpgs_state: %x\n", tgt_port, rel_tgt_port,
5490205780bSrralphs 				    tpgs_state));
5500205780bSrralphs 				/*
5510205780bSrralphs 				 * once we have the preferred flag
5520205780bSrralphs 				 * and a non-optimized state flag
5530205780bSrralphs 				 * we will get preferred flag  from the
5540205780bSrralphs 				 * report target groups
5550205780bSrralphs 				 */
5560205780bSrralphs 				if (tpgs_state == STD_ACTIVE_OPTIMIZED) {
5570205780bSrralphs 					*pstate = STD_ACTIVE_OPTIMIZED;
5580205780bSrralphs 					*preferred = PCLASS_PREFERRED;
5590205780bSrralphs 				} else if (tpgs_state ==
5600205780bSrralphs 				    STD_ACTIVE_NONOPTIMIZED) {
5610205780bSrralphs 					*pstate = STD_ACTIVE_NONOPTIMIZED;
5620205780bSrralphs 					*preferred = PCLASS_NONPREFERRED;
5630205780bSrralphs 				} else if (tpgs_state == STD_STANDBY) {
5640205780bSrralphs 					*pstate = STD_STANDBY;
5650205780bSrralphs 					*preferred = PCLASS_NONPREFERRED;
5660205780bSrralphs 				} else {
5670205780bSrralphs 					*pstate = STD_UNAVAILABLE;
5680205780bSrralphs 					*preferred = PCLASS_NONPREFERRED;
5690205780bSrralphs 				}
570b1a03ab1SVictor Li 				vhci_mpapi_update_tpg_data(ap, mpapi_ptr,
571b1a03ab1SVictor Li 				    rel_tgt_port);
5720205780bSrralphs 				kmem_free((void *)bufp, len);
5730205780bSrralphs 				scsi_destroy_pkt(pkt);
5740205780bSrralphs 				return (0);
5750205780bSrralphs 			}
5760205780bSrralphs 			VHCI_DEBUG(4, (CE_NOTE, NULL,
5770205780bSrralphs 			    "!vhci_tpgs_report_tgt_groups:"
5780205780bSrralphs 			    " tgt_port: %x rel_tgt_port:%x\n", tgt_port,
5790205780bSrralphs 			    rel_tgt_port));
5800205780bSrralphs 			ptr += 4;
5810205780bSrralphs 		}
5820205780bSrralphs 	}
5830205780bSrralphs 	*pstate = SCSI_PATH_INACTIVE;
5840205780bSrralphs 	*preferred = PCLASS_NONPREFERRED;
5850205780bSrralphs 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups: "
5860205780bSrralphs 	    "NO rel_TGTPRT MATCH!!! Assigning Default: state: %x "
5870205780bSrralphs 	    "preferred: %d\n", *pstate, *preferred));
5880205780bSrralphs 	kmem_free((void *)bufp, len);
5890205780bSrralphs 	scsi_destroy_pkt(pkt);
5900205780bSrralphs 	return (1);
5910205780bSrralphs }
592