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