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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/conf.h>
27 #include <sys/file.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/scsi/scsi.h>
31 #include <sys/scsi/adapters/scsi_vhci.h>
32 #include <sys/scsi/adapters/scsi_vhci_tpgs.h>
33 
34 /*
35  * External function definitions
36  */
37 extern void vhci_mpapi_update_tpg_data(struct scsi_address *, char *, int);
38 
39 
40 
41 static int vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp,
42     int *mode);
43 static int vhci_tpgs_page83(struct scsi_address *ap, struct buf *bp,
44     int *rel_tgt_port, int *tgt_port, int *lu);
45 static void print_buf(char *buf, int buf_size);
46 static int vhci_tpgs_report_target_groups(struct scsi_address *ap,
47     struct buf *bp, int rel_tgt_port, int tgt_port, int *pstate,
48     int *preferred);
49 
50 int
vhci_tpgs_set_target_groups(struct scsi_address * ap,int set_state,int tpg_id)51 vhci_tpgs_set_target_groups(struct scsi_address *ap, int set_state,
52     int tpg_id)
53 {
54 	struct scsi_pkt			*pkt;
55 	struct buf			*bp;
56 	int				len, rval, ss = SCSI_SENSE_UNKNOWN;
57 	char				*bufp;
58 	uint8_t				*sns, skey, asc, ascq;
59 
60 	len = 8;
61 
62 	bp = getrbuf(KM_NOSLEEP);
63 	if (bp == NULL) {
64 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: "
65 		    " failed getrbuf"));
66 		return (1);
67 	}
68 
69 	bufp = kmem_zalloc(len, KM_NOSLEEP);
70 	if (bufp == NULL) {
71 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: "
72 		    "request packet allocation for %d failed....", len));
73 		freerbuf(bp);
74 		return (1);
75 	}
76 
77 	bp->b_un.b_addr = bufp;
78 	bp->b_flags = B_WRITE;
79 	bp->b_bcount = len;
80 	bp->b_resid = 0;
81 
82 	bufp[4] = (0x0f & set_state);
83 	bufp[6] = (0xff00 & tpg_id) >> 8;
84 	bufp[7] = (0x00ff & tpg_id);
85 
86 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
87 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
88 
89 	if (pkt == NULL) {
90 		VHCI_DEBUG(1, (CE_NOTE, NULL,
91 		    "!vhci_tpgs_set_target_groups: scsi_init_pkt error\n"));
92 		freerbuf(bp);
93 		kmem_free((void *)bufp, len);
94 		return (1);
95 	}
96 
97 	/*
98 	 * Sends 1 TPG descriptor only. Hence Parameter list length pkt_cdbp[9]
99 	 * is set to 8 bytes - Refer SPC3 for details.
100 	 */
101 	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_OUT;
102 	pkt->pkt_cdbp[1] = SSVC_ACTION_SET_TARGET_PORT_GROUPS;
103 	pkt->pkt_cdbp[9] = 8;
104 	pkt->pkt_time = 90;
105 
106 	VHCI_DEBUG(1, (CE_NOTE, NULL,
107 	    "!vhci_tpgs_set_target_groups: sending set target port group:"
108 	    " cdb[0/1/6/7/8/9]: %x/%x/%x/%x/%x/%x\n", pkt->pkt_cdbp[0],
109 	    pkt->pkt_cdbp[1], pkt->pkt_cdbp[6], pkt->pkt_cdbp[7],
110 	    pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
111 
112 #ifdef DEBUG
113 	print_buf(bufp, len);
114 #endif
115 	rval = vhci_do_scsi_cmd(pkt);
116 
117 	if (rval == 0) {
118 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups:"
119 		    " vhci_do_scsi_cmd failed\n"));
120 		freerbuf(bp);
121 		kmem_free((void *)bufp, len);
122 		scsi_destroy_pkt(pkt);
123 		return (-1);
124 	} else if ((pkt->pkt_reason == CMD_CMPLT) &&
125 	    (SCBP_C(pkt) == STATUS_CHECK) &&
126 	    (pkt->pkt_state & STATE_ARQ_DONE)) {
127 		sns = (uint8_t *)
128 		    &(((struct scsi_arq_status *)(uintptr_t)
129 		    (pkt->pkt_scbp))->sts_sensedata);
130 		skey = scsi_sense_key(sns);
131 		asc = scsi_sense_asc(sns);
132 		ascq = scsi_sense_ascq(sns);
133 
134 		if ((skey == KEY_UNIT_ATTENTION) &&
135 		    (asc == STD_SCSI_ASC_STATE_CHG) &&
136 		    (ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
137 			ss = SCSI_SENSE_STATE_CHANGED;
138 			VHCI_DEBUG(4, (CE_NOTE, NULL,
139 			    "!vhci_tpgs_set_target_groups:"
140 			    " sense:%x, add_code: %x, qual_code:%x"
141 			    " sense:%x\n", skey, asc, ascq, ss));
142 		} else if ((skey == KEY_ILLEGAL_REQUEST) &&
143 		    (asc == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
144 			ss = SCSI_SENSE_NOFAILOVER;
145 			VHCI_DEBUG(1, (CE_NOTE, NULL,
146 			    "!vhci_tpgs_set_target_groups:"
147 			    " sense:%x, add_code: %x, qual_code:%x"
148 			    " sense:%x\n", skey, asc, ascq, ss));
149 		} else if ((skey == KEY_ILLEGAL_REQUEST) &&
150 		    (asc == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
151 			ss = SCSI_SENSE_NOFAILOVER;
152 			VHCI_DEBUG(1, (CE_NOTE, NULL,
153 			    "!vhci_tpgs_set_target_groups:"
154 			    " sense_key:%x, add_code: %x, qual_code:%x"
155 			    " sense:%x\n", skey, asc, ascq, rval));
156 		} else {
157 			/*
158 			 * At this point sns data may be for power-on-reset
159 			 * UNIT ATTN hardware errors, vendor unqiue sense etc.
160 			 * For all these cases, sense is unknown.
161 			 */
162 			ss = SCSI_SENSE_NOFAILOVER;
163 			VHCI_DEBUG(1, (CE_NOTE, NULL,
164 			    "!vhci_tpgs_set_target_groups: "
165 			    " sense UNKNOWN: sense key:%x, ASC:%x, ASCQ:%x\n",
166 			    skey, asc, ascq));
167 		}
168 
169 		if (ss == SCSI_SENSE_STATE_CHANGED) {
170 			freerbuf(bp);
171 			kmem_free((void *)bufp, len);
172 			scsi_destroy_pkt(pkt);
173 			return (0);
174 		}
175 	} else if ((pkt->pkt_reason == CMD_CMPLT) &&
176 	    (SCBP_C(pkt) == STATUS_GOOD)) {
177 		freerbuf(bp);
178 		kmem_free((void *)bufp, len);
179 		scsi_destroy_pkt(pkt);
180 		return (0);
181 	}
182 
183 	freerbuf(bp);
184 	kmem_free((void *)bufp, len);
185 	scsi_destroy_pkt(pkt);
186 	return (1);
187 }
188 
189 /*
190  * get the failover mode, ownership and if it has extended failover
191  * capability. The mode(bits5-4/byte5) is defined as implicit, explicit, or
192  * both.  The state is defined as online-optimized(0h),
193  * online-nonoptimized(1h), standby(2h), offline(3h),
194  * and transitioning(fh). Currently, there is online,
195  * standby, and offline(defined in sunmdi.h).
196  * Online-nonoptimized will be a mode of secondary
197  * and an ownership of online. Thought about using a different mode but
198  * it appears the states are really for the states for secondary mode.
199  * We currently have IS_ONLINING, IS_OFFLINING - should we have TRANSITIONING
200  * to mean from online-optimized to online-nonoptimized or does onlining
201  * cover this?
202  */
203 /* ARGSUSED */
204 int
vhci_tpgs_get_target_fo_mode(struct scsi_device * sd,int * mode,int * state,int * xlf_capable,int * preferred)205 vhci_tpgs_get_target_fo_mode(struct scsi_device *sd, int *mode,
206     int *state, int *xlf_capable, int *preferred)
207 {
208 	int			retval = 0;
209 	struct buf		*bp;
210 	struct scsi_address	*ap;
211 	int			lu = 0, rel_tgt_port = 0, tgt_port = 0x0;
212 
213 	VHCI_DEBUG(6, (CE_NOTE, NULL,
214 	    "!vhci_tpgs_get_target_fo_mode: enter\n"));
215 	*mode = *state = *xlf_capable = 0;
216 	bp = getrbuf(KM_NOSLEEP);
217 	if (bp == NULL) {
218 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
219 		    " failed getrbuf\n"));
220 		return (1);
221 	}
222 
223 	ap = &sd->sd_address;
224 	if (vhci_tpgs_inquiry(ap, bp, mode)) {
225 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
226 		    " failed vhci_tpgs_inquiry\n"));
227 		retval = 1;
228 	} else if (vhci_tpgs_page83(ap, bp, &rel_tgt_port, &tgt_port, &lu)) {
229 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
230 		    " failed vhci_tpgs_page83\n"));
231 		retval = 1;
232 	} else if (vhci_tpgs_report_target_groups(ap, bp, rel_tgt_port,
233 	    tgt_port, state, preferred)) {
234 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
235 		    " failed vhci_tpgs_report_target_groups\n"));
236 		retval = 1;
237 	}
238 
239 	freerbuf(bp);
240 	if (retval == 0) {
241 		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
242 		    "SUCCESS\n"));
243 	}
244 	return (retval);
245 }
246 
247 static int
vhci_tpgs_inquiry(struct scsi_address * ap,struct buf * bp,int * mode)248 vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp, int *mode)
249 {
250 	struct scsi_pkt		*pkt;
251 	struct scsi_inquiry	inq;
252 	int			retval;
253 
254 	*mode = 0;
255 	bp->b_un.b_addr = (caddr_t)&inq;
256 	bp->b_flags = B_READ;
257 	bp->b_bcount = sizeof (inq);
258 	bp->b_resid = 0;
259 
260 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
261 	    sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC, NULL);
262 	if (pkt == NULL) {
263 		VHCI_DEBUG(1, (CE_WARN, NULL,
264 		    "!vhci_tpgs_inquiry: Failure returned from scsi_init_pkt"));
265 		return (1);
266 	}
267 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
268 	pkt->pkt_cdbp[4] = sizeof (inq);
269 	pkt->pkt_time = 60;
270 
271 	retval = vhci_do_scsi_cmd(pkt);
272 	scsi_destroy_pkt(pkt);
273 	if (retval == 0) {
274 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_inquiry: Failure"
275 		    " returned from vhci_do_scsi_cmd"));
276 		return (1);
277 	}
278 
279 	if (inq.inq_tpgs == TPGS_FAILOVER_NONE) {
280 		VHCI_DEBUG(1, (CE_WARN, NULL,
281 		    "!vhci_tpgs_inquiry: zero tpgs_bits"));
282 		return (1);
283 	}
284 	retval = 0;
285 	if (inq.inq_tpgs == TPGS_FAILOVER_IMPLICIT) {
286 		*mode = SCSI_IMPLICIT_FAILOVER;
287 	} else if (inq.inq_tpgs == TPGS_FAILOVER_EXPLICIT) {
288 		*mode = SCSI_EXPLICIT_FAILOVER;
289 	} else if (inq.inq_tpgs == TPGS_FAILOVER_BOTH) {
290 		*mode = SCSI_BOTH_FAILOVER;
291 	} else {
292 		VHCI_DEBUG(1, (CE_WARN, NULL,
293 		    "!vhci_tpgs_inquiry: Illegal mode returned: %x mode: %x",
294 		    inq.inq_tpgs, *mode));
295 		retval = 1;
296 	}
297 
298 	return (retval);
299 }
300 
301 static int
vhci_tpgs_page83(struct scsi_address * ap,struct buf * bp,int * rel_tgt_port,int * tgt_port,int * lu)302 vhci_tpgs_page83(struct scsi_address *ap, struct buf *bp,
303 	int *rel_tgt_port, int *tgt_port, int *lu)
304 {
305 	char			*ptr, *end;
306 	struct scsi_pkt		*pkt;
307 	char			*bufp;
308 	unsigned int		buf_len, rx_bsize;
309 
310 	/*
311 	 * lets start the buf size with 512 bytes. If this
312 	 * if found to be insufficient, we can allocate
313 	 * appropriate size in the next iteration.
314 	 */
315 	buf_len = 512;
316 
317 once_again:
318 	bufp = kmem_zalloc(buf_len, KM_NOSLEEP);
319 	if (bufp == NULL) {
320 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_page83: "
321 		    "request packet allocation for %d failed....",
322 		    buf_len));
323 		return (1);
324 	}
325 
326 
327 	bp->b_un.b_addr = bufp;
328 	bp->b_flags = B_READ;
329 	bp->b_bcount = buf_len;
330 	bp->b_resid = 0;
331 
332 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
333 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
334 	if (pkt == NULL) {
335 		VHCI_DEBUG(1, (CE_WARN, NULL,
336 		    "!vhci_tpgs_page83: Failure returned from scsi_init_pkt"));
337 		kmem_free((void *)bufp, buf_len);
338 		return (1);
339 	}
340 
341 	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
342 	pkt->pkt_cdbp[1] = 0x1;
343 	pkt->pkt_cdbp[2] = 0x83;
344 	pkt->pkt_cdbp[3] = (unsigned char)((buf_len >> 8) & 0xff);
345 	pkt->pkt_cdbp[4] = (unsigned char)(buf_len & 0xff);
346 	pkt->pkt_time = 90;
347 
348 	if (vhci_do_scsi_cmd(pkt) == 0) {
349 		VHCI_DEBUG(1, (CE_NOTE, NULL,
350 		    "!vhci_tpgs_page83: vhci_do_scsi_cmd failed\n"));
351 		kmem_free((void *)bufp, buf_len);
352 		scsi_destroy_pkt(pkt);
353 		return (1);
354 	}
355 
356 	/*
357 	 * Now lets check if the size that was provided was
358 	 * sufficient. If not, allocate the appropriate size
359 	 * and retry the command again.
360 	 */
361 	rx_bsize = (((bufp[2] & 0xff) << 8) | (bufp[3] & 0xff));
362 	rx_bsize += 4;
363 	if (rx_bsize > buf_len) {
364 		/*
365 		 * Need to allocate more buf and retry again
366 		 */
367 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: "
368 		    "bufsize: %d greater than allocated buf: %d\n",
369 		    rx_bsize, buf_len));
370 		VHCI_DEBUG(1, (CE_NOTE, NULL, "Retrying for size %d\n",
371 		    rx_bsize));
372 		kmem_free((void *)bufp, buf_len);
373 		buf_len = (unsigned int)(rx_bsize);
374 		goto once_again;
375 	}
376 
377 	ptr = bufp;
378 	ptr += 4; /* identification descriptor 0 */
379 	end = bufp + rx_bsize;
380 	while (ptr < end) {
381 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_tpgs_page83: "
382 		    "desc[1/4/5/6/7]:%x %x %x %x %x\n",
383 		    ptr[1], ptr[4], ptr[5], ptr[6], ptr[7]));
384 		if ((ptr[1] & 0x0f) == 0x04) {
385 			*rel_tgt_port = 0;
386 			*rel_tgt_port |= ((ptr[6] & 0xff) << 8);
387 			*rel_tgt_port |= (ptr[7] & 0xff);
388 			VHCI_DEBUG(1, (CE_NOTE, NULL,
389 			    "!vhci_tpgs_page83: relative target port: %x\n",
390 			    *rel_tgt_port));
391 		} else if ((ptr[1] & 0x0f) == 0x05) {
392 			*tgt_port = 0;
393 			*tgt_port = ((ptr[6] & 0xff) << 8);
394 			*tgt_port |= (ptr[7] & 0xff);
395 			VHCI_DEBUG(1, (CE_NOTE, NULL,
396 			    "!vhci_tpgs_page83: target port: %x\n", *tgt_port));
397 		} else if ((ptr[1] & 0x0f) == 0x06) {
398 			*lu = 0;
399 			*lu |= ((ptr[6] & 0xff)<< 8);
400 			*lu |= (ptr[7] & 0xff);
401 			VHCI_DEBUG(1, (CE_NOTE, NULL,
402 			    "!vhci_tpgs_page83: logical unit: %x\n", *lu));
403 		}
404 		ptr += ptr[3] + 4;  /* next identification descriptor */
405 	}
406 	kmem_free((void *)bufp, buf_len);
407 	scsi_destroy_pkt(pkt);
408 	return (0);
409 }
410 
411 #ifdef DEBUG
412 static void
print_buf(char * buf,int buf_size)413 print_buf(char *buf, int buf_size)
414 {
415 	int		i = 0, j;
416 	int		loop, left;
417 
418 	loop = buf_size / 8;
419 	left = buf_size % 8;
420 
421 	VHCI_DEBUG(4, (CE_NOTE, NULL, "!buf_size: %x loop: %x left: %x",
422 	    buf_size, loop, left));
423 
424 	for (j = 0; j < loop; j++) {
425 		VHCI_DEBUG(4, (CE_NOTE, NULL,
426 		    "!buf[%d-%d]: %x %x %x %x %x %x %x %x",
427 		    i, i + 7, buf[i], buf[i+1], buf[i+2], buf[i+3],
428 		    buf[i+4], buf[i+5], buf[i+6], buf[i+7]));
429 		i += 8;
430 	}
431 
432 	if (left) {
433 		VHCI_DEBUG(4, (CE_CONT, NULL,
434 		    "NOTICE: buf[%d-%d]:", i, i + left));
435 		for (j = 0; j < left; j++) {
436 			VHCI_DEBUG(4, (CE_CONT, NULL, " %x", buf[i + j]));
437 		}
438 		VHCI_DEBUG(4, (CE_CONT, NULL, "\n"));
439 	}
440 }
441 #endif
442 
443 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)444 vhci_tpgs_report_target_groups(struct scsi_address *ap, struct buf *bp,
445 	int rel_tgt_port, int tgt_port, int *pstate, int *preferred)
446 {
447 	struct scsi_pkt		*pkt;
448 	char			*ptr, *end, *bufp, *mpapi_ptr;
449 	unsigned int		rtpg_len = 0;
450 	unsigned int		l_tgt_port = 0, tpgs_state = 0;
451 	unsigned int		tgt_port_cnt = 0, lr_tgt_port = 0;
452 	int			i, len;
453 
454 	/*
455 	 * Start with buffer size of 512.
456 	 * If this is found to be insufficient, required size
457 	 * will be allocated and the command will be retried.
458 	 */
459 	len = 512;
460 
461 try_again:
462 	bufp = kmem_zalloc(len, KM_NOSLEEP);
463 	if (bufp == NULL) {
464 		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_report_target_groups:"
465 		    " request packet allocation for %d failed....", len));
466 		return (1);
467 	}
468 
469 	bp->b_un.b_addr = bufp;
470 	bp->b_flags = B_READ;
471 	bp->b_bcount = len;
472 	bp->b_resid = 0;
473 
474 	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
475 	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
476 
477 	if (pkt == NULL) {
478 		VHCI_DEBUG(1, (CE_NOTE, NULL,
479 		    "!vhci_tpgs_report_target_groups: scsi_init_pkt error\n"));
480 		kmem_free((void *)bufp, len);
481 		return (1);
482 	}
483 
484 	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_IN;
485 	pkt->pkt_cdbp[1] = SSVC_ACTION_GET_TARGET_PORT_GROUPS;
486 	pkt->pkt_cdbp[6] = ((len >>  24) & 0xff);
487 	pkt->pkt_cdbp[7] = ((len >> 16) & 0xff);
488 	pkt->pkt_cdbp[8] = ((len >> 8) & 0xff);
489 	pkt->pkt_cdbp[9] = len & 0xff;
490 	pkt->pkt_time = 90;
491 
492 	VHCI_DEBUG(6, (CE_NOTE, NULL,
493 	    "!vhci_tpgs_report_target_groups: sending target port group:"
494 	    " cdb[6/7/8/9]: %x/%x/%x/%x\n", pkt->pkt_cdbp[6],
495 	    pkt->pkt_cdbp[7], pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
496 	if (vhci_do_scsi_cmd(pkt) == 0) {
497 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
498 		    " vhci_do_scsi_cmd failed\n"));
499 		kmem_free((void *)bufp, len);
500 		scsi_destroy_pkt(pkt);
501 		return (1);
502 	}
503 	ptr = bufp;
504 	VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
505 	    " returned from target"
506 	    " port group: buf[0/1/2/3]: %x/%x/%x/%x\n",
507 	    ptr[0], ptr[1], ptr[2], ptr[3]));
508 	rtpg_len = (unsigned int)((0xff & ptr[0]) << 24);
509 	rtpg_len |= (unsigned int)((0xff & ptr[1]) << 16);
510 	rtpg_len |= (unsigned int)((0xff & ptr[2]) << 8);
511 	rtpg_len |= (unsigned int)(0xff & ptr[3]);
512 	rtpg_len += 4;
513 	if (rtpg_len > len) {
514 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
515 		    " bufsize: %d greater than allocated buf: %d\n",
516 		    rtpg_len, len));
517 		VHCI_DEBUG(4, (CE_NOTE, NULL, "Retrying for size %d\n",
518 		    rtpg_len));
519 		kmem_free((void *)bufp, len);
520 		len = (unsigned int)(rtpg_len + 1);
521 		goto try_again;
522 	}
523 #ifdef DEBUG
524 	print_buf(bufp, rtpg_len);
525 #endif
526 	end = ptr + rtpg_len;
527 	ptr += 4;
528 	while (ptr < end) {
529 		mpapi_ptr = ptr;
530 		l_tgt_port = ((ptr[2] & 0xff) << 8) + (ptr[3] & 0xff);
531 		tpgs_state = ptr[0] & 0x0f;
532 		tgt_port_cnt = (ptr[7] & 0xff);
533 		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups:"
534 		    " tpgs state: %x"
535 		    " tgt_group: %x count: %x\n", tpgs_state,
536 		    l_tgt_port, tgt_port_cnt));
537 		ptr += 8;
538 		for (i = 0; i < tgt_port_cnt; i++) {
539 			lr_tgt_port = 0;
540 			lr_tgt_port |= ((ptr[2] & 0Xff) << 8);
541 			lr_tgt_port |= (ptr[3] & 0xff);
542 
543 			if ((lr_tgt_port == rel_tgt_port) &&
544 			    (l_tgt_port == tgt_port)) {
545 				VHCI_DEBUG(4, (CE_NOTE, NULL,
546 				    "!vhci_tpgs_report_tgt_groups:"
547 				    " found tgt_port: %x rel_tgt_port:%x"
548 				    " tpgs_state: %x\n", tgt_port, rel_tgt_port,
549 				    tpgs_state));
550 				/*
551 				 * once we have the preferred flag
552 				 * and a non-optimized state flag
553 				 * we will get preferred flag  from the
554 				 * report target groups
555 				 */
556 				if (tpgs_state == STD_ACTIVE_OPTIMIZED) {
557 					*pstate = STD_ACTIVE_OPTIMIZED;
558 					*preferred = PCLASS_PREFERRED;
559 				} else if (tpgs_state ==
560 				    STD_ACTIVE_NONOPTIMIZED) {
561 					*pstate = STD_ACTIVE_NONOPTIMIZED;
562 					*preferred = PCLASS_NONPREFERRED;
563 				} else if (tpgs_state == STD_STANDBY) {
564 					*pstate = STD_STANDBY;
565 					*preferred = PCLASS_NONPREFERRED;
566 				} else {
567 					*pstate = STD_UNAVAILABLE;
568 					*preferred = PCLASS_NONPREFERRED;
569 				}
570 				vhci_mpapi_update_tpg_data(ap, mpapi_ptr,
571 				    rel_tgt_port);
572 				kmem_free((void *)bufp, len);
573 				scsi_destroy_pkt(pkt);
574 				return (0);
575 			}
576 			VHCI_DEBUG(4, (CE_NOTE, NULL,
577 			    "!vhci_tpgs_report_tgt_groups:"
578 			    " tgt_port: %x rel_tgt_port:%x\n", tgt_port,
579 			    rel_tgt_port));
580 			ptr += 4;
581 		}
582 	}
583 	*pstate = SCSI_PATH_INACTIVE;
584 	*preferred = PCLASS_NONPREFERRED;
585 	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups: "
586 	    "NO rel_TGTPRT MATCH!!! Assigning Default: state: %x "
587 	    "preferred: %d\n", *pstate, *preferred));
588 	kmem_free((void *)bufp, len);
589 	scsi_destroy_pkt(pkt);
590 	return (1);
591 }
592