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 
27 #include "cfga_fp.h"
28 #include <sys/fibre-channel/impl/fc_error.h>
29 
30 /* Structure for walking the tree */
31 typedef struct {
32 	apid_t		*apidp;
33 	char		*xport_logp;
34 	ldata_list_t	*listp;
35 	fpcfga_cmd_t	cmd;
36 	cfga_stat_t	chld_config;
37 	cfga_type_t	xport_type;
38 	cfga_stat_t	xport_rstate;
39 	fpcfga_ret_t	ret;
40 	int		l_errno;
41 } fpcfga_list_t;
42 
43 typedef struct {
44 	uint_t itype;
45 	const char *ntype;
46 	const char *name;
47 } fpcfga_devtype_t;
48 
49 #define	ERR_INQ_DTYPE	0xff
50 
51 /* The TYPE field is parseable and should not contain spaces */
52 #define	FP_FC_PORT_TYPE		"fc"
53 #define	FP_FC_PORT_ERROR	"fc-error"
54 #define	FP_FC_FABRIC_PORT_TYPE	"fc-fabric"
55 #define	FP_FC_PUBLIC_PORT_TYPE	"fc-public"
56 #define	FP_FC_PRIVATE_PORT_TYPE	"fc-private"
57 #define	FP_FC_PT_TO_PT_PORT_TYPE	"fc-pt_to_pt"
58 
59 /* Indicates no plag passing */
60 #define	NO_FLAG			0
61 
62 /* defines for retry algorithm */
63 #define	OPEN_RETRY_COUNT	5
64 #define	OPEN_RETRY_INTERVAL	10000 /* 1/100 of a sec. */
65 #define	IOCTL_RETRY_COUNT	5
66 #define	IOCTL_RETRY_INTERVAL	5000000 /* 5 sec */
67 
68 /* define for fcp scsi passthru wait */
69 #define	FCP_SCSI_CMD_TIMEOUT	10
70 
71 /* define for fcp pseudo node */
72 #define	FCP_PATH	"/devices/pseudo/fcp@0:fcp"
73 
74 /* Function prototypes */
75 static fpcfga_ret_t postprocess_list_data(const ldata_list_t *listp,
76     fpcfga_cmd_t cmd, cfga_stat_t chld_config, int *np, uint_t flags);
77 static int stat_fc_dev(di_node_t node, void *arg);
78 static int stat_FCP_dev(di_node_t node, void *arg);
79 static fpcfga_ret_t do_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
80 	HBA_PORTATTRIBUTES portAttrs);
81 static int get_xport_state(di_node_t node, void *arg);
82 
83 static fpcfga_ret_t do_stat_fc_dev(const di_node_t node, const char *nodepath,
84     fpcfga_list_t *lap, int limited_stat);
85 static fpcfga_ret_t do_stat_FCP_dev(const di_node_t node, const char *nodepath,
86     fpcfga_list_t *lap, int limited_stat);
87 static cfga_stat_t xport_devinfo_to_recep_state(uint_t xport_di_state);
88 static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
89 static void get_hw_info(di_node_t node, cfga_list_data_t *clp);
90 static const char *get_device_type(di_node_t);
91 static fpcfga_ret_t init_ldata_for_accessible_dev(const char *dyncomp,
92 	uchar_t inq_type, fpcfga_list_t *lap);
93 static fpcfga_ret_t init_ldata_for_accessible_FCP_dev(const char *port_wwn,
94 	int num_luns, struct report_lun_resp *resp_buf,
95 	fpcfga_list_t *larg, int *l_errnop);
96 static fpcfga_ret_t is_dyn_ap_on_ldata_list(const char *port_wwn,
97 	const ldata_list_t *listp, ldata_list_t **matchldpp, int *l_errno);
98 static fpcfga_ret_t is_FCP_dev_ap_on_ldata_list(const char *port_wwn,
99 	const int lun_num, ldata_list_t *ldatap, ldata_list_t **matchldpp);
100 
101 static fpcfga_ret_t init_ldata_for_mpath_dev(di_path_t path, char *port_wwn,
102 	int *l_errnop, fpcfga_list_t *lap);
103 static fpcfga_ret_t insert_ldata_to_ldatalist(const char *port_wwn,
104 	int *lun_nump, ldata_list_t *listp, ldata_list_t **ldatapp);
105 static fpcfga_ret_t insert_fc_dev_ldata(const char *port_wwn,
106 	ldata_list_t *listp, ldata_list_t **ldatapp);
107 static fpcfga_ret_t insert_FCP_dev_ldata(const char *port_wwn, int lun_num,
108 	ldata_list_t *listp, ldata_list_t **ldatapp);
109 static int stat_path_info_fc_dev(di_node_t root, fpcfga_list_t	*lap,
110 	int *l_errnop);
111 static int stat_path_info_FCP_dev(di_node_t root, fpcfga_list_t	*lap,
112 	int *l_errnop);
113 static fpcfga_ret_t get_accessible_FCP_dev_ldata(const char *dyncomp,
114 	fpcfga_list_t *lap, int *l_errnop);
115 static fpcfga_ret_t get_standard_inq_data(const char *xport_phys,
116 	const char *dyncomp, uchar_t *lun_num, struct scsi_inquiry **inq_buf,
117 	int *l_errnop);
118 static void init_fcp_scsi_cmd(struct fcp_scsi_cmd *fscsi, uchar_t *lun_num,
119 	la_wwn_t *pwwn, void *scmdbuf, size_t scmdbuf_len, void *respbuf,
120 	size_t respbuf_len, void *sensebuf, size_t sensebuf_len);
121 static fpcfga_ret_t issue_fcp_scsi_cmd(const char *xport_phys,
122 	struct fcp_scsi_cmd *fscsi, int *l_errnop);
123 static uchar_t get_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
124     HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs);
125 
126 static fpcfga_devtype_t device_list[] = {
127 	{ DTYPE_DIRECT,		DDI_NT_BLOCK_CHAN,	"disk"},
128 	{ DTYPE_DIRECT,		DDI_NT_BLOCK,		"disk"},
129 	{ DTYPE_DIRECT,		DDI_NT_BLOCK_WWN,	"disk"},
130 	{ DTYPE_DIRECT,		DDI_NT_BLOCK_FABRIC,	"disk"},
131 	{ DTYPE_SEQUENTIAL,	DDI_NT_TAPE,		"tape"},
132 	{ DTYPE_PRINTER,	NULL,			"printer"},
133 	{ DTYPE_PROCESSOR,	NULL,			"processor"},
134 	{ DTYPE_WORM,		NULL,			"WORM"},
135 	{ DTYPE_RODIRECT,	DDI_NT_CD_CHAN,		"CD-ROM"},
136 	{ DTYPE_RODIRECT,	DDI_NT_CD,		"CD-ROM"},
137 	{ DTYPE_SCANNER,	NULL,			"scanner"},
138 	{ DTYPE_OPTICAL,	NULL,			"optical"},
139 	{ DTYPE_CHANGER,	NULL,			"med-changer"},
140 	{ DTYPE_COMM,		NULL,			"comm-device"},
141 	{ DTYPE_ARRAY_CTRL,	NULL,			"array-ctrl"},
142 	{ DTYPE_ESI,		NULL,			"ESI"},
143 	/*
144 	 * This has to be the LAST entry for DTYPE_UNKNOWN_INDEX.
145 	 * Add entries before this.
146 	 */
147 	{ DTYPE_UNKNOWN,	NULL,			"unknown"}
148 };
149 
150 #define	N_DEVICE_TYPES	(sizeof (device_list) / sizeof (device_list[0]))
151 
152 #define	DTYPE_UNKNOWN_INDEX	(N_DEVICE_TYPES - 1)
153 
154 /*
155  * Main routine for list operation.
156  * It calls various routines to consturct ldata list and
157  * postprocess the list data.
158  *
159  * Overall algorithm:
160  * Get the device list on input hba port and construct ldata list for
161  * accesible devices.
162  * Stat hba port and devices through walking the device tree.
163  * Verify the validity of the list data.
164  */
165 fpcfga_ret_t
do_list(apid_t * apidp,fpcfga_cmd_t cmd,ldata_list_t ** llpp,int * nelemp,char ** errstring)166 do_list(
167 	apid_t *apidp,
168 	fpcfga_cmd_t cmd,
169 	ldata_list_t **llpp,
170 	int *nelemp,
171 	char **errstring)
172 {
173 	int		n = -1, l_errno = 0, limited_stat;
174 	walkarg_t	walkarg;
175 	fpcfga_list_t	larg = {NULL};
176 	fpcfga_ret_t	ret;
177 	la_wwn_t	pwwn;
178 	char		*dyncomp = NULL;
179 	HBA_HANDLE	handle;
180 	HBA_PORTATTRIBUTES	portAttrs;
181 	HBA_PORTATTRIBUTES	discPortAttrs;
182 	HBA_STATUS		status;
183 	int			portIndex, discIndex;
184 	int			retry;
185 	uchar_t			inq_dtype;
186 
187 	if (*llpp != NULL || *nelemp != 0) {
188 		return (FPCFGA_ERR);
189 	}
190 
191 	/* Create the hba logid (also base component of logical ap_id) */
192 	ret = make_xport_logid(apidp->xport_phys, &larg.xport_logp, &l_errno);
193 	if (ret != FPCFGA_OK) {
194 		cfga_err(errstring, l_errno, ERR_LIST, 0);
195 		return (FPCFGA_ERR);
196 	}
197 
198 	assert(larg.xport_logp != NULL);
199 
200 	larg.cmd = cmd;
201 	larg.apidp = apidp;
202 	larg.xport_rstate = CFGA_STAT_NONE;
203 
204 	if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
205 	    &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
206 	    S_FREE(larg.xport_logp);
207 	    return (ret);
208 	}
209 
210 	/*
211 	 * If stating a specific device, we will do limited stat on fca port.
212 	 * otherwise full stat on fca part is required.
213 	 * If stating a specific device we don't know if it exists or is
214 	 * configured yet.  larg.ret is set to apid noexist for do_stat_dev.
215 	 * otherwise larg.ret is set to ok initially.
216 	 */
217 	if (larg.cmd == FPCFGA_STAT_FC_DEV) {
218 		limited_stat = 1;		/* for do_stat_fca_xport */
219 		larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev	*/
220 	} else {
221 		limited_stat = 0;		/* for do_stat_fca_xport */
222 		larg.ret = FPCFGA_OK;		/* for stat_fc_dev	*/
223 	}
224 
225 	/* For all list commands, the fca port needs to be stat'ed */
226 	if ((ret = do_stat_fca_xport(&larg, limited_stat,
227 		portAttrs)) != FPCFGA_OK) {
228 		cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
229 		list_free(&larg.listp);
230 		S_FREE(larg.xport_logp);
231 		HBA_CloseAdapter(handle);
232 		HBA_FreeLibrary();
233 		return (ret);
234 	}
235 
236 #ifdef DEBUG
237 	if (limited_stat) {
238 		assert(larg.listp == NULL);
239 	} else {
240 		assert(larg.listp != NULL);
241 	}
242 #endif
243 	/*
244 	 * If stat'ing a FCA port or ALL, we have the bus stat data at
245 	 * this point.
246 	 * Assume that the bus has no configured children.
247 	 */
248 	larg.chld_config = CFGA_STAT_UNCONFIGURED;
249 
250 	switch (larg.cmd) {
251 	case FPCFGA_STAT_FC_DEV:
252 		/* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
253 		if (cvt_dyncomp_to_lawwn(apidp->dyncomp, &pwwn) != 0) {
254 			cfga_err(errstring, 0, ERR_LIST, 0);
255 			list_free(&larg.listp);
256 			S_FREE(larg.xport_logp);
257 			HBA_CloseAdapter(handle);
258 			HBA_FreeLibrary();
259 			return (FPCFGA_LIB_ERR);
260 		}
261 		/*
262 		 * if the dyncomp exists on disco ports construct list_data
263 		 * otherwise return FPCFGA_APID_NOEXIST.
264 		 */
265 		retry = 0;
266 		do {
267 		    status = getPortAttrsByWWN(handle,
268 			*((HBA_WWN *)(&pwwn)), &discPortAttrs);
269 		    if (status == HBA_STATUS_ERROR_STALE_DATA) {
270 			/* get Port Attributes again after refresh. */
271 			HBA_RefreshInformation(handle);
272 		    } else {
273 			break; /* either okay or some other error */
274 		    }
275 		} while (retry++ < HBA_MAX_RETRIES);
276 
277 		if (status == HBA_STATUS_OK) {
278 			/*
279 			 * if dyncomp found in disco ports
280 			 * construct  ldata_list and return.
281 			 * otherwise continue to stat on dev tree with
282 			 * larg.ret set to access_ok which informs stat_fc_dev
283 			 * the existence of device on disco ports.
284 			 *
285 			 * if path is null that guatantees the node is not
286 			 * configured.  if node is detached the path
287 			 * is incomplete and not usable for further
288 			 * operations like uscsi_inq so take care of it here.
289 			 */
290 			inq_dtype = get_inq_dtype(apidp->xport_phys,
291 			    apidp->dyncomp, handle, &portAttrs, &discPortAttrs);
292 
293 			if (init_ldata_for_accessible_dev(apidp->dyncomp,
294 				inq_dtype, &larg) != FPCFGA_OK) {
295 				cfga_err(errstring, larg.l_errno,
296 					ERR_LIST, 0);
297 				list_free(&larg.listp);
298 				S_FREE(larg.xport_logp);
299 				HBA_CloseAdapter(handle);
300 				HBA_FreeLibrary();
301 				return (FPCFGA_LIB_ERR);
302 			}
303 			if (apidp->lunlist == NULL) {
304 				n = 0;
305 				if (postprocess_list_data(
306 					larg.listp, cmd,
307 					larg.chld_config, &n, NO_FLAG) !=
308 					FPCFGA_OK) {
309 					cfga_err(errstring,
310 					larg.l_errno, ERR_LIST, 0);
311 					list_free(&larg.listp);
312 					S_FREE(larg.xport_logp);
313 					HBA_CloseAdapter(handle);
314 					HBA_FreeLibrary();
315 					return (FPCFGA_LIB_ERR);
316 				}
317 				*nelemp = n;
318 				*llpp = larg.listp;
319 				S_FREE(larg.xport_logp);
320 				HBA_CloseAdapter(handle);
321 				HBA_FreeLibrary();
322 				return (FPCFGA_OK);
323 			}
324 			larg.ret = FPCFGA_ACCESS_OK;
325 		} else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
326 			/*
327 			 * path indicates if the node exists in dev tree.
328 			 * if not found in dev tree return apid no exist.
329 			 * otherwise continue to stat with larg.ret set to
330 			 * apid_noexist.
331 			 */
332 			if (apidp->lunlist == NULL) {
333 				list_free(&larg.listp);
334 				S_FREE(larg.xport_logp);
335 				HBA_CloseAdapter(handle);
336 				HBA_FreeLibrary();
337 				return (FPCFGA_APID_NOEXIST);
338 			}
339 		} else { /* any error */
340 			/*
341 			 * path indicates if the node exists in dev tree.
342 			 * if not found in dev tree return lib error.
343 			 * otherwise continue to stat with larg.ret set to
344 			 * apid_noexist.
345 			 */
346 			if (apidp->lunlist == NULL) {
347 				cfga_err(errstring, 0, ERR_FC_GET_DEVLIST, 0);
348 				list_free(&larg.listp);
349 				S_FREE(larg.xport_logp);
350 				HBA_CloseAdapter(handle);
351 				HBA_FreeLibrary();
352 				return (FPCFGA_LIB_ERR);
353 			}
354 		}
355 		break;
356 	case FPCFGA_STAT_ALL:
357 		/*
358 		 * for each dev in disco ports, create a ldata_list element.
359 		 * if if no disco ports found, continue to stat on devinfo tree
360 		 * to see if any node exist on the fca port.
361 		 */
362 		for (discIndex = 0;
363 			discIndex < portAttrs.NumberofDiscoveredPorts;
364 			discIndex++) {
365 		    if (getDiscPortAttrs(handle, portIndex,
366 			discIndex, &discPortAttrs)) {
367 			/* Move on to the next target */
368 			continue;
369 		    }
370 		    memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
371 		    cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
372 		    if (dyncomp == NULL) {
373 			cfga_err(errstring, l_errno, ERR_LIST, 0);
374 			list_free(&larg.listp);
375 			S_FREE(larg.xport_logp);
376 			HBA_CloseAdapter(handle);
377 			HBA_FreeLibrary();
378 			return (FPCFGA_LIB_ERR);
379 		    }
380 		    inq_dtype = get_inq_dtype(apidp->xport_phys, dyncomp,
381 			handle, &portAttrs, &discPortAttrs);
382 
383 		    if ((ret = init_ldata_for_accessible_dev(
384 			    dyncomp, inq_dtype, &larg)) != FPCFGA_OK) {
385 			S_FREE(dyncomp);
386 			cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
387 				list_free(&larg.listp);
388 			S_FREE(larg.xport_logp);
389 			HBA_CloseAdapter(handle);
390 			HBA_FreeLibrary();
391 			return (FPCFGA_LIB_ERR);
392 		    }
393 		    S_FREE(dyncomp);
394 		}
395 		break;
396 	default:
397 		break;
398 	}
399 
400 	/* we need to stat at least 1 device for all commands */
401 	if (apidp->flags == FLAG_DEVINFO_FORCE) {
402 		walkarg.flags = FLAG_DEVINFO_FORCE;
403 	} else {
404 		walkarg.flags = 0;
405 	}
406 
407 	walkarg.flags |= FLAG_PATH_INFO_WALK;
408 	walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
409 	walkarg.walkmode.node_args.fcn = stat_fc_dev;
410 
411 	/*
412 	 * Subtree is ALWAYS rooted at the HBA (not at the device) as
413 	 * otherwise deadlock may occur if bus is disconnected.
414 	 *
415 	 * DINFOPROP was sufficient on apidp->xport_phys prior to the support
416 	 * on scsi_vhci child node.  In order to get the link between
417 	 * scsi_vhci node and path info node the snap shot of the
418 	 * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
419 	 */
420 	ret = walk_tree(apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
421 			&walkarg, FPCFGA_WALK_NODE, &larg.l_errno);
422 
423 	/*
424 	 * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
425 	 * larg.ret is used to detect other errors. Make sure larg.ret
426 	 * is set to a correct error.
427 	 */
428 	if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
429 		if (ret != FPCFGA_APID_NOEXIST) {
430 			cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
431 		}
432 		/* if larg.ret = FPCFGA_APID_NOEXIST; */
433 		goto out;
434 	}
435 
436 	assert(larg.listp != NULL);
437 
438 	n = 0;
439 	ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
440 		NO_FLAG);
441 	if (ret != FPCFGA_OK) {
442 		cfga_err(errstring, 0, ERR_LIST, 0);
443 		ret = FPCFGA_LIB_ERR;
444 		goto out;
445 	}
446 
447 	*nelemp = n;
448 	*llpp = larg.listp;
449 	ret = FPCFGA_OK;
450 	/* FALLTHROUGH */
451 out:
452 	if (ret != FPCFGA_OK) list_free(&larg.listp);
453 	S_FREE(larg.xport_logp);
454 	HBA_CloseAdapter(handle);
455 	HBA_FreeLibrary();
456 	return (ret);
457 }
458 
459 /*
460  * Main routine for list operation when show_FCP_dev option is given.
461  * It calls various routines to consturct ldata list and
462  * postprocess the list data.
463  *
464  * The difference between do_list() and do_list_FCP_dev() is to
465  * process FCP SCSI LUN data list via uscsi report lun operation and
466  * stat lun level instead of port WWN based target level.
467  * The rest of logic is same.
468  *
469  * Overall algorithm:
470  * Get the device list on input hba port and construct ldata list for
471  * accesible devices.
472  * For each configured device, USCSI report lun is issued and ldata list
473  * with FCP device level(LUN) information is created.
474  * Stat hba port and LUN devices through walking the device tree.
475  * Verify the validity of the list data.
476  */
477 fpcfga_ret_t
do_list_FCP_dev(const char * ap_id,uint_t flags,fpcfga_cmd_t cmd,ldata_list_t ** llpp,int * nelemp,char ** errstring)478 do_list_FCP_dev(
479 	const char *ap_id,
480 	uint_t flags,
481 	fpcfga_cmd_t cmd,
482 	ldata_list_t **llpp,
483 	int *nelemp,
484 	char **errstring)
485 {
486 	int		n = -1, l_errno = 0, limited_stat, len;
487 	walkarg_t	walkarg;
488 	fpcfga_list_t	larg = {NULL};
489 	fpcfga_ret_t	ret;
490 	la_wwn_t	pwwn;
491 	char		*xport_phys = NULL, *dyn = NULL, *dyncomp = NULL,
492 			*lun_dyn = NULL;
493 	apid_t		apid_con = {NULL};
494 	HBA_HANDLE	handle;
495 	HBA_PORTATTRIBUTES	portAttrs;
496 	HBA_PORTATTRIBUTES	discPortAttrs;
497 	HBA_STATUS		status;
498 	int			portIndex, discIndex;
499 	int			retry;
500 	uint64_t		lun = 0;
501 	struct scsi_inquiry inq;
502 	struct scsi_extended_sense sense;
503 	HBA_UINT8		scsiStatus;
504 	uint32_t		inquirySize = sizeof (inq),
505 				senseSize = sizeof (sense);
506 
507 	if (*llpp != NULL || *nelemp != 0) {
508 		return (FPCFGA_ERR);
509 	}
510 
511 	if ((xport_phys = pathdup(ap_id, &l_errno)) == NULL) {
512 		cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
513 		return (FPCFGA_LIB_ERR);
514 	}
515 
516 	/* Extract the base(hba) and dynamic(device) component if any */
517 	if ((dyn = GET_DYN(xport_phys)) != NULL) {
518 		len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
519 		dyncomp = calloc(1, len);
520 		if (dyncomp == NULL) {
521 			cfga_err(errstring, errno, ERR_OP_FAILED, 0);
522 			S_FREE(xport_phys);
523 			return (FPCFGA_LIB_ERR);
524 		}
525 
526 		(void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
527 		/* Remove the dynamic component from the base. */
528 		*dyn = '\0';
529 		/* if lun dyncomp exists delete it */
530 		if ((lun_dyn = GET_LUN_DYN(dyncomp)) != NULL) {
531 			*lun_dyn = '\0';
532 		}
533 	}
534 
535 	apid_con.xport_phys = xport_phys;
536 	apid_con.dyncomp = dyncomp;
537 	apid_con.flags = flags;
538 
539 	larg.apidp = &apid_con;
540 
541 	/* Create the hba logid (also base component of logical ap_id) */
542 	ret = make_xport_logid(larg.apidp->xport_phys, &larg.xport_logp,
543 		&l_errno);
544 	if (ret != FPCFGA_OK) {
545 		cfga_err(errstring, l_errno, ERR_LIST, 0);
546 		S_FREE(larg.apidp->xport_phys);
547 		S_FREE(larg.apidp->dyncomp);
548 		return (FPCFGA_ERR);
549 	}
550 
551 	assert(larg.xport_logp != NULL);
552 
553 	larg.cmd = cmd;
554 	larg.xport_rstate = CFGA_STAT_NONE;
555 
556 	if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
557 	    &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
558 	    S_FREE(larg.xport_logp);
559 	    S_FREE(larg.apidp->dyncomp);
560 	    return (ret);
561 	}
562 
563 	/*
564 	 * If stating a specific device, we will do limited stat on fca port.
565 	 * otherwise full stat on fca part is required.
566 	 * If stating a specific device we don't know if it exists or is
567 	 * configured yet.  larg.ret is set to apid noexist for do_stat_dev.
568 	 * otherwise larg.ret is set to ok initially.
569 	 */
570 	if (larg.cmd == FPCFGA_STAT_FC_DEV) {
571 		limited_stat = 1;		/* for do_stat_fca_xport */
572 		larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev	*/
573 	} else {
574 		limited_stat = 0;		/* for do_stat_fca_xport */
575 		larg.ret = FPCFGA_OK;		/* for stat_fc_dev	*/
576 	}
577 
578 	/* For all list commands, the fca port needs to be stat'ed */
579 	if ((ret = do_stat_fca_xport(&larg, limited_stat,
580 		portAttrs)) != FPCFGA_OK) {
581 		cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
582 		list_free(&larg.listp);
583 		S_FREE(larg.xport_logp);
584 		S_FREE(larg.apidp->xport_phys);
585 		S_FREE(larg.apidp->dyncomp);
586 		HBA_CloseAdapter(handle);
587 		HBA_FreeLibrary();
588 		return (ret);
589 	}
590 
591 	/*
592 	 * If stat'ing a FCA port or ALL, we have the bus stat data at
593 	 * this point.
594 	 * Assume that the bus has no configured children.
595 	 */
596 	larg.chld_config = CFGA_STAT_UNCONFIGURED;
597 
598 	switch (larg.cmd) {
599 	case FPCFGA_STAT_FC_DEV:
600 		/* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
601 		if (cvt_dyncomp_to_lawwn(larg.apidp->dyncomp, &pwwn) != 0) {
602 			cfga_err(errstring, 0, ERR_LIST, 0);
603 			list_free(&larg.listp);
604 			S_FREE(larg.xport_logp);
605 			S_FREE(larg.apidp->xport_phys);
606 			S_FREE(larg.apidp->dyncomp);
607 			HBA_CloseAdapter(handle);
608 			HBA_FreeLibrary();
609 			return (FPCFGA_LIB_ERR);
610 		}
611 		/*
612 		 * if the dyncomp exists on disco ports construct list_data
613 		 * otherwise return FPCFGA_APID_NOEXIST.
614 		 */
615 		retry = 0;
616 		do {
617 		    status = getPortAttrsByWWN(handle,
618 			*((HBA_WWN *)(&pwwn)), &discPortAttrs);
619 		    if (status == HBA_STATUS_ERROR_STALE_DATA) {
620 			/* get Port Attributes again after refresh. */
621 			HBA_RefreshInformation(handle);
622 		    } else {
623 			break; /* either okay or some other error */
624 		    }
625 		} while (retry++ < HBA_MAX_RETRIES);
626 
627 		if (status == HBA_STATUS_OK) {
628 			/*
629 			 * if dyncomp exists only in dev list
630 			 * construct  ldata_list and return.
631 			 * otherwise continue to stat on dev tree with
632 			 * larg.ret set to access_ok which informs stat_fc_dev
633 			 * the existence of device on dev_list.
634 			 *
635 			 * if path is null that guatantees the node is not
636 			 * configured.  if node is detached the path
637 			 * is incomplete and not usable for further
638 			 * operations like uscsi_inq so take care of it here.
639 			 */
640 			status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
641 				    discPortAttrs.PortWWN, lun, 0, 0,
642 				    &inq, &inquirySize, &scsiStatus,
643 				    &sense, &senseSize);
644 			if (status == HBA_STATUS_OK) {
645 				inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
646 			} else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
647 				inq.inq_dtype = DTYPE_UNKNOWN;
648 			} else {
649 			    inq.inq_dtype = ERR_INQ_DTYPE;
650 			}
651 
652 			if (init_ldata_for_accessible_dev(larg.apidp->dyncomp,
653 				inq.inq_dtype, &larg) != FPCFGA_OK) {
654 				cfga_err(errstring, larg.l_errno,
655 					ERR_LIST, 0);
656 				list_free(&larg.listp);
657 				S_FREE(larg.xport_logp);
658 				S_FREE(larg.apidp->xport_phys);
659 				S_FREE(larg.apidp->dyncomp);
660 				HBA_CloseAdapter(handle);
661 				HBA_FreeLibrary();
662 				return (FPCFGA_LIB_ERR);
663 			}
664 			if ((ret = get_accessible_FCP_dev_ldata(
665 					larg.apidp->dyncomp, &larg, &l_errno))
666 					!= FPCFGA_OK) {
667 				cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
668 				list_free(&larg.listp);
669 				S_FREE(larg.xport_logp);
670 				S_FREE(larg.apidp->xport_phys);
671 				S_FREE(larg.apidp->dyncomp);
672 				HBA_CloseAdapter(handle);
673 				HBA_FreeLibrary();
674 				return (FPCFGA_LIB_ERR);
675 			} else {
676 				/* continue to stat dev with access okay. */
677 				larg.ret = FPCFGA_ACCESS_OK;
678 			}
679 		} else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
680 			/*
681 			 * path indicates if the node exists in dev tree.
682 			 * if not found in dev tree return apid no exist.
683 			 * otherwise continue to stat with larg.ret set to
684 			 * apid_noexist.
685 			 */
686 			if (larg.apidp->lunlist == NULL) {
687 				list_free(&larg.listp);
688 				S_FREE(larg.xport_logp);
689 				HBA_CloseAdapter(handle);
690 				HBA_FreeLibrary();
691 				return (FPCFGA_APID_NOEXIST);
692 			}
693 		} else {	/* not found or any error */
694 			/*
695 			 * continue to stat dev with larg.ret set to
696 			 * apid_noexist.
697 			 */
698 			larg.ret = FPCFGA_APID_NOEXIST;
699 		}
700 		break;
701 	case FPCFGA_STAT_ALL:
702 		/*
703 		 * for each dev in disco ports, create a ldata_list element.
704 		 * if if no disco ports found, continue to stat on devinfo tree
705 		 * to see if any node exist on the fca port.
706 		 */
707 		for (discIndex = 0;
708 			discIndex < portAttrs.NumberofDiscoveredPorts;
709 			discIndex++) {
710 		    if (getDiscPortAttrs(handle, portIndex,
711 			discIndex, &discPortAttrs)) {
712 			/* Move on to the next target */
713 			continue;
714 		    }
715 		    memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
716 		    cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
717 		    if (dyncomp == NULL) {
718 			cfga_err(errstring, l_errno, ERR_LIST, 0);
719 			list_free(&larg.listp);
720 			S_FREE(larg.xport_logp);
721 			S_FREE(larg.apidp->xport_phys);
722 			S_FREE(larg.apidp->dyncomp);
723 			HBA_CloseAdapter(handle);
724 			HBA_FreeLibrary();
725 			return (FPCFGA_LIB_ERR);
726 		    }
727 		    status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
728 			    discPortAttrs.PortWWN, lun, 0, 0,
729 			    &inq, &inquirySize, &scsiStatus,
730 			    &sense, &senseSize);
731 		    if (status == HBA_STATUS_OK) {
732 			    inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
733 		    } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
734 			    inq.inq_dtype = DTYPE_UNKNOWN;
735 		    } else {
736 			    inq.inq_dtype = ERR_INQ_DTYPE;
737 		    }
738 		    if ((ret = init_ldata_for_accessible_dev(
739 			    dyncomp, inq.inq_dtype, &larg)) != FPCFGA_OK) {
740 			S_FREE(dyncomp);
741 			cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
742 			list_free(&larg.listp);
743 			S_FREE(larg.xport_logp);
744 			S_FREE(larg.apidp->xport_phys);
745 			S_FREE(larg.apidp->dyncomp);
746 			HBA_CloseAdapter(handle);
747 			HBA_FreeLibrary();
748 			return (FPCFGA_LIB_ERR);
749 		    }
750 		    if ((ret = get_accessible_FCP_dev_ldata(
751 			dyncomp, &larg, &l_errno)) != FPCFGA_OK) {
752 			S_FREE(dyncomp);
753 			cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
754 			list_free(&larg.listp);
755 			S_FREE(larg.xport_logp);
756 			S_FREE(larg.apidp->xport_phys);
757 			S_FREE(larg.apidp->dyncomp);
758 			HBA_CloseAdapter(handle);
759 			HBA_FreeLibrary();
760 			return (ret);
761 		    }
762 		    S_FREE(dyncomp);
763 		}
764 		break;
765 	/* default: continue */
766 	}
767 
768 	/* we need to stat at least 1 device for all commands */
769 	if ((larg.apidp->flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) {
770 		walkarg.flags = FLAG_DEVINFO_FORCE;
771 	} else {
772 		walkarg.flags = 0;
773 	}
774 
775 	walkarg.flags |= FLAG_PATH_INFO_WALK;
776 	walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
777 	walkarg.walkmode.node_args.fcn = stat_FCP_dev;
778 
779 	/*
780 	 * Subtree is ALWAYS rooted at the HBA (not at the device) as
781 	 * otherwise deadlock may occur if bus is disconnected.
782 	 *
783 	 * DINFOPROP was sufficient on apidp->xport_phys prior to the support
784 	 * on scsi_vhci child node.  In order to get the link between
785 	 * scsi_vhci node and path info node the snap shot of the
786 	 * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
787 	 */
788 	ret = walk_tree(larg.apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
789 			&walkarg, FPCFGA_WALK_NODE, &larg.l_errno);
790 
791 	/*
792 	 * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
793 	 * larg.ret is used to detect other errors. Make sure larg.ret
794 	 * is set to a correct error.
795 	 */
796 	if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
797 		if (ret != FPCFGA_APID_NOEXIST) {
798 			cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
799 		}
800 		/* if larg.ret = FPCFGA_APID_NOEXIST return. */
801 		list_free(&larg.listp);
802 		S_FREE(larg.xport_logp);
803 		S_FREE(larg.apidp->xport_phys);
804 		S_FREE(larg.apidp->dyncomp);
805 		HBA_CloseAdapter(handle);
806 		HBA_FreeLibrary();
807 		return (ret);
808 	}
809 
810 	assert(larg.listp != NULL);
811 
812 	n = 0;
813 	ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
814 			flags);
815 	if (ret != FPCFGA_OK) {
816 		cfga_err(errstring, 0, ERR_LIST, 0);
817 		list_free(&larg.listp);
818 		S_FREE(larg.xport_logp);
819 		S_FREE(larg.apidp->xport_phys);
820 		S_FREE(larg.apidp->dyncomp);
821 		HBA_CloseAdapter(handle);
822 		HBA_FreeLibrary();
823 		return (FPCFGA_LIB_ERR);
824 	}
825 
826 	*nelemp = n;
827 	*llpp = larg.listp;
828 	ret = FPCFGA_OK;
829 	S_FREE(larg.xport_logp);
830 	S_FREE(larg.apidp->xport_phys);
831 	S_FREE(larg.apidp->dyncomp);
832 	HBA_CloseAdapter(handle);
833 	HBA_FreeLibrary();
834 	return (FPCFGA_OK);
835 }
836 
837 /*
838  * This routine returns initialize struct fcp_ioctl.
839  */
840 static void
init_fcp_scsi_cmd(struct fcp_scsi_cmd * fscsi,uchar_t * lun_num,la_wwn_t * pwwn,void * scmdbuf,size_t scmdbuf_len,void * respbuf,size_t respbuf_len,void * sensebuf,size_t sensebuf_len)841 init_fcp_scsi_cmd(
842 	struct fcp_scsi_cmd *fscsi,
843 	uchar_t *lun_num,
844 	la_wwn_t *pwwn,
845 	void *scmdbuf,
846 	size_t scmdbuf_len,
847 	void *respbuf,
848 	size_t respbuf_len,
849 	void *sensebuf,
850 	size_t sensebuf_len)
851 {
852 	memset(fscsi, 0, sizeof (struct fcp_scsi_cmd));
853 	memset(scmdbuf, 0, scmdbuf_len);
854 	memcpy(fscsi->scsi_fc_pwwn.raw_wwn, pwwn, sizeof (u_longlong_t));
855 	fscsi->scsi_fc_rspcode = 0;
856 	fscsi->scsi_flags = FCP_SCSI_READ;
857 	fscsi->scsi_timeout = FCP_SCSI_CMD_TIMEOUT;  /* second */
858 	fscsi->scsi_cdbbufaddr = (caddr_t)scmdbuf;
859 	fscsi->scsi_cdblen = scmdbuf_len;
860 	fscsi->scsi_bufaddr = (caddr_t)respbuf;
861 	fscsi->scsi_buflen = respbuf_len;
862 	fscsi->scsi_bufresid = 0;
863 	fscsi->scsi_bufstatus = 0;
864 	fscsi->scsi_rqbufaddr = (caddr_t)sensebuf;
865 	fscsi->scsi_rqlen = sensebuf_len;
866 	fscsi->scsi_rqresid = 0;
867 	memcpy(&fscsi->scsi_lun, lun_num, sizeof (fscsi->scsi_lun));
868 }
869 
870 /*
871  * This routine returns issues FCP_TGT_SEND_SCSI
872  */
873 static fpcfga_ret_t
issue_fcp_scsi_cmd(const char * xport_phys,struct fcp_scsi_cmd * fscsi,int * l_errnop)874 issue_fcp_scsi_cmd(
875 	const char *xport_phys,
876 	struct fcp_scsi_cmd *fscsi,
877 	int *l_errnop)
878 {
879 	struct stat	stbuf;
880 	int fcp_fd, retry, rv;
881 
882 	if (stat(xport_phys, &stbuf) < 0) {
883 		*l_errnop = errno;
884 		return (FPCFGA_LIB_ERR);
885 	}
886 
887 	fscsi->scsi_fc_port_num = (uint32_t)minor(stbuf.st_rdev);
888 	fcp_fd = open(FCP_PATH, O_RDONLY | O_NDELAY);
889 	retry = 0;
890 	while (fcp_fd < 0 && retry++ < OPEN_RETRY_COUNT && (
891 		errno == EBUSY || errno == EAGAIN)) {
892 		(void) usleep(OPEN_RETRY_INTERVAL);
893 		fcp_fd = open(FCP_PATH, O_RDONLY|O_NDELAY);
894 	}
895 	if (fcp_fd < 0) {
896 		*l_errnop = errno;
897 		return (FPCFGA_LIB_ERR);
898 	}
899 
900 	rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
901 	retry = 0;
902 	while ((rv != 0 && retry++ < IOCTL_RETRY_COUNT &&
903 			(errno == EBUSY || errno == EAGAIN)) ||
904 			(retry++ < IOCTL_RETRY_COUNT &&
905 			((uchar_t)fscsi->scsi_bufstatus & STATUS_MASK)
906 			== STATUS_BUSY)) {
907 		(void) usleep(IOCTL_RETRY_INTERVAL);
908 		rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
909 	}
910 	close(fcp_fd);
911 
912 	if (fscsi->scsi_fc_status == FC_DEVICE_NOT_TGT) {
913 		return (FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT);
914 	} else if (rv != 0 || fscsi->scsi_bufstatus != 0) {
915 		*l_errnop = errno;
916 		return (FPCFGA_FCP_TGT_SEND_SCSI_FAILED);
917 	}
918 	return (FPCFGA_OK);
919 }
920 
921 /*
922  * This routine returns standard inq data for
923  * a target represented by dyncomp.
924  *
925  * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get inquiry data.
926  *
927  * Caller should free the *inq_buf.
928  */
929 static fpcfga_ret_t
get_standard_inq_data(const char * xport_phys,const char * dyncomp,uchar_t * lun_num,struct scsi_inquiry ** inq_buf,int * l_errnop)930 get_standard_inq_data(
931 	const char *xport_phys,
932 	const char *dyncomp,
933 	uchar_t *lun_num,
934 	struct scsi_inquiry **inq_buf,
935 	int *l_errnop)
936 {
937 	struct fcp_scsi_cmd	fscsi;
938 	struct scsi_extended_sense sensebuf;
939 	union scsi_cdb  scsi_inq_req;
940 	la_wwn_t	pwwn;
941 	int 	alloc_len;
942 	fpcfga_ret_t ret;
943 
944 
945 	alloc_len = sizeof (struct scsi_inquiry);
946 	if ((*inq_buf = (struct scsi_inquiry *)calloc(1, alloc_len)) == NULL) {
947 		*l_errnop = errno;
948 		return (FPCFGA_LIB_ERR);
949 	}
950 
951 	if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
952 		return (FPCFGA_LIB_ERR);
953 	}
954 
955 	init_fcp_scsi_cmd(&fscsi, lun_num, &pwwn, &scsi_inq_req,
956 		sizeof (scsi_inq_req), *inq_buf, alloc_len, &sensebuf,
957 		sizeof (struct scsi_extended_sense));
958 	scsi_inq_req.scc_cmd = SCMD_INQUIRY;
959 	scsi_inq_req.g0_count0 = sizeof (struct scsi_inquiry);
960 
961 	if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
962 			!= FPCFGA_OK) {
963 		S_FREE(*inq_buf);
964 		return (ret);
965 	}
966 
967 	return (FPCFGA_OK);
968 }
969 
970 /*
971  * This routine returns report lun data and number of luns found
972  * on a target represented by dyncomp.
973  *
974  * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get report lun data.
975  *
976  * Caller should free the *resp_buf when FPCFGA_OK is returned.
977  */
978 fpcfga_ret_t
get_report_lun_data(const char * xport_phys,const char * dyncomp,int * num_luns,report_lun_resp_t ** resp_buf,struct scsi_extended_sense * sensebuf,int * l_errnop)979 get_report_lun_data(
980 	const char *xport_phys,
981 	const char *dyncomp,
982 	int *num_luns,
983 	report_lun_resp_t **resp_buf,
984 	struct scsi_extended_sense *sensebuf,
985 	int *l_errnop)
986 {
987 	struct fcp_scsi_cmd	fscsi;
988 	union scsi_cdb  scsi_rl_req;
989 	la_wwn_t	pwwn;
990 	int 	alloc_len;
991 	fpcfga_ret_t 	ret;
992 	uchar_t		lun_data[SAM_LUN_SIZE];
993 
994 	alloc_len = sizeof (struct report_lun_resp);
995 	if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len)) == NULL) {
996 		*l_errnop = errno;
997 		return (FPCFGA_LIB_ERR);
998 	}
999 
1000 	if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
1001 		S_FREE(*resp_buf);
1002 		return (FPCFGA_LIB_ERR);
1003 	}
1004 
1005 	/* sending to LUN 0 so initializing lun_data buffer to be 0 */
1006 	memset(lun_data, 0, sizeof (lun_data));
1007 	init_fcp_scsi_cmd(&fscsi, lun_data, &pwwn, &scsi_rl_req,
1008 	    sizeof (scsi_rl_req), *resp_buf, alloc_len, sensebuf,
1009 	    sizeof (struct scsi_extended_sense));
1010 	scsi_rl_req.scc_cmd = FP_SCMD_REPORT_LUN;
1011 	FORMG5COUNT(&scsi_rl_req, alloc_len);
1012 
1013 	if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
1014 			!= FPCFGA_OK) {
1015 		S_FREE(*resp_buf);
1016 		return (ret);
1017 	}
1018 
1019 	if (ntohl((*resp_buf)->num_lun) >
1020 		(sizeof (struct report_lun_resp) - REPORT_LUN_HDR_SIZE)) {
1021 		alloc_len = (*resp_buf)->num_lun + REPORT_LUN_HDR_SIZE;
1022 		S_FREE(*resp_buf);
1023 		if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len))
1024 		    == NULL) {
1025 			*l_errnop = errno;
1026 			return (FPCFGA_LIB_ERR);
1027 		}
1028 		(void) memset((char *)*resp_buf, 0, alloc_len);
1029 		FORMG5COUNT(&scsi_rl_req, alloc_len);
1030 
1031 		fscsi.scsi_bufaddr = (caddr_t)*resp_buf;
1032 		fscsi.scsi_buflen = alloc_len;
1033 
1034 		if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
1035 				!= FPCFGA_OK) {
1036 			S_FREE(*resp_buf);
1037 			return (ret);
1038 		}
1039 	}
1040 
1041 	/* num_lun represent number of luns * 8. */
1042 	*num_luns = ntohl((*resp_buf)->num_lun) >> 3;
1043 
1044 	return (FPCFGA_OK);
1045 }
1046 
1047 /*
1048  * Routine for consturct ldata list for each FCP SCSI LUN device
1049  * for a discovered target device.
1050  * It calls get_report_lun_data to get report lun data and
1051  * construct ldata list per each lun.
1052  *
1053  * It is called only when show_FCP_dev option is given.
1054  *
1055  * Overall algorithm:
1056  * Get the report lun data thru FCP passthru ioctl.
1057  * Call init_ldata_for_accessible_FCP_dev to process the report LUN data.
1058  * For each LUN found standard inquiry is issued to get device type.
1059  */
1060 static fpcfga_ret_t
get_accessible_FCP_dev_ldata(const char * dyncomp,fpcfga_list_t * lap,int * l_errnop)1061 get_accessible_FCP_dev_ldata(
1062 	const char *dyncomp,
1063 	fpcfga_list_t *lap,
1064 	int *l_errnop)
1065 {
1066 	report_lun_resp_t	    *resp_buf;
1067 	struct scsi_extended_sense  sense;
1068 	int			    num_luns;
1069 	fpcfga_ret_t		    ret;
1070 
1071 	memset(&sense, 0, sizeof (sense));
1072 	if ((ret = get_report_lun_data(lap->apidp->xport_phys, dyncomp,
1073 		&num_luns, &resp_buf, &sense, l_errnop)) != FPCFGA_OK) {
1074 		/*
1075 		 * when report lun data fails then return FPCFGA_OK thus
1076 		 * keep the ldata for the target which is acquired previously.
1077 		 * For remote hba node this will be normal.
1078 		 * For a target error may already be detected through
1079 		 * FCP_TGT_INQ.
1080 		 */
1081 		if ((ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) ||
1082 		    (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT)) {
1083 			ret = FPCFGA_OK;
1084 		}
1085 		return (ret);
1086 	}
1087 
1088 	if (num_luns > 0) {
1089 		ret = init_ldata_for_accessible_FCP_dev(
1090 			dyncomp, num_luns, resp_buf, lap, l_errnop);
1091 	} else {
1092 		/*
1093 		 * proceed with to stat if no lun found.
1094 		 * This will make the target apid will be kept.
1095 		 */
1096 		ret = FPCFGA_OK;
1097 	}
1098 
1099 	S_FREE(resp_buf);
1100 	return (ret);
1101 }
1102 
1103 /*
1104  * Routine for checking validity of ldata list based on input argumemnt.
1105  * Set the occupant state of hba port if the list is valid.
1106  */
1107 static fpcfga_ret_t
postprocess_list_data(const ldata_list_t * listp,fpcfga_cmd_t cmd,cfga_stat_t chld_config,int * np,uint_t flags)1108 postprocess_list_data(
1109 	const ldata_list_t *listp,
1110 	fpcfga_cmd_t cmd,
1111 	cfga_stat_t chld_config,
1112 	int *np,
1113 	uint_t flags)
1114 {
1115 	ldata_list_t *tmplp = NULL;
1116 	cfga_list_data_t *xport_ldatap = NULL;
1117 	int i;
1118 
1119 
1120 	*np = 0;
1121 
1122 	if (listp == NULL) {
1123 		return (FPCFGA_ERR);
1124 	}
1125 
1126 	tmplp = (ldata_list_t *)listp;
1127 	for (i = 0; tmplp != NULL; tmplp = tmplp->next) {
1128 		i++;
1129 		if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) {
1130 			/* A bus stat data */
1131 			assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL);
1132 			xport_ldatap = &tmplp->ldata;
1133 #ifdef DEBUG
1134 		} else {
1135 			assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL);
1136 #endif
1137 		}
1138 	}
1139 
1140 	switch (cmd) {
1141 	case FPCFGA_STAT_FC_DEV:
1142 		if ((flags & FLAG_FCP_DEV) == FLAG_FCP_DEV) {
1143 			if (i < 1 || xport_ldatap != NULL) {
1144 				return (FPCFGA_LIB_ERR);
1145 			}
1146 		} else {
1147 			if (i != 1 || xport_ldatap != NULL) {
1148 				return (FPCFGA_LIB_ERR);
1149 			}
1150 		}
1151 		break;
1152 	case FPCFGA_STAT_FCA_PORT:
1153 		if (i != 1 || xport_ldatap == NULL) {
1154 			return (FPCFGA_LIB_ERR);
1155 		}
1156 		break;
1157 	case FPCFGA_STAT_ALL:
1158 		if (i < 1 || xport_ldatap == NULL) {
1159 			return (FPCFGA_LIB_ERR);
1160 		}
1161 		break;
1162 	default:
1163 		return (FPCFGA_LIB_ERR);
1164 	}
1165 
1166 	*np = i;
1167 
1168 	/* Fill in the occupant (child) state. */
1169 	if (xport_ldatap != NULL) {
1170 		xport_ldatap->ap_o_state = chld_config;
1171 	}
1172 	return (FPCFGA_OK);
1173 }
1174 
1175 /*
1176  * Routine for checking each target device found in device tree.
1177  * When the matching port WWN dev is found from the accessble ldata list
1178  * the target device is updated with configured ostate.
1179  *
1180  * Overall algorithm:
1181  * Parse the device tree to find configured devices which matches with
1182  * list argument.  If cmd is stat on a specific target device it
1183  * matches port WWN and continues to further processing.  If cmd is
1184  * stat on hba port all the device target under the hba are processed.
1185  */
1186 static int
stat_fc_dev(di_node_t node,void * arg)1187 stat_fc_dev(di_node_t node, void *arg)
1188 {
1189 	fpcfga_list_t *lap = NULL;
1190 	char *devfsp = NULL, *nodepath = NULL;
1191 	size_t len = 0;
1192 	int limited_stat = 0, match_minor, rv;
1193 	fpcfga_ret_t ret;
1194 	di_prop_t prop = DI_PROP_NIL;
1195 	uchar_t	*port_wwn_data;
1196 	char	port_wwn[WWN_SIZE*2+1];
1197 	int	count;
1198 
1199 	lap = (fpcfga_list_t *)arg;
1200 
1201 	/*
1202 	 * Skip partial nodes
1203 	 *
1204 	 * This checking is from the scsi plug-in and will be deleted for
1205 	 * fp plug-in. The node will be processed for fp even if it is
1206 	 * in driver detached state. From fp perspective the node is configured
1207 	 * as long as the node is not in offline or down state.
1208 	 * scsi plug-in considers the known state when it is offlined
1209 	 * regradless of driver detached state or when it is not in driver
1210 	 * detached state like normal state.
1211 	 * If the node is only in driver detached state it is considered as
1212 	 * unknown state.
1213 	 *
1214 	 * if (!known_state(node) && (lap->cmd != FPCFGA_STAT_FC_DEV)) {
1215 	 *	return (DI_WALK_CONTINUE);
1216 	 *
1217 	 */
1218 
1219 	devfsp = di_devfs_path(node);
1220 	if (devfsp == NULL) {
1221 		rv = DI_WALK_CONTINUE;
1222 		goto out;
1223 	}
1224 
1225 	len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
1226 
1227 	nodepath = calloc(1, len);
1228 	if (nodepath == NULL) {
1229 		lap->l_errno = errno;
1230 		lap->ret = FPCFGA_LIB_ERR;
1231 		rv = DI_WALK_TERMINATE;
1232 		goto out;
1233 	}
1234 
1235 	(void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
1236 
1237 	/* Skip node if it is HBA */
1238 	match_minor = 0;
1239 	if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
1240 		rv = DI_WALK_CONTINUE;
1241 		goto out;
1242 	}
1243 
1244 	/* If stat'ing a specific device, is this node that device */
1245 	if (lap->cmd == FPCFGA_STAT_FC_DEV) {
1246 		/* checks port wwn property to find a match */
1247 		while ((prop = di_prop_next(node, prop))
1248 					!= DI_PROP_NIL) {
1249 			if ((strcmp(PORT_WWN_PROP,
1250 				di_prop_name(prop)) == 0) &&
1251 				(di_prop_type(prop) ==
1252 					DI_PROP_TYPE_BYTE)) {
1253 				break;
1254 			}
1255 		}
1256 
1257 		if (prop != DI_PROP_NIL) {
1258 			count = di_prop_bytes(prop, &port_wwn_data);
1259 			if (count != WWN_SIZE) {
1260 				lap->ret = FPCFGA_LIB_ERR;
1261 				rv = DI_WALK_TERMINATE;
1262 				goto out;
1263 			}
1264 			(void) sprintf(port_wwn, "%016llx",
1265 				(wwnConversion(port_wwn_data)));
1266 			/*
1267 			 * port wwn doesn't match contine to walk
1268 			 * if match call do_stat_fc_dev.
1269 			 */
1270 			if (strncmp(port_wwn, lap->apidp->dyncomp,
1271 					WWN_SIZE*2)) {
1272 				rv = DI_WALK_CONTINUE;
1273 				goto out;
1274 			}
1275 		} else {
1276 			rv = DI_WALK_CONTINUE;
1277 			goto out;
1278 		}
1279 	}
1280 
1281 	/*
1282 	 * If stat'ing a xport only, we look at device nodes only to get
1283 	 * xport configuration status. So a limited stat will suffice.
1284 	 */
1285 	if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
1286 		limited_stat = 1;
1287 	} else {
1288 		limited_stat = 0;
1289 	}
1290 
1291 	/*
1292 	 * Ignore errors if stat'ing a bus or listing all
1293 	 */
1294 	ret = do_stat_fc_dev(node, nodepath, lap, limited_stat);
1295 	if (ret != FPCFGA_OK) {
1296 		if (lap->cmd == FPCFGA_STAT_FC_DEV) {
1297 			lap->ret = ret;
1298 			rv = DI_WALK_TERMINATE;
1299 		} else {
1300 			rv = DI_WALK_CONTINUE;
1301 		}
1302 		goto out;
1303 	}
1304 
1305 	/* Are we done ? */
1306 	rv = DI_WALK_CONTINUE;
1307 	if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
1308 	    lap->chld_config == CFGA_STAT_CONFIGURED) {
1309 		rv = DI_WALK_TERMINATE;
1310 	} else if (lap->cmd == FPCFGA_STAT_FC_DEV) {
1311 		/*
1312 		 * If stat'ing a specific device, we are done at this point.
1313 		 */
1314 		rv = DI_WALK_TERMINATE;
1315 	}
1316 
1317 	/*FALLTHRU*/
1318 out:
1319 	S_FREE(nodepath);
1320 	if (devfsp != NULL) di_devfs_path_free(devfsp);
1321 	return (rv);
1322 }
1323 
1324 /*
1325  * Routine for checking each FCP SCSI LUN device found in device tree.
1326  * When the matching port WWN and LUN are found from the accessble ldata list
1327  * the FCP SCSI LUN is updated with configured ostate.
1328  *
1329  * Overall algorithm:
1330  * Parse the device tree to find configured devices which matches with
1331  * list argument.  If cmd is stat on a specific target device it
1332  * matches port WWN and continues to further processing.  If cmd is
1333  * stat on hba port all the FCP SCSI LUN under the hba are processed.
1334  */
1335 static int
stat_FCP_dev(di_node_t node,void * arg)1336 stat_FCP_dev(di_node_t node, void *arg)
1337 {
1338 	fpcfga_list_t *lap = NULL;
1339 	char *devfsp = NULL, *nodepath = NULL;
1340 	size_t len = 0;
1341 	int limited_stat = 0, match_minor, rv, di_ret;
1342 	fpcfga_ret_t ret;
1343 	uchar_t	*port_wwn_data;
1344 	char	port_wwn[WWN_SIZE*2+1];
1345 
1346 	lap = (fpcfga_list_t *)arg;
1347 
1348 	devfsp = di_devfs_path(node);
1349 	if (devfsp == NULL) {
1350 		rv = DI_WALK_CONTINUE;
1351 		goto out;
1352 	}
1353 
1354 	len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
1355 
1356 	nodepath = calloc(1, len);
1357 	if (nodepath == NULL) {
1358 		lap->l_errno = errno;
1359 		lap->ret = FPCFGA_LIB_ERR;
1360 		rv = DI_WALK_TERMINATE;
1361 		goto out;
1362 	}
1363 
1364 	(void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
1365 
1366 	/* Skip node if it is HBA */
1367 	match_minor = 0;
1368 	if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
1369 		rv = DI_WALK_CONTINUE;
1370 		goto out;
1371 	}
1372 
1373 	/* If stat'ing a specific device, is this node that device */
1374 	if (lap->cmd == FPCFGA_STAT_FC_DEV) {
1375 		/* checks port wwn property to find a match */
1376 		di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
1377 			PORT_WWN_PROP, &port_wwn_data);
1378 		if (di_ret == -1) {
1379 			rv = DI_WALK_CONTINUE;
1380 			goto out;
1381 		} else {
1382 			(void) sprintf(port_wwn, "%016llx",
1383 				(wwnConversion(port_wwn_data)));
1384 			/*
1385 			 * port wwn doesn't match contine to walk
1386 			 * if match call do_stat_FCP_dev.
1387 			 */
1388 			if (strncmp(port_wwn, lap->apidp->dyncomp,
1389 					WWN_SIZE*2)) {
1390 				rv = DI_WALK_CONTINUE;
1391 				goto out;
1392 			}
1393 		}
1394 	}
1395 
1396 	/*
1397 	 * If stat'ing a xport only, we look at device nodes only to get
1398 	 * xport configuration status. So a limited stat will suffice.
1399 	 */
1400 	if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
1401 		limited_stat = 1;
1402 	} else {
1403 		limited_stat = 0;
1404 	}
1405 
1406 	/*
1407 	 * Ignore errors if stat'ing a bus or listing all
1408 	 */
1409 	ret = do_stat_FCP_dev(node, nodepath, lap, limited_stat);
1410 	if (ret != FPCFGA_OK) {
1411 		rv = DI_WALK_CONTINUE;
1412 		goto out;
1413 	}
1414 
1415 	/* Are we done ? */
1416 	rv = DI_WALK_CONTINUE;
1417 	if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
1418 	    lap->chld_config == CFGA_STAT_CONFIGURED) {
1419 		rv = DI_WALK_TERMINATE;
1420 	}
1421 
1422 	/*FALLTHRU*/
1423 out:
1424 	S_FREE(nodepath);
1425 	if (devfsp != NULL) di_devfs_path_free(devfsp);
1426 	return (rv);
1427 }
1428 
1429 static fpcfga_ret_t
do_stat_fca_xport(fpcfga_list_t * lap,int limited_stat,HBA_PORTATTRIBUTES portAttrs)1430 do_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
1431 	HBA_PORTATTRIBUTES portAttrs)
1432 {
1433 	cfga_list_data_t *clp = NULL;
1434 	ldata_list_t *listp = NULL;
1435 	int l_errno = 0;
1436 	uint_t devinfo_state = 0;
1437 	walkarg_t walkarg;
1438 	fpcfga_ret_t ret;
1439 	cfga_cond_t cond = CFGA_COND_UNKNOWN;
1440 
1441 	assert(lap->xport_logp != NULL);
1442 
1443 	/* Get xport state */
1444 	if (lap->apidp->flags == FLAG_DEVINFO_FORCE) {
1445 		walkarg.flags = FLAG_DEVINFO_FORCE;
1446 	} else {
1447 		walkarg.flags = 0;
1448 	}
1449 	walkarg.walkmode.node_args.flags = 0;
1450 	walkarg.walkmode.node_args.fcn = get_xport_state;
1451 
1452 	ret = walk_tree(lap->apidp->xport_phys, &devinfo_state,
1453 		DINFOCPYALL | DINFOPATH, &walkarg, FPCFGA_WALK_NODE, &l_errno);
1454 	if (ret == FPCFGA_OK) {
1455 		lap->xport_rstate = xport_devinfo_to_recep_state(devinfo_state);
1456 	} else {
1457 		lap->xport_rstate = CFGA_STAT_NONE;
1458 	}
1459 
1460 	/*
1461 	 * Get topology works okay even if the fp port is connected
1462 	 * to a switch and no devices connected to the switch.
1463 	 * In this case the list will only shows fp port info without
1464 	 * any device listed.
1465 	 */
1466 	switch (portAttrs.PortType) {
1467 		case HBA_PORTTYPE_NLPORT:
1468 			(void) snprintf(lap->xport_type,
1469 				sizeof (lap->xport_type), "%s",
1470 				FP_FC_PUBLIC_PORT_TYPE);
1471 			break;
1472 		case HBA_PORTTYPE_NPORT:
1473 			(void) snprintf(lap->xport_type,
1474 				sizeof (lap->xport_type), "%s",
1475 				FP_FC_FABRIC_PORT_TYPE);
1476 			break;
1477 		case HBA_PORTTYPE_LPORT:
1478 			(void) snprintf(lap->xport_type,
1479 				sizeof (lap->xport_type), "%s",
1480 				FP_FC_PRIVATE_PORT_TYPE);
1481 			break;
1482 		case HBA_PORTTYPE_PTP:
1483 			(void) snprintf(lap->xport_type,
1484 				sizeof (lap->xport_type), "%s",
1485 				FP_FC_PT_TO_PT_PORT_TYPE);
1486 			break;
1487 		/*
1488 		 * HBA_PORTTYPE_UNKNOWN means nothing is connected
1489 		 */
1490 		case HBA_PORTTYPE_UNKNOWN:
1491 			(void) snprintf(lap->xport_type,
1492 				sizeof (lap->xport_type), "%s",
1493 				FP_FC_PORT_TYPE);
1494 			break;
1495 		/* NOT_PRESENT, OTHER, FPORT, FLPORT */
1496 		default:
1497 			(void) snprintf(lap->xport_type,
1498 				sizeof (lap->xport_type), "%s",
1499 				FP_FC_PORT_TYPE);
1500 			cond = CFGA_COND_FAILED;
1501 			break;
1502 	}
1503 
1504 	if (limited_stat) {
1505 		/* We only want to know bus(receptacle) connect status */
1506 		return (FPCFGA_OK);
1507 	}
1508 
1509 	listp = calloc(1, sizeof (ldata_list_t));
1510 	if (listp == NULL) {
1511 		lap->l_errno = errno;
1512 		return (FPCFGA_LIB_ERR);
1513 	}
1514 
1515 	clp = &listp->ldata;
1516 
1517 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
1518 	    lap->xport_logp);
1519 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s",
1520 	    lap->apidp->xport_phys);
1521 
1522 	clp->ap_class[0] = '\0';	/* Filled by libcfgadm */
1523 	clp->ap_r_state = lap->xport_rstate;
1524 	clp->ap_o_state = lap->chld_config;
1525 	clp->ap_cond = cond;
1526 	clp->ap_busy = 0;
1527 	clp->ap_status_time = (time_t)-1;
1528 	clp->ap_info[0] = '\0';
1529 	(void) strncpy(clp->ap_type, lap->xport_type, sizeof (clp->ap_type));
1530 
1531 	/* Link it in.  lap->listp is NULL originally. */
1532 	listp->next = lap->listp;
1533 	/* lap->listp now gets cfga_list_data for the fca port. */
1534 	lap->listp = listp;
1535 
1536 	return (FPCFGA_OK);
1537 }
1538 
1539 
1540 static int
get_xport_state(di_node_t node,void * arg)1541 get_xport_state(di_node_t node, void *arg)
1542 {
1543 	uint_t *di_statep = (uint_t *)arg;
1544 
1545 	*di_statep = di_state(node);
1546 
1547 	return (DI_WALK_TERMINATE);
1548 }
1549 
1550 /*
1551  * Routine for updating ldata list based on the state of device node.
1552  * When no matching accessible ldata is found a new ldata is created
1553  * with proper state information.
1554  *
1555  * Overall algorithm:
1556  * If the device node is online and the matching ldata is found
1557  * the target device is updated with configued and unknown condition.
1558  * If the device node is offline or down and the matching ldata is found
1559  * the target device is updated with configued and unusable condition.
1560  * If the device node is online but the matching ldata is not found
1561  * the target device is created with configued and failing condition.
1562  * If the device node is offline or down and the matching ldata is not found
1563  * the target device is created with configued and unusable condition.
1564  */
1565 static fpcfga_ret_t
do_stat_fc_dev(const di_node_t node,const char * nodepath,fpcfga_list_t * lap,int limited_stat)1566 do_stat_fc_dev(
1567 	const di_node_t node,
1568 	const char *nodepath,
1569 	fpcfga_list_t *lap,
1570 	int limited_stat)
1571 {
1572 	uint_t dctl_state = 0, devinfo_state = 0;
1573 	char *dyncomp = NULL;
1574 	cfga_list_data_t *clp = NULL;
1575 	cfga_busy_t busy;
1576 	ldata_list_t *listp = NULL;
1577 	ldata_list_t *matchldp = NULL;
1578 	int l_errno = 0;
1579 	cfga_stat_t ostate;
1580 	cfga_cond_t cond;
1581 	fpcfga_ret_t ret;
1582 
1583 	assert(lap->apidp->xport_phys != NULL);
1584 	assert(lap->xport_logp != NULL);
1585 
1586 	cond = CFGA_COND_UNKNOWN;
1587 
1588 	devinfo_state = di_state(node);
1589 	ostate = dev_devinfo_to_occupant_state(devinfo_state);
1590 
1591 	/*
1592 	 * NOTE: The framework cannot currently detect layered driver
1593 	 * opens, so the busy indicator is not very reliable. Also,
1594 	 * non-root users will not be able to determine busy
1595 	 * status (libdevice needs root permissions).
1596 	 * This should probably be fixed by adding a DI_BUSY to the di_state()
1597 	 * routine in libdevinfo.
1598 	 */
1599 	if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
1600 	    &l_errno) == FPCFGA_OK) {
1601 		busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
1602 	} else {
1603 		busy = 0;
1604 	}
1605 
1606 	/* We only want to know device config state */
1607 	if (limited_stat) {
1608 		if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
1609 			strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
1610 			lap->chld_config = CFGA_STAT_CONFIGURED;
1611 		} else {
1612 			if (ostate != CFGA_STAT_UNCONFIGURED) {
1613 				lap->chld_config = CFGA_STAT_CONFIGURED;
1614 			}
1615 		}
1616 		return (FPCFGA_OK);
1617 	}
1618 
1619 	/*
1620 	 * If child device is configured, see if it is accessible also
1621 	 * for FPCFGA_STAT_FC_DEV cmd.
1622 	 */
1623 	if (lap->cmd == FPCFGA_STAT_FC_DEV) {
1624 		switch (ostate) {
1625 		case CFGA_STAT_CONFIGURED:
1626 			/*
1627 			 * if configured and not accessble, the device is
1628 			 * till be displayed with failing condition.
1629 			 * return code should be FPCFGA_OK to display it.
1630 			 */
1631 		case CFGA_STAT_NONE:
1632 			/*
1633 			 * If not unconfigured and not attached
1634 			 * the state is set to CFGA_STAT_NONE currently.
1635 			 * This is okay for the detached node due to
1636 			 * the driver being unloaded.
1637 			 * May need to define another state to
1638 			 * isolate the detached only state.
1639 			 *
1640 			 * handle the same way as configured.
1641 			 */
1642 			if (lap->ret != FPCFGA_ACCESS_OK) {
1643 				cond = CFGA_COND_FAILING;
1644 			}
1645 			lap->chld_config = CFGA_STAT_CONFIGURED;
1646 			break;
1647 		case CFGA_STAT_UNCONFIGURED:
1648 			/*
1649 			 * if unconfigured - offline or down,
1650 			 * set to cond to unusable regardless of accessibility.
1651 			 * This behavior needs to be examined further.
1652 			 * When the device is not accessible the node
1653 			 * may get offline or down. In that case failing
1654 			 * cond may make more sense.
1655 			 * In anycase the ostate will be set to configured
1656 			 * configured.
1657 			 */
1658 			cond = CFGA_COND_UNUSABLE;
1659 			/*
1660 			 * For fabric port the fca port is considered as
1661 			 * configured since user configured previously
1662 			 * for any existing node.  Otherwise when the
1663 			 * device was accessible, the hba is considered as
1664 			 * configured.
1665 			 */
1666 			if (((strcmp(lap->xport_type,
1667 				FP_FC_PUBLIC_PORT_TYPE) == 0) ||
1668 				(strcmp(lap->xport_type,
1669 				FP_FC_FABRIC_PORT_TYPE) == 0)) ||
1670 				(lap->ret == FPCFGA_ACCESS_OK)) {
1671 				lap->chld_config = CFGA_STAT_CONFIGURED;
1672 			} else {
1673 				lap->ret = FPCFGA_APID_NOEXIST;
1674 				return (FPCFGA_OK);
1675 			}
1676 			break;
1677 		default:
1678 			break;
1679 		}
1680 
1681 		/* if device found in disco ports, ldata already created. */
1682 		if (lap->ret == FPCFGA_ACCESS_OK) {
1683 			/*
1684 			 * if cond is not changed then don't update
1685 			 * condition to keep the previous condition.
1686 			 */
1687 			if (cond != CFGA_COND_UNKNOWN) {
1688 				lap->listp->ldata.ap_cond = cond;
1689 			}
1690 			lap->listp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
1691 			lap->listp->ldata.ap_busy = busy;
1692 			lap->ret = FPCFGA_OK;
1693 			return (FPCFGA_OK);
1694 		}
1695 	}
1696 
1697 	/*
1698 	 * if cmd is stat all check ldata list
1699 	 * to see if the node exist on the dev list.  Otherwise create
1700 	 * the list element.
1701 	 */
1702 	if (lap->cmd == FPCFGA_STAT_ALL) {
1703 		if (lap->listp != NULL) {
1704 			if ((ret = make_dyncomp_from_dinode(node,
1705 					&dyncomp, &l_errno)) != FPCFGA_OK) {
1706 				return (ret);
1707 			}
1708 			ret = is_dyn_ap_on_ldata_list(dyncomp, lap->listp,
1709 					&matchldp, &l_errno);
1710 			switch (ret) {
1711 			case FPCFGA_ACCESS_OK:
1712 				/* node exists so set ostate to configured. */
1713 				lap->chld_config = CFGA_STAT_CONFIGURED;
1714 				matchldp->ldata.ap_o_state =
1715 					CFGA_STAT_CONFIGURED;
1716 				matchldp->ldata.ap_busy = busy;
1717 				clp = &matchldp->ldata;
1718 				switch (ostate) {
1719 				case CFGA_STAT_CONFIGURED:
1720 				/*
1721 				 * If not unconfigured and not attached
1722 				 * the state is set to CFGA_STAT_NONE currently.
1723 				 * This is okay for the detached node due to
1724 				 * the driver being unloaded.
1725 				 * May need to define another state to
1726 				 * isolate the detached only state.
1727 				 */
1728 				case CFGA_STAT_NONE:
1729 					/* update ap_type and ap_info */
1730 					get_hw_info(node, clp);
1731 					break;
1732 				/*
1733 				 * node is offline or down.
1734 				 * set cond to unusable.
1735 				 */
1736 				case CFGA_STAT_UNCONFIGURED:
1737 					/*
1738 					 * if cond is not unknown
1739 					 * we already set the cond from
1740 					 * a different node with the same
1741 					 * port WWN or initial probing
1742 					 * was failed so don't update again.
1743 					 */
1744 					if (matchldp->ldata.ap_cond ==
1745 						CFGA_COND_UNKNOWN) {
1746 						matchldp->ldata.ap_cond =
1747 						CFGA_COND_UNUSABLE;
1748 					}
1749 					break;
1750 				default:
1751 					break;
1752 				}
1753 				/* node found in ldata list so just return. */
1754 				lap->ret = FPCFGA_OK;
1755 				S_FREE(dyncomp);
1756 				return (FPCFGA_OK);
1757 			case FPCFGA_LIB_ERR:
1758 				lap->l_errno = l_errno;
1759 				S_FREE(dyncomp);
1760 				return (ret);
1761 			case FPCFGA_APID_NOACCESS:
1762 				switch (ostate) {
1763 				/* node is attached but not in dev list */
1764 				case CFGA_STAT_CONFIGURED:
1765 				case CFGA_STAT_NONE:
1766 					lap->chld_config = CFGA_STAT_CONFIGURED;
1767 					cond = CFGA_COND_FAILING;
1768 					break;
1769 				/*
1770 				 * node is offline or down.
1771 				 * set cond to unusable.
1772 				 */
1773 				case CFGA_STAT_UNCONFIGURED:
1774 					/*
1775 					 * For fabric port the fca port is
1776 					 * considered as configured since user
1777 					 * configured previously for any
1778 					 * existing node.
1779 					 */
1780 					cond = CFGA_COND_UNUSABLE;
1781 					if ((strcmp(lap->xport_type,
1782 						FP_FC_PUBLIC_PORT_TYPE) == 0) ||
1783 						(strcmp(lap->xport_type,
1784 						FP_FC_FABRIC_PORT_TYPE) == 0)) {
1785 						lap->chld_config =
1786 						CFGA_STAT_CONFIGURED;
1787 					} else {
1788 						lap->ret = FPCFGA_OK;
1789 						S_FREE(dyncomp);
1790 						return (FPCFGA_OK);
1791 					}
1792 					break;
1793 				default:
1794 				/*
1795 				 * continue to create ldata_list struct for
1796 				 * this node
1797 				 */
1798 					break;
1799 				}
1800 			default:
1801 				break;
1802 			}
1803 		} else {
1804 			/*
1805 			 * dev_list is null so there is no accessible dev.
1806 			 * set the cond and continue to create ldata.
1807 			 */
1808 			switch (ostate) {
1809 			case CFGA_STAT_CONFIGURED:
1810 			case CFGA_STAT_NONE:
1811 				cond = CFGA_COND_FAILING;
1812 				lap->chld_config = CFGA_STAT_CONFIGURED;
1813 				break;
1814 			/*
1815 			 * node is offline or down.
1816 			 * set cond to unusable.
1817 			 */
1818 			case CFGA_STAT_UNCONFIGURED:
1819 				cond = CFGA_COND_UNUSABLE;
1820 				/*
1821 				 * For fabric port the fca port is
1822 				 * considered as configured since user
1823 				 * configured previously for any
1824 				 * existing node.
1825 				 */
1826 				if ((strcmp(lap->xport_type,
1827 					FP_FC_PUBLIC_PORT_TYPE) == 0) ||
1828 					(strcmp(lap->xport_type,
1829 					FP_FC_FABRIC_PORT_TYPE) == 0)) {
1830 					lap->chld_config =
1831 					CFGA_STAT_CONFIGURED;
1832 				} else {
1833 					lap->ret = FPCFGA_OK;
1834 					S_FREE(dyncomp);
1835 					return (FPCFGA_OK);
1836 				}
1837 				break;
1838 			default:
1839 				break;
1840 			}
1841 		}
1842 	}
1843 
1844 	listp = calloc(1, sizeof (ldata_list_t));
1845 	if (listp == NULL) {
1846 		lap->l_errno = errno;
1847 		S_FREE(dyncomp);
1848 		return (FPCFGA_LIB_ERR);
1849 	}
1850 
1851 	clp = &listp->ldata;
1852 
1853 	/* Create the dynamic component. */
1854 	if (dyncomp == NULL) {
1855 		ret = make_dyncomp_from_dinode(node, &dyncomp, &l_errno);
1856 		if (ret != FPCFGA_OK) {
1857 			S_FREE(listp);
1858 			return (ret);
1859 		}
1860 	}
1861 
1862 	/* Create logical and physical ap_id */
1863 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
1864 	    lap->xport_logp, DYN_SEP, dyncomp);
1865 
1866 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
1867 	    lap->apidp->xport_phys, DYN_SEP, dyncomp);
1868 
1869 	S_FREE(dyncomp);
1870 
1871 	clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
1872 	clp->ap_r_state = lap->xport_rstate;
1873 	/* set to ostate to configured and set cond with info. */
1874 	clp->ap_o_state = CFGA_STAT_CONFIGURED;
1875 	clp->ap_cond = cond;
1876 	clp->ap_busy = busy;
1877 	clp->ap_status_time = (time_t)-1;
1878 
1879 	/* get ap_type and ap_info. */
1880 	get_hw_info(node, clp);
1881 
1882 	/* Link it in */
1883 	listp->next = lap->listp;
1884 	lap->listp = listp;
1885 
1886 	lap->ret = FPCFGA_OK;
1887 	return (FPCFGA_OK);
1888 }
1889 
1890 /*
1891  * Wrapper routine for handling path info.
1892  *
1893  * When show_FCP_dev option is given stat_path_info_FCP_dev() is called.
1894  * Otherwise stat_path_info_fc_dev() is called.
1895  */
1896 int
stat_path_info_node(di_node_t root,void * arg,int * l_errnop)1897 stat_path_info_node(
1898 	di_node_t 	root,
1899 	void 		*arg,
1900 	int 		*l_errnop)
1901 {
1902 	fpcfga_list_t *lap = NULL;
1903 
1904 	lap = (fpcfga_list_t *)arg;
1905 	if ((lap->apidp->flags & (FLAG_FCP_DEV)) == FLAG_FCP_DEV) {
1906 		return (stat_path_info_FCP_dev(root, lap, l_errnop));
1907 	} else {
1908 		return (stat_path_info_fc_dev(root, lap, l_errnop));
1909 	}
1910 }
1911 
1912 /*
1913  * Routine for updating ldata list based on the state of path info node.
1914  * When no matching accessible ldata is found a new ldata is created
1915  * with proper state information.
1916  *
1917  * Overall algorithm:
1918  * If the path info node is not offline and the matching ldata is found
1919  * the target device is updated with configued and unknown condition.
1920  * If the path info node is offline or failed and the matching ldata is found
1921  * the target device is updated with configued and unusable condition.
1922  * If the path info node is online but the matching ldata is not found
1923  * the target device is created with configued and failing condition.
1924  * If the path info is offline or failed and the matching ldata is not found
1925  * the target device is created with configued and unusable condition.
1926  */
1927 static int
stat_path_info_fc_dev(di_node_t root,fpcfga_list_t * lap,int * l_errnop)1928 stat_path_info_fc_dev(
1929 	di_node_t 	root,
1930 	fpcfga_list_t	*lap,
1931 	int 		*l_errnop)
1932 {
1933 	ldata_list_t *matchldp = NULL;
1934 	di_path_t path = DI_PATH_NIL;
1935 	uchar_t		*port_wwn_data;
1936 	char		port_wwn[WWN_SIZE*2+1];
1937 	int		count;
1938 	fpcfga_ret_t 	ret;
1939 	di_path_state_t	pstate;
1940 
1941 	if (root == DI_NODE_NIL) {
1942 		return (FPCFGA_LIB_ERR);
1943 	}
1944 
1945 	/*
1946 	 * if stat on a specific dev and walk_node found it okay
1947 	 * then just return ok.
1948 	 */
1949 	if ((lap->cmd == FPCFGA_STAT_FC_DEV) && (lap->ret == FPCFGA_OK)) {
1950 		return (FPCFGA_OK);
1951 	}
1952 
1953 	/*
1954 	 * if stat on a fca xport and chld_config is set
1955 	 * then just return ok.
1956 	 */
1957 	if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
1958 				(lap->chld_config == CFGA_STAT_CONFIGURED)) {
1959 		return (FPCFGA_OK);
1960 	}
1961 
1962 	/*
1963 	 * when there is no path_info node return FPCFGA_OK.
1964 	 * That way the result from walk_node shall be maintained.
1965 	 */
1966 	if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
1967 		/*
1968 		 * if the dev was in dev list but not found
1969 		 * return OK to indicate is not configured.
1970 		 */
1971 		if (lap->ret == FPCFGA_ACCESS_OK) {
1972 			lap->ret = FPCFGA_OK;
1973 		}
1974 		return (FPCFGA_OK);
1975 	}
1976 
1977 	/* if stat on fca port return. */
1978 	if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
1979 		if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
1980 			strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
1981 			lap->chld_config = CFGA_STAT_CONFIGURED;
1982 			return (FPCFGA_OK);
1983 		} else {
1984 			if ((pstate = di_path_state(path)) !=
1985 				DI_PATH_STATE_OFFLINE) {
1986 				lap->chld_config = CFGA_STAT_CONFIGURED;
1987 				return (FPCFGA_OK);
1988 			}
1989 		}
1990 	}
1991 	/*
1992 	 * now parse the path info node.
1993 	 */
1994 	do {
1995 		count = di_path_prop_lookup_bytes(path, PORT_WWN_PROP,
1996 			&port_wwn_data);
1997 		if (count != WWN_SIZE) {
1998 			ret = FPCFGA_LIB_ERR;
1999 			break;
2000 		}
2001 
2002 		(void) sprintf(port_wwn, "%016llx",
2003 			(wwnConversion(port_wwn_data)));
2004 		switch (lap->cmd) {
2005 		case FPCFGA_STAT_FC_DEV:
2006 			/* if no match contine to the next path info node. */
2007 			if (strncmp(port_wwn, lap->apidp->dyncomp,
2008 					WWN_SIZE*2)) {
2009 				break;
2010 			}
2011 			/* if device in dev_list, ldata already created. */
2012 			if (lap->ret == FPCFGA_ACCESS_OK) {
2013 				lap->listp->ldata.ap_o_state =
2014 					CFGA_STAT_CONFIGURED;
2015 				if (((pstate = di_path_state(path)) ==
2016 					DI_PATH_STATE_OFFLINE) ||
2017 					(pstate == DI_PATH_STATE_FAULT)) {
2018 					lap->listp->ldata.ap_cond =
2019 							CFGA_COND_UNUSABLE;
2020 				}
2021 				lap->ret = FPCFGA_OK;
2022 				return (FPCFGA_OK);
2023 			} else {
2024 				if ((strcmp(lap->xport_type,
2025 					FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2026 					(strcmp(lap->xport_type,
2027 					FP_FC_FABRIC_PORT_TYPE) == 0)) {
2028 					lap->chld_config = CFGA_STAT_CONFIGURED;
2029 					return (init_ldata_for_mpath_dev(
2030 						path, port_wwn, l_errnop, lap));
2031 				} else {
2032 					if ((di_path_state(path)) !=
2033 						DI_PATH_STATE_OFFLINE) {
2034 					    return (init_ldata_for_mpath_dev(
2035 						path, port_wwn, l_errnop, lap));
2036 					} else {
2037 					    lap->ret = FPCFGA_APID_NOEXIST;
2038 					    return (FPCFGA_OK);
2039 					}
2040 				}
2041 			}
2042 		case FPCFGA_STAT_ALL:
2043 			/* check if there is list data. */
2044 			if (lap->listp != NULL) {
2045 				ret = is_dyn_ap_on_ldata_list(port_wwn,
2046 					lap->listp, &matchldp, l_errnop);
2047 				if (ret == FPCFGA_ACCESS_OK) {
2048 					lap->chld_config = CFGA_STAT_CONFIGURED;
2049 					matchldp->ldata.ap_o_state =
2050 							CFGA_STAT_CONFIGURED;
2051 					/*
2052 					 * Update the condition as unusable
2053 					 * if the pathinfo state is failed
2054 					 * or offline.
2055 					 */
2056 					if (((pstate = di_path_state(path)) ==
2057 						DI_PATH_STATE_OFFLINE) ||
2058 						(pstate ==
2059 							DI_PATH_STATE_FAULT)) {
2060 						matchldp->ldata.ap_cond =
2061 							CFGA_COND_UNUSABLE;
2062 					}
2063 					break;
2064 				} else if (ret == FPCFGA_LIB_ERR) {
2065 					lap->l_errno = *l_errnop;
2066 					return (ret);
2067 				}
2068 			}
2069 			/*
2070 			 * now create ldata for this particular path info node.
2071 			 * if port top is private loop and pathinfo is in
2072 			 * in offline state don't include to ldata list.
2073 			 */
2074 			if (((strcmp(lap->xport_type,
2075 				FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2076 				(strcmp(lap->xport_type,
2077 					FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2078 				(di_path_state(path) !=
2079 					DI_PATH_STATE_OFFLINE)) {
2080 				lap->chld_config = CFGA_STAT_CONFIGURED;
2081 				ret = init_ldata_for_mpath_dev(
2082 					path, port_wwn, l_errnop, lap);
2083 				if (ret != FPCFGA_OK) {
2084 					return (ret);
2085 				}
2086 			}
2087 			break;
2088 		case FPCFGA_STAT_FCA_PORT:
2089 			if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
2090 				lap->chld_config = CFGA_STAT_CONFIGURED;
2091 				return (FPCFGA_OK);
2092 			}
2093 		}
2094 		path = di_path_next_client(root, path);
2095 	} while (path != DI_PATH_NIL);
2096 
2097 	return (FPCFGA_OK);
2098 
2099 }
2100 
2101 /*
2102  * Routine for updating ldata list based on the state of path info node.
2103  * When no matching accessible ldata is found a new ldata is created
2104  * with proper state information.
2105  *
2106  * The difference from stat_path_info_fc_dev() is
2107  * to handle FCP SCSI LUN information. Otherwise overall algorithm is
2108  * same.
2109  *
2110  * Overall algorithm:
2111  * If the path info node is not offline and the matching ldata is found
2112  * the target device is updated with configued and unknown condition.
2113  * If the path info node is offline or failed and the matching ldata is found
2114  * the target device is updated with configued and unusable condition.
2115  * If the path info node is online but the matching ldata is not found
2116  * the target device is created with configued and failing condition.
2117  * If the path info is offline or failed and the matching ldata is not found
2118  * the target device is created with configued and unusable condition.
2119  */
2120 static int
stat_path_info_FCP_dev(di_node_t root,fpcfga_list_t * lap,int * l_errnop)2121 stat_path_info_FCP_dev(
2122 	di_node_t 	root,
2123 	fpcfga_list_t	*lap,
2124 	int 		*l_errnop)
2125 {
2126 	ldata_list_t	*matchldp = NULL, *listp = NULL;
2127 	cfga_list_data_t	*clp;
2128 	di_path_t	path = DI_PATH_NIL;
2129 	di_node_t	client_node = DI_NODE_NIL;
2130 	char		*port_wwn = NULL, *nodepath = NULL;
2131 	int		*lun_nump;
2132 	fpcfga_ret_t 	ldata_ret;
2133 	di_path_state_t	pstate;
2134 	cfga_busy_t	busy;
2135 	uint_t		dctl_state = 0;
2136 
2137 	if (root == DI_NODE_NIL) {
2138 		return (FPCFGA_LIB_ERR);
2139 	}
2140 
2141 	/*
2142 	 * if stat on a fca xport and chld_config is set
2143 	 * then just return ok.
2144 	 */
2145 	if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
2146 				(lap->chld_config == CFGA_STAT_CONFIGURED)) {
2147 		return (FPCFGA_OK);
2148 	}
2149 	/*
2150 	 * when there is no path_info node return FPCFGA_OK.
2151 	 * That way the result from walk_node shall be maintained.
2152 	 */
2153 	if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
2154 		/*
2155 		 * if the dev was in dev list but not found
2156 		 * return ok.
2157 		 */
2158 		if (lap->ret == FPCFGA_ACCESS_OK) {
2159 			lap->ret = FPCFGA_OK;
2160 		}
2161 		return (FPCFGA_OK);
2162 	}
2163 	/*
2164 	 * If stat on fca port and port topology is fabric return here.
2165 	 * If not fabric return only when path state is not offfline.
2166 	 * The other cases are handbled below.
2167 	 */
2168 	if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
2169 		if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
2170 			strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
2171 			lap->chld_config = CFGA_STAT_CONFIGURED;
2172 			return (FPCFGA_OK);
2173 		} else {
2174 			if ((pstate = di_path_state(path)) !=
2175 				DI_PATH_STATE_OFFLINE) {
2176 				lap->chld_config = CFGA_STAT_CONFIGURED;
2177 				return (FPCFGA_OK);
2178 			}
2179 		}
2180 	}
2181 	/*
2182 	 * now parse the path info node.
2183 	 */
2184 	do {
2185 		switch (lap->cmd) {
2186 		case FPCFGA_STAT_FC_DEV:
2187 			if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
2188 				&lun_nump, l_errnop)) != FPCFGA_OK) {
2189 				return (FPCFGA_LIB_ERR);
2190 			}
2191 
2192 			if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
2193 				*lun_nump, lap->listp, &matchldp))
2194 				== FPCFGA_LIB_ERR) {
2195 				S_FREE(port_wwn);
2196 				return (ldata_ret);
2197 			}
2198 
2199 			if (ldata_ret == FPCFGA_ACCESS_OK) {
2200 				lap->chld_config = CFGA_STAT_CONFIGURED;
2201 				matchldp->ldata.ap_o_state =
2202 						CFGA_STAT_CONFIGURED;
2203 				/*
2204 				 * Update the condition as unusable
2205 				 * if the pathinfo state is failed
2206 				 * or offline.
2207 				 */
2208 				if (((pstate = di_path_state(path)) ==
2209 					DI_PATH_STATE_OFFLINE) ||
2210 					(pstate == DI_PATH_STATE_FAULT)) {
2211 					matchldp->ldata.ap_cond =
2212 							CFGA_COND_UNUSABLE;
2213 				}
2214 				lap->ret = FPCFGA_OK;
2215 				break;
2216 			}
2217 
2218 			if (strncmp(port_wwn, lap->apidp->dyncomp, WWN_SIZE*2)
2219 					!= 0) {
2220 				break;
2221 			}
2222 			/*
2223 			 * now create ldata for this particular path info node.
2224 			 * if port top is private loop and pathinfo is in
2225 			 * in offline state don't include to ldata list.
2226 			 */
2227 			if (((strcmp(lap->xport_type,
2228 				FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2229 				(strcmp(lap->xport_type,
2230 					FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2231 				(di_path_state(path) !=
2232 					DI_PATH_STATE_OFFLINE)) {
2233 			    lap->chld_config = CFGA_STAT_CONFIGURED;
2234 				/* create ldata for this pi node. */
2235 			    client_node = di_path_client_node(path);
2236 			    if (client_node == DI_NODE_NIL) {
2237 				*l_errnop = errno;
2238 				S_FREE(port_wwn);
2239 				return (FPCFGA_LIB_ERR);
2240 			    }
2241 			    if ((construct_nodepath_from_dinode(
2242 				client_node, &nodepath, l_errnop))
2243 					!= FPCFGA_OK) {
2244 				S_FREE(port_wwn);
2245 				return (FPCFGA_LIB_ERR);
2246 			    }
2247 
2248 			    listp = calloc(1, sizeof (ldata_list_t));
2249 			    if (listp == NULL) {
2250 				S_FREE(port_wwn);
2251 				S_FREE(nodepath);
2252 				lap->l_errno = errno;
2253 				return (FPCFGA_LIB_ERR);
2254 			    }
2255 
2256 			    clp = &listp->ldata;
2257 
2258 			    /* Create logical and physical ap_id */
2259 			    (void) snprintf(clp->ap_log_id,
2260 				sizeof (clp->ap_log_id), "%s%s%s%s%d",
2261 				lap->xport_logp, DYN_SEP, port_wwn,
2262 				LUN_COMP_SEP, *lun_nump);
2263 			    (void) snprintf(clp->ap_phys_id,
2264 				sizeof (clp->ap_phys_id), "%s%s%s%s%d",
2265 				lap->apidp->xport_phys, DYN_SEP, port_wwn,
2266 				LUN_COMP_SEP, *lun_nump);
2267 				/*
2268 				 * We reached here since FCP dev is not found
2269 				 * in ldata list but path info node exists.
2270 				 *
2271 				 * Update the condition as failing
2272 				 * if the pathinfo state was normal.
2273 				 * Update the condition as unusable
2274 				 * if the pathinfo state is failed
2275 				 * or offline.
2276 				 */
2277 			    clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
2278 			    clp->ap_o_state = CFGA_STAT_CONFIGURED;
2279 			    if (((pstate = di_path_state(path))
2280 					== DI_PATH_STATE_OFFLINE) ||
2281 				(pstate == DI_PATH_STATE_FAULT)) {
2282 				clp->ap_cond = CFGA_COND_UNUSABLE;
2283 			    } else {
2284 				clp->ap_cond = CFGA_COND_FAILING;
2285 			    }
2286 			    clp->ap_r_state = lap->xport_rstate;
2287 			    clp->ap_info[0] = '\0';
2288 				/* update ap_type and ap_info */
2289 			    get_hw_info(client_node, clp);
2290 			    if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
2291 				&dctl_state, l_errnop) == FPCFGA_OK) {
2292 				busy = ((dctl_state & DEVICE_BUSY)
2293 					== DEVICE_BUSY) ? 1 : 0;
2294 			    } else {
2295 				busy = 0;
2296 			    }
2297 			    clp->ap_busy = busy;
2298 			    clp->ap_status_time = (time_t)-1;
2299 
2300 			    (void) insert_ldata_to_ldatalist(port_wwn,
2301 				lun_nump, listp, &(lap->listp));
2302 			}
2303 			break;
2304 		case FPCFGA_STAT_ALL:
2305 			if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
2306 				&lun_nump, l_errnop)) != FPCFGA_OK) {
2307 				return (FPCFGA_LIB_ERR);
2308 			}
2309 
2310 			if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
2311 				*lun_nump, lap->listp, &matchldp))
2312 				== FPCFGA_LIB_ERR) {
2313 				S_FREE(port_wwn);
2314 				return (ldata_ret);
2315 			}
2316 
2317 			if (ldata_ret == FPCFGA_ACCESS_OK) {
2318 				lap->chld_config = CFGA_STAT_CONFIGURED;
2319 				matchldp->ldata.ap_o_state =
2320 						CFGA_STAT_CONFIGURED;
2321 				/*
2322 				 * Update the condition as unusable
2323 				 * if the pathinfo state is failed
2324 				 * or offline.
2325 				 */
2326 				if (((pstate = di_path_state(path)) ==
2327 					DI_PATH_STATE_OFFLINE) ||
2328 					(pstate == DI_PATH_STATE_FAULT)) {
2329 					matchldp->ldata.ap_cond =
2330 							CFGA_COND_UNUSABLE;
2331 				}
2332 				break;
2333 			}
2334 			/*
2335 			 * now create ldata for this particular path info node.
2336 			 * if port top is private loop and pathinfo is in
2337 			 * in offline state don't include to ldata list.
2338 			 */
2339 			if (((strcmp(lap->xport_type,
2340 				FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2341 				(strcmp(lap->xport_type,
2342 					FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2343 				(di_path_state(path) !=
2344 					DI_PATH_STATE_OFFLINE)) {
2345 			    lap->chld_config = CFGA_STAT_CONFIGURED;
2346 				/* create ldata for this pi node. */
2347 			    client_node = di_path_client_node(path);
2348 			    if (client_node == DI_NODE_NIL) {
2349 				*l_errnop = errno;
2350 				S_FREE(port_wwn);
2351 				return (FPCFGA_LIB_ERR);
2352 			    }
2353 			    if ((construct_nodepath_from_dinode(
2354 				client_node, &nodepath, l_errnop))
2355 					!= FPCFGA_OK) {
2356 				S_FREE(port_wwn);
2357 				return (FPCFGA_LIB_ERR);
2358 			    }
2359 
2360 			    listp = calloc(1, sizeof (ldata_list_t));
2361 			    if (listp == NULL) {
2362 				S_FREE(port_wwn);
2363 				S_FREE(nodepath);
2364 				lap->l_errno = errno;
2365 				return (FPCFGA_LIB_ERR);
2366 			    }
2367 
2368 			    clp = &listp->ldata;
2369 
2370 			    /* Create logical and physical ap_id */
2371 			    (void) snprintf(clp->ap_log_id,
2372 				sizeof (clp->ap_log_id), "%s%s%s%s%d",
2373 				lap->xport_logp, DYN_SEP, port_wwn,
2374 				LUN_COMP_SEP, *lun_nump);
2375 			    (void) snprintf(clp->ap_phys_id,
2376 				sizeof (clp->ap_phys_id), "%s%s%s%s%d",
2377 				lap->apidp->xport_phys, DYN_SEP, port_wwn,
2378 				LUN_COMP_SEP, *lun_nump);
2379 				/*
2380 				 * We reached here since FCP dev is not found
2381 				 * in ldata list but path info node exists.
2382 				 *
2383 				 * Update the condition as failing
2384 				 * if the pathinfo state was normal.
2385 				 * Update the condition as unusable
2386 				 * if the pathinfo state is failed
2387 				 * or offline.
2388 				 */
2389 			    clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
2390 			    clp->ap_o_state = CFGA_STAT_CONFIGURED;
2391 			    if (((pstate = di_path_state(path))
2392 					== DI_PATH_STATE_OFFLINE) ||
2393 				(pstate == DI_PATH_STATE_FAULT)) {
2394 				clp->ap_cond = CFGA_COND_UNUSABLE;
2395 			    } else {
2396 				clp->ap_cond = CFGA_COND_FAILING;
2397 			    }
2398 			    clp->ap_r_state = lap->xport_rstate;
2399 			    clp->ap_info[0] = '\0';
2400 				/* update ap_type and ap_info */
2401 			    get_hw_info(client_node, clp);
2402 			    if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
2403 				&dctl_state, l_errnop) == FPCFGA_OK) {
2404 				busy = ((dctl_state & DEVICE_BUSY)
2405 					== DEVICE_BUSY) ? 1 : 0;
2406 			    } else {
2407 				busy = 0;
2408 			    }
2409 			    clp->ap_busy = busy;
2410 			    clp->ap_status_time = (time_t)-1;
2411 
2412 			    (void) insert_ldata_to_ldatalist(port_wwn,
2413 				lun_nump, listp, &(lap->listp));
2414 			}
2415 			break;
2416 		case FPCFGA_STAT_FCA_PORT:
2417 			if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
2418 				lap->chld_config = CFGA_STAT_CONFIGURED;
2419 				lap->ret = FPCFGA_OK;
2420 				return (FPCFGA_OK);
2421 			}
2422 		}
2423 		path = di_path_next_client(root, path);
2424 	} while (path != DI_PATH_NIL);
2425 
2426 	lap->ret = FPCFGA_OK;
2427 	S_FREE(port_wwn);
2428 	S_FREE(nodepath);
2429 	return (FPCFGA_OK);
2430 
2431 }
2432 
2433 /*
2434  * Routine for updating ldata list based on the state of device node.
2435  * When no matching accessible ldata is found a new ldata is created
2436  * with proper state information.
2437  *
2438  * The difference from do_stat_fc_dev() is
2439  * to handle FCP SCSI LUN information. Otherwise overall algorithm is
2440  * same.
2441  *
2442  * Overall algorithm:
2443  * If the device node is online and the matching ldata is found
2444  * the target device is updated with configued and unknown condition.
2445  * If the device node is offline or down and the matching ldata is found
2446  * the target device is updated with configued and unusable condition.
2447  * If the device node is online but the matching ldata is not found
2448  * the target device is created with configued and failing condition.
2449  * If the device node is offline or down and the matching ldata is not found
2450  * the target device is created with configued and unusable condition.
2451  */
2452 static fpcfga_ret_t
do_stat_FCP_dev(const di_node_t node,const char * nodepath,fpcfga_list_t * lap,int limited_stat)2453 do_stat_FCP_dev(
2454 	const di_node_t node,
2455 	const char *nodepath,
2456 	fpcfga_list_t *lap,
2457 	int limited_stat)
2458 {
2459 	uint_t dctl_state = 0, devinfo_state = 0;
2460 	char *port_wwn = NULL;
2461 	cfga_list_data_t *clp = NULL;
2462 	cfga_busy_t busy;
2463 	ldata_list_t *listp = NULL;
2464 	ldata_list_t *matchldp = NULL;
2465 	int l_errno = 0, *lun_nump;
2466 	cfga_stat_t ostate;
2467 	cfga_cond_t cond;
2468 	fpcfga_ret_t ldata_ret;
2469 
2470 	assert(lap->apidp->xport_phys != NULL);
2471 	assert(lap->xport_logp != NULL);
2472 
2473 	cond = CFGA_COND_UNKNOWN;
2474 
2475 	devinfo_state = di_state(node);
2476 	ostate = dev_devinfo_to_occupant_state(devinfo_state);
2477 
2478 	/*
2479 	 * NOTE: The devctl framework cannot currently detect layered driver
2480 	 * opens, so the busy indicator is not very reliable. Also,
2481 	 * non-root users will not be able to determine busy
2482 	 * status (libdevice needs root permissions).
2483 	 * This should probably be fixed by adding a DI_BUSY to the di_state()
2484 	 * routine in libdevinfo.
2485 	 */
2486 	if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
2487 	    &l_errno) == FPCFGA_OK) {
2488 		busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
2489 	} else {
2490 		busy = 0;
2491 	}
2492 
2493 	/* We only want to know device config state */
2494 	if (limited_stat) {
2495 		if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
2496 			strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
2497 			lap->chld_config = CFGA_STAT_CONFIGURED;
2498 		} else {
2499 			if (ostate != CFGA_STAT_UNCONFIGURED) {
2500 				lap->chld_config = CFGA_STAT_CONFIGURED;
2501 			}
2502 		}
2503 		return (FPCFGA_OK);
2504 	}
2505 
2506 	/*
2507 	 * If child device is configured, see if it is accessible also
2508 	 * for FPCFGA_STAT_FC_DEV cmd.
2509 	 */
2510 	if ((make_portwwn_luncomp_from_dinode(node, &port_wwn, &lun_nump,
2511 			&l_errno)) != FPCFGA_OK) {
2512 		lap->l_errno = l_errno;
2513 		return (FPCFGA_LIB_ERR);
2514 	}
2515 
2516 	if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn, *lun_nump,
2517 			lap->listp, &matchldp)) == FPCFGA_LIB_ERR) {
2518 		lap->l_errno = l_errno;
2519 		S_FREE(port_wwn);
2520 		return (ldata_ret);
2521 	}
2522 
2523 	if (lap->cmd == FPCFGA_STAT_FC_DEV) {
2524 		switch (ostate) {
2525 		case CFGA_STAT_CONFIGURED:
2526 			/*
2527 			 * if configured and not accessble, the device is
2528 			 * till be displayed with failing condition.
2529 			 * return code should be FPCFGA_OK to display it.
2530 			 */
2531 		case CFGA_STAT_NONE:
2532 			/*
2533 			 * If not unconfigured and not attached
2534 			 * the state is set to CFGA_STAT_NONE currently.
2535 			 * This is okay for the detached node due to
2536 			 * the driver being unloaded.
2537 			 * May need to define another state to
2538 			 * isolate the detached only state.
2539 			 *
2540 			 * handle the same way as configured.
2541 			 */
2542 			if (ldata_ret != FPCFGA_ACCESS_OK) {
2543 				cond = CFGA_COND_FAILING;
2544 			}
2545 			lap->chld_config = CFGA_STAT_CONFIGURED;
2546 			break;
2547 		case CFGA_STAT_UNCONFIGURED:
2548 			/*
2549 			 * if unconfigured - offline or down,
2550 			 * set to cond to unusable regardless of accessibility.
2551 			 * This behavior needs to be examined further.
2552 			 * When the device is not accessible the node
2553 			 * may get offline or down. In that case failing
2554 			 * cond may make more sense.
2555 			 * In anycase the ostate will be set to configured
2556 			 * configured.
2557 			 */
2558 			cond = CFGA_COND_UNUSABLE;
2559 			/*
2560 			 * For fabric port the fca port is considered as
2561 			 * configured since user configured previously
2562 			 * for any existing node.  Otherwise when the
2563 			 * device was accessible, the hba is considered as
2564 			 * configured.
2565 			 */
2566 			if (((strcmp(lap->xport_type,
2567 				FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2568 				(strcmp(lap->xport_type,
2569 				FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2570 				(lap->ret == FPCFGA_ACCESS_OK)) {
2571 				lap->chld_config = CFGA_STAT_CONFIGURED;
2572 			} else {
2573 				/*
2574 				 * if lap->ret is okay there is at least
2575 				 * one matching ldata exist.  Need to keep
2576 				 * okay ret to display the matching ones.
2577 				 */
2578 				if (lap->ret != FPCFGA_OK) {
2579 					lap->ret = FPCFGA_APID_NOEXIST;
2580 				}
2581 				S_FREE(port_wwn);
2582 				return (FPCFGA_OK);
2583 			}
2584 			break;
2585 		default:
2586 			break;
2587 		}
2588 
2589 		/* if device found in dev_list, ldata already created. */
2590 		if (ldata_ret == FPCFGA_ACCESS_OK) {
2591 			/*
2592 			 * if cond is not changed then don't update
2593 			 * condition to keep any condtion
2594 			 * from initial discovery. If the initial
2595 			 * cond was failed the same condition will be kept.
2596 			 */
2597 			if (cond != CFGA_COND_UNKNOWN) {
2598 				matchldp->ldata.ap_cond = cond;
2599 			}
2600 			matchldp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
2601 			matchldp->ldata.ap_busy = busy;
2602 			/* update ap_info via inquiry */
2603 			clp = &matchldp->ldata;
2604 			/* update ap_type and ap_info */
2605 			get_hw_info(node, clp);
2606 			lap->ret = FPCFGA_OK;
2607 			S_FREE(port_wwn);
2608 			return (FPCFGA_OK);
2609 		}
2610 	}
2611 
2612 	/*
2613 	 * if cmd is stat all check ldata list
2614 	 * to see if the node exist on the dev list.  Otherwise create
2615 	 * the list element.
2616 	 */
2617 	if (lap->cmd == FPCFGA_STAT_ALL) {
2618 		switch (ldata_ret) {
2619 		case FPCFGA_ACCESS_OK:
2620 			/* node exists so set ostate to configured. */
2621 			lap->chld_config = CFGA_STAT_CONFIGURED;
2622 			matchldp->ldata.ap_o_state =
2623 				CFGA_STAT_CONFIGURED;
2624 			matchldp->ldata.ap_busy = busy;
2625 			clp = &matchldp->ldata;
2626 			switch (ostate) {
2627 			case CFGA_STAT_CONFIGURED:
2628 			/*
2629 			 * If not unconfigured and not attached
2630 			 * the state is set to CFGA_STAT_NONE currently.
2631 			 * This is okay for the detached node due to
2632 			 * the driver being unloaded.
2633 			 * May need to define another state to
2634 			 * isolate the detached only state.
2635 			 */
2636 			case CFGA_STAT_NONE:
2637 				/* update ap_type and ap_info */
2638 				get_hw_info(node, clp);
2639 				break;
2640 			/*
2641 			 * node is offline or down.
2642 			 * set cond to unusable.
2643 			 */
2644 			case CFGA_STAT_UNCONFIGURED:
2645 				/*
2646 				 * if cond is not unknown
2647 				 * initial probing was failed
2648 				 * so don't update again.
2649 				 */
2650 				if (matchldp->ldata.ap_cond ==
2651 					CFGA_COND_UNKNOWN) {
2652 					matchldp->ldata.ap_cond =
2653 					CFGA_COND_UNUSABLE;
2654 				}
2655 				break;
2656 			default:
2657 				break;
2658 			}
2659 			/* node found in ldata list so just return. */
2660 			lap->ret = FPCFGA_OK;
2661 			S_FREE(port_wwn);
2662 			return (FPCFGA_OK);
2663 		case FPCFGA_APID_NOACCESS:
2664 			switch (ostate) {
2665 			/* node is attached but not in dev list */
2666 			case CFGA_STAT_CONFIGURED:
2667 			case CFGA_STAT_NONE:
2668 				lap->chld_config = CFGA_STAT_CONFIGURED;
2669 				cond = CFGA_COND_FAILING;
2670 				break;
2671 			/*
2672 			 * node is offline or down.
2673 			 * set cond to unusable.
2674 			 */
2675 			case CFGA_STAT_UNCONFIGURED:
2676 				/*
2677 				 * For fabric port the fca port is
2678 				 * considered as configured since user
2679 				 * configured previously for any
2680 				 * existing node.
2681 				 */
2682 				cond = CFGA_COND_UNUSABLE;
2683 				if ((strcmp(lap->xport_type,
2684 					FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2685 					(strcmp(lap->xport_type,
2686 					FP_FC_FABRIC_PORT_TYPE) == 0)) {
2687 					lap->chld_config =
2688 					CFGA_STAT_CONFIGURED;
2689 				} else {
2690 					lap->ret = FPCFGA_OK;
2691 					S_FREE(port_wwn);
2692 					return (FPCFGA_OK);
2693 				}
2694 				break;
2695 			default:
2696 			/*
2697 			 * continue to create ldata_list struct for
2698 			 * this node
2699 			 */
2700 				break;
2701 			}
2702 		default:
2703 			break;
2704 		}
2705 	}
2706 
2707 	listp = calloc(1, sizeof (ldata_list_t));
2708 	if (listp == NULL) {
2709 		lap->l_errno = errno;
2710 		S_FREE(port_wwn);
2711 		return (FPCFGA_LIB_ERR);
2712 	}
2713 
2714 	clp = &listp->ldata;
2715 
2716 	/* Create logical and physical ap_id */
2717 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
2718 		"%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
2719 		LUN_COMP_SEP, *lun_nump);
2720 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
2721 		"%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
2722 		LUN_COMP_SEP, *lun_nump);
2723 	clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
2724 	clp->ap_r_state = lap->xport_rstate;
2725 	clp->ap_o_state = CFGA_STAT_CONFIGURED;
2726 	clp->ap_cond = cond;
2727 	clp->ap_busy = busy;
2728 	clp->ap_status_time = (time_t)-1;
2729 	clp->ap_info[0] = '\0';
2730 
2731 	get_hw_info(node, clp);
2732 
2733 	(void) insert_ldata_to_ldatalist(port_wwn, lun_nump, listp,
2734 		&(lap->listp));
2735 
2736 	lap->ret = FPCFGA_OK;
2737 	S_FREE(port_wwn);
2738 	return (FPCFGA_OK);
2739 }
2740 
2741 /*
2742  * Searches the ldata_list to find if the the input port_wwn exist.
2743  *
2744  * Input:  port_wwn, ldata_list.
2745  * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
2746  *		 FPCFGA_ACCESS_OK if found on ldata_list.
2747  */
2748 static fpcfga_ret_t
is_dyn_ap_on_ldata_list(const char * port_wwn,const ldata_list_t * listp,ldata_list_t ** matchldpp,int * l_errnop)2749 is_dyn_ap_on_ldata_list(const char *port_wwn, const ldata_list_t *listp,
2750 			ldata_list_t **matchldpp, int *l_errnop)
2751 {
2752 	char		*dyn = NULL, *dyncomp = NULL;
2753 	int		len;
2754 	ldata_list_t	*tmplp;
2755 	fpcfga_ret_t 	ret;
2756 
2757 
2758 	ret = FPCFGA_APID_NOACCESS;
2759 
2760 	tmplp = (ldata_list_t *)listp;
2761 	while (tmplp != NULL) {
2762 		if ((dyn = GET_DYN(tmplp->ldata.ap_phys_id)) != NULL) {
2763 			len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
2764 			dyncomp = calloc(1, len);
2765 			if (dyncomp == NULL) {
2766 				*l_errnop = errno;
2767 				ret = FPCFGA_LIB_ERR;
2768 				break;
2769 			}
2770 			(void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
2771 			if (!(strncmp(port_wwn, dyncomp, WWN_SIZE*2))) {
2772 				*matchldpp = tmplp;
2773 				S_FREE(dyncomp);
2774 				ret = FPCFGA_ACCESS_OK;
2775 				break;
2776 			}
2777 			S_FREE(dyncomp);
2778 		}
2779 		tmplp = tmplp->next;
2780 	}
2781 
2782 	return (ret);
2783 }
2784 
2785 /*
2786  * Searches the ldata_list to find if the the input port_wwn and lun exist.
2787  *
2788  * Input:  port_wwn, ldata_list.
2789  * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
2790  *		 FPCFGA_ACCESS_OK if found on ldata_list.
2791  */
2792 static fpcfga_ret_t
is_FCP_dev_ap_on_ldata_list(const char * port_wwn,const int lun_num,ldata_list_t * ldatap,ldata_list_t ** matchldpp)2793 is_FCP_dev_ap_on_ldata_list(const char *port_wwn, const int lun_num,
2794 			ldata_list_t *ldatap,
2795 			ldata_list_t **matchldpp)
2796 {
2797 	ldata_list_t *curlp = NULL;
2798 	char *dyn = NULL, *dyncomp = NULL;
2799 	char *lun_dyn = NULL, *lunp = NULL;
2800 	int ldata_lun;
2801 	fpcfga_ret_t ret;
2802 
2803 	/*
2804 	 * if there is no list data just return the FCP dev list.
2805 	 * Normally this should not occur since list data should
2806 	 * be created through discoveredPort list.
2807 	 */
2808 	ret = FPCFGA_APID_NOACCESS;
2809 	if (ldatap == NULL) {
2810 		return (ret);
2811 	}
2812 
2813 	dyn = GET_DYN(ldatap->ldata.ap_phys_id);
2814 	if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2815 	if ((dyncomp != NULL) &&
2816 			(strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
2817 		lun_dyn = GET_LUN_DYN(dyncomp);
2818 		if (lun_dyn != NULL) {
2819 			lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
2820 			if ((ldata_lun = atoi(lunp)) == lun_num) {
2821 				*matchldpp = ldatap;
2822 				return (FPCFGA_ACCESS_OK);
2823 			} else if (ldata_lun > lun_num) {
2824 				return (ret);
2825 			}
2826 			/* else continue */
2827 		} else {
2828 			/* we have match without lun comp. */
2829 			*matchldpp = ldatap;
2830 			return (FPCFGA_ACCESS_OK);
2831 		}
2832 	}
2833 
2834 	curlp = ldatap->next;
2835 
2836 	dyn = dyncomp = NULL;
2837 	lun_dyn = lunp = NULL;
2838 	while (curlp != NULL) {
2839 		dyn = GET_DYN(curlp->ldata.ap_phys_id);
2840 		if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2841 		if ((dyncomp != NULL) &&
2842 				(strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
2843 			lun_dyn = GET_LUN_DYN(dyncomp);
2844 			if (lun_dyn != NULL) {
2845 				lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
2846 				if ((ldata_lun = atoi(lunp)) == lun_num) {
2847 					*matchldpp = curlp;
2848 					return (FPCFGA_ACCESS_OK);
2849 				} else if (ldata_lun > lun_num) {
2850 					return (ret);
2851 				}
2852 				/* else continue */
2853 			} else {
2854 				/* we have match without lun comp. */
2855 				*matchldpp = curlp;
2856 				return (FPCFGA_ACCESS_OK);
2857 			}
2858 		}
2859 		dyn = dyncomp = NULL;
2860 		lun_dyn = lunp = NULL;
2861 		curlp = curlp->next;
2862 	}
2863 
2864 	return (ret);
2865 
2866 }
2867 
2868 /*
2869  * This routine is called when a pathinfo without matching pwwn in dev_list
2870  * is found.
2871  */
2872 static fpcfga_ret_t
init_ldata_for_mpath_dev(di_path_t path,char * pwwn,int * l_errnop,fpcfga_list_t * lap)2873 init_ldata_for_mpath_dev(di_path_t path, char *pwwn, int *l_errnop,
2874 	fpcfga_list_t *lap)
2875 {
2876 	ldata_list_t *listp = NULL;
2877 	cfga_list_data_t *clp = NULL;
2878 	size_t		devlen;
2879 	char		*devpath;
2880 	di_node_t	client_node = DI_NODE_NIL;
2881 	uint_t 		dctl_state = 0;
2882 	cfga_busy_t 	busy;
2883 	char		*client_path;
2884 	di_path_state_t	pstate;
2885 
2886 	/* get the client node path */
2887 	if (path == DI_PATH_NIL) {
2888 		return (FPCFGA_LIB_ERR);
2889 	}
2890 	client_node = di_path_client_node(path);
2891 	if (client_node == DI_NODE_NIL) {
2892 		return (FPCFGA_LIB_ERR);
2893 	}
2894 	if ((client_path = di_devfs_path(client_node)) == NULL) {
2895 		return (FPCFGA_LIB_ERR);
2896 	}
2897 	devlen = strlen(DEVICES_DIR) + strlen(client_path) + 1;
2898 	devpath = calloc(1, devlen);
2899 	if (devpath == NULL) {
2900 		di_devfs_path_free(client_path);
2901 		*l_errnop = errno;
2902 		return (FPCFGA_LIB_ERR);
2903 	}
2904 	(void) snprintf(devpath, devlen, "%s%s", DEVICES_DIR, client_path);
2905 
2906 	/* now need to create ldata for this dev */
2907 	listp = calloc(1, sizeof (ldata_list_t));
2908 	if (listp == NULL) {
2909 		di_devfs_path_free(client_path);
2910 		S_FREE(devpath);
2911 		*l_errnop = errno;
2912 		return (FPCFGA_LIB_ERR);
2913 	}
2914 
2915 	clp = &listp->ldata;
2916 
2917 	/* Create logical and physical ap_id */
2918 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
2919 			lap->xport_logp, DYN_SEP, pwwn);
2920 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
2921 			lap->apidp->xport_phys, DYN_SEP, pwwn);
2922 
2923 	/* Filled in by libcfgadm */
2924 	clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
2925 	clp->ap_r_state = lap->xport_rstate;
2926 	/* set to ostate to configured. */
2927 	clp->ap_o_state = CFGA_STAT_CONFIGURED;
2928 	/*
2929 	 * This routine is called when a port WWN is not found in dev list
2930 	 * but path info node exists.
2931 	 *
2932 	 * Update the condition as failing if the pathinfo state was normal.
2933 	 * Update the condition as unusable if the pathinfo state is failed
2934 	 * or offline.
2935 	 */
2936 	if (((pstate = di_path_state(path)) == DI_PATH_STATE_OFFLINE) ||
2937 			(pstate == DI_PATH_STATE_FAULT)) {
2938 		clp->ap_cond = CFGA_COND_UNUSABLE;
2939 	} else {
2940 		clp->ap_cond = CFGA_COND_FAILING;
2941 	}
2942 	clp->ap_status_time = (time_t)-1;
2943 	/* update ap_type and ap_info */
2944 	get_hw_info(client_node, clp);
2945 
2946 	if (devctl_cmd(devpath, FPCFGA_DEV_GETSTATE,
2947 		&dctl_state, l_errnop) == FPCFGA_OK) {
2948 		busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
2949 	} else {
2950 		busy = 0;
2951 	}
2952 	clp->ap_busy = busy;
2953 	/* Link it in */
2954 	listp->next = lap->listp;
2955 	lap->listp = listp;
2956 
2957 	di_devfs_path_free(client_path);
2958 	S_FREE(devpath);
2959 
2960 	/* now return with ok status with ldata. */
2961 	lap->ret = FPCFGA_OK;
2962 	return (FPCFGA_OK);
2963 }
2964 
2965 /*
2966  * Initialize the cfga_list_data struct for an accessible device
2967  * from g_get_dev_list().
2968  *
2969  * Input:  fca port ldata.
2970  * Output: device cfga_list_data.
2971  *
2972  */
2973 static fpcfga_ret_t
init_ldata_for_accessible_dev(const char * dyncomp,uchar_t inq_type,fpcfga_list_t * lap)2974 init_ldata_for_accessible_dev(const char *dyncomp, uchar_t inq_type,
2975 							fpcfga_list_t *lap)
2976 {
2977 	ldata_list_t *listp = NULL;
2978 	cfga_list_data_t *clp = NULL;
2979 	int i;
2980 
2981 	listp = calloc(1, sizeof (ldata_list_t));
2982 	if (listp == NULL) {
2983 		lap->l_errno = errno;
2984 		return (FPCFGA_LIB_ERR);
2985 	}
2986 
2987 	clp = &listp->ldata;
2988 
2989 	assert(dyncomp != NULL);
2990 
2991 	/* Create logical and physical ap_id */
2992 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
2993 		lap->xport_logp, DYN_SEP, dyncomp);
2994 
2995 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
2996 		lap->apidp->xport_phys, DYN_SEP, dyncomp);
2997 
2998 	clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
2999 	clp->ap_r_state = lap->xport_rstate;
3000 	clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
3001 	clp->ap_cond = CFGA_COND_UNKNOWN;
3002 	clp->ap_busy = 0;
3003 	clp->ap_status_time = (time_t)-1;
3004 	clp->ap_info[0] = '\0';
3005 	for (i = 0; i < N_DEVICE_TYPES; i++) {
3006 		if (inq_type == device_list[i].itype) {
3007 			(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
3008 			"%s", (char *)device_list[i].name);
3009 		break;
3010 		}
3011 	}
3012 	if (i == N_DEVICE_TYPES) {
3013 		if (inq_type == ERR_INQ_DTYPE) {
3014 			clp->ap_cond = CFGA_COND_FAILED;
3015 			snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
3016 			    (char *)GET_MSG_STR(ERR_UNAVAILABLE));
3017 		} else {
3018 			(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
3019 				"%s", "unknown");
3020 		}
3021 	}
3022 
3023 	/* Link it in */
3024 	(void) insert_ldata_to_ldatalist(dyncomp, NULL, listp, &(lap->listp));
3025 
3026 	return (FPCFGA_OK);
3027 }
3028 
3029 /*
3030  * Initialize the cfga_list_data struct for an accessible FCP SCSI LUN device
3031  * from the report lun data.
3032  *
3033  * Input:  fca port ldata. report lun info
3034  * Output: device cfga_list_data.
3035  *
3036  */
3037 static fpcfga_ret_t
init_ldata_for_accessible_FCP_dev(const char * port_wwn,int num_luns,struct report_lun_resp * resp_buf,fpcfga_list_t * lap,int * l_errnop)3038 init_ldata_for_accessible_FCP_dev(
3039 	const char *port_wwn,
3040 	int num_luns,
3041 	struct report_lun_resp *resp_buf,
3042 	fpcfga_list_t	*lap,
3043 	int *l_errnop)
3044 {
3045 	ldata_list_t *listp = NULL, *listp_start = NULL, *listp_end = NULL,
3046 		*prevlp = NULL, *curlp = NULL, *matchp_start = NULL,
3047 		*matchp_end = NULL;
3048 	cfga_list_data_t *clp = NULL;
3049 	char *dyn = NULL, *dyncomp = NULL;
3050 	uchar_t *lun_string;
3051 	uint16_t lun_num;
3052 	int i, j, str_ret;
3053 	fpcfga_ret_t ret;
3054 	char dtype[CFGA_TYPE_LEN];
3055 	struct scsi_inquiry *inq_buf;
3056 	uchar_t	peri_qual;
3057 	cfga_cond_t cond = CFGA_COND_UNKNOWN;
3058 	uchar_t lun_num_raw[SAM_LUN_SIZE];
3059 
3060 	/* when number of lun is 0 it is not an error. so just return ok. */
3061 	if (num_luns == 0) {
3062 		return (FPCFGA_OK);
3063 	}
3064 
3065 	for (i = 0; i < num_luns; i++) {
3066 	    lun_string = (uchar_t *)&(resp_buf->lun_string[i]);
3067 	    memcpy(lun_num_raw, lun_string, sizeof (lun_num_raw));
3068 	    if ((ret = get_standard_inq_data(lap->apidp->xport_phys, port_wwn,
3069 		lun_num_raw, &inq_buf, l_errnop))
3070 		!= FPCFGA_OK) {
3071 		if (ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) {
3072 			(void) strlcpy(dtype,
3073 			(char *)GET_MSG_STR(ERR_UNAVAILABLE), CFGA_TYPE_LEN);
3074 			cond = CFGA_COND_FAILED;
3075 		} else {
3076 			S_FREE(inq_buf);
3077 			return (FPCFGA_LIB_ERR);
3078 		}
3079 	    } else {
3080 		peri_qual = inq_buf->inq_dtype & FP_PERI_QUAL_MASK;
3081 		/*
3082 		 * peripheral qualifier is not 0 so the device node should not
3083 		 * included in the ldata list. There should not be a device
3084 		 * node for the lun either.
3085 		 */
3086 		if (peri_qual != DPQ_POSSIBLE) {
3087 			S_FREE(inq_buf);
3088 			continue;
3089 		}
3090 		*dtype = '\0';
3091 		for (j = 0; j < N_DEVICE_TYPES; j++) {
3092 		    if ((inq_buf->inq_dtype & DTYPE_MASK)
3093 				== device_list[j].itype) {
3094 			(void) strlcpy(dtype, (char *)device_list[j].name,
3095 					CFGA_TYPE_LEN);
3096 			break;
3097 		    }
3098 		}
3099 		if (*dtype == '\0') {
3100 			(void) strlcpy(dtype,
3101 				(char *)device_list[DTYPE_UNKNOWN_INDEX].name,
3102 				CFGA_TYPE_LEN);
3103 		}
3104 	    }
3105 		/*
3106 		 * Followed FCP driver for getting lun number from report
3107 		 * lun data.
3108 		 * According to SAM-2 there are multiple address method for
3109 		 * FCP SCIS LUN.  Logincal unit addressing, peripheral device
3110 		 * addressing, flat space addressing, and extended logical
3111 		 * unit addressing.
3112 		 *
3113 		 * as of 11/2001 FCP supports logical unit addressing and
3114 		 * peripheral device addressing even thoough 3 defined.
3115 		 * SSFCP_LUN_ADDRESSING 0x80
3116 		 * SSFCP_PD_ADDRESSING 0x00
3117 		 * SSFCP_VOLUME_ADDRESSING 0x40
3118 		 *
3119 		 * the menthod below is used by FCP when (lun_string[0] & 0xC0)
3120 		 * is either SSFCP_LUN_ADDRESSING or SSFCP_PD_ADDRESSING mode.
3121 		 */
3122 	    lun_num = ((lun_string[0] & 0x3F) << 8) | lun_string[1];
3123 	    listp = calloc(1, sizeof (ldata_list_t));
3124 	    if (listp == NULL) {
3125 		*l_errnop = errno;
3126 		list_free(&listp_start);
3127 		return (FPCFGA_LIB_ERR);
3128 	    }
3129 
3130 	    clp = &listp->ldata;
3131 		/* Create logical and physical ap_id */
3132 	    (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
3133 		"%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
3134 		LUN_COMP_SEP, lun_num);
3135 	    (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
3136 		"%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
3137 		LUN_COMP_SEP, lun_num);
3138 	    (void) strncpy(clp->ap_type, dtype, strlen(dtype));
3139 	    clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
3140 	    clp->ap_r_state = lap->xport_rstate;
3141 	    clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
3142 	    clp->ap_cond = cond;
3143 	    clp->ap_busy = 0;
3144 	    clp->ap_status_time = (time_t)-1;
3145 	    clp->ap_info[0] = '\0';
3146 	    if (listp_start == NULL) {
3147 		listp_start = listp;
3148 	    } else {
3149 		if ((ret = insert_FCP_dev_ldata(
3150 			port_wwn, lun_num, listp,
3151 			&listp_start)) != FPCFGA_OK) {
3152 			list_free(&listp_start);
3153 			return (ret);
3154 		}
3155 	    }
3156 	    listp = NULL;
3157 	    S_FREE(inq_buf);
3158 	}
3159 
3160 	/*
3161 	 * list data can be null when device peripheral qualifier is not 0
3162 	 * for any luns.  Return ok to continue.
3163 	 */
3164 	if (listp_start == NULL) {
3165 		return (FPCFGA_OK);
3166 	}
3167 	/*
3168 	 * get the end of list for later uses.
3169 	 */
3170 	curlp = listp_start->next;
3171 	prevlp = listp_start;
3172 	while (curlp) {
3173 		prevlp = curlp;
3174 		curlp = curlp->next;
3175 	}
3176 	listp_end = prevlp;
3177 
3178 	/*
3179 	 * if there is no list data just return the FCP dev list.
3180 	 * Normally this should not occur since list data should
3181 	 * be created through g_get_dev_list().
3182 	 */
3183 	if (lap->listp == NULL) {
3184 		lap->listp = listp_start;
3185 		for (listp = listp_start; listp != NULL; listp = listp->next) {
3186 			listp->ldata.ap_cond = CFGA_COND_FAILING;
3187 		}
3188 		return (FPCFGA_OK);
3189 	}
3190 
3191 	dyn = GET_DYN(lap->listp->ldata.ap_phys_id);
3192 	if ((dyn != NULL) && ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
3193 		if ((str_ret = strncmp(dyncomp, port_wwn, WWN_SIZE*2)) == 0) {
3194 			matchp_start = matchp_end = lap->listp;
3195 			while (matchp_end->next != NULL) {
3196 				dyn = GET_DYN(
3197 					matchp_end->next->ldata.ap_phys_id);
3198 				if ((dyn != NULL) &&
3199 				((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
3200 					if ((str_ret = strncmp(dyncomp,
3201 						port_wwn, WWN_SIZE*2)) == 0) {
3202 						matchp_end = matchp_end->next;
3203 					} else {
3204 						break;
3205 					}
3206 				} else {
3207 					break;
3208 				}
3209 			}
3210 			/* fillup inqdtype */
3211 			for (listp = listp_start; listp != NULL;
3212 					listp = listp->next) {
3213 				listp->ldata.ap_cond =
3214 					lap->listp->ldata.ap_cond;
3215 			}
3216 			/* link the new elem of lap->listp. */
3217 			listp_end->next = matchp_end->next;
3218 			/* free the one matching wwn. */
3219 			matchp_end->next = NULL;
3220 			list_free(&matchp_start);
3221 			/* link lap->listp to listp_start. */
3222 			lap->listp = listp_start;
3223 			return (FPCFGA_OK);
3224 		} else if (str_ret > 0) {
3225 			for (listp = listp_start; listp != NULL;
3226 					listp = listp->next) {
3227 				listp->ldata.ap_cond = CFGA_COND_FAILING;
3228 			}
3229 			listp_end->next = lap->listp->next;
3230 			lap->listp = listp_start;
3231 			return (FPCFGA_OK);
3232 		}
3233 	}
3234 
3235 	prevlp = lap->listp;
3236 	curlp = lap->listp->next;
3237 
3238 	dyn = dyncomp = NULL;
3239 	while (curlp != NULL) {
3240 		dyn = GET_DYN(curlp->ldata.ap_phys_id);
3241 		if ((dyn != NULL) &&
3242 			((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
3243 			if ((str_ret = strncmp(dyncomp, port_wwn,
3244 					WWN_SIZE*2)) == 0) {
3245 				matchp_start = matchp_end = curlp;
3246 				while (matchp_end->next != NULL) {
3247 					dyn = GET_DYN(
3248 					matchp_end->next->ldata.ap_phys_id);
3249 					if ((dyn != NULL) &&
3250 						((dyncomp = DYN_TO_DYNCOMP(dyn))
3251 						!= NULL)) {
3252 						if ((str_ret = strncmp(dyncomp,
3253 							port_wwn, WWN_SIZE*2))
3254 							== 0) {
3255 							matchp_end =
3256 							matchp_end->next;
3257 						} else {
3258 							break;
3259 						}
3260 					} else {
3261 						break;
3262 					}
3263 				}
3264 				for (listp = listp_start; listp != NULL;
3265 						listp = listp->next) {
3266 				    listp->ldata.ap_cond = curlp->ldata.ap_cond;
3267 				}
3268 				/* link the next elem to listp_end. */
3269 				listp_end->next = matchp_end->next;
3270 				/* link prevlp to listp_start to drop curlp. */
3271 				prevlp->next = listp_start;
3272 				/* free matching pwwn elem. */
3273 				matchp_end->next = NULL;
3274 				list_free(&matchp_start);
3275 				return (FPCFGA_OK);
3276 			} else if (str_ret > 0) {
3277 				for (listp = listp_start; listp != NULL;
3278 						listp = listp->next) {
3279 					/*
3280 					 * Dev not found from accessible
3281 					 * fc dev list but the node should
3282 					 * exist. Set to failing cond now
3283 					 * and check the node state later.
3284 					 */
3285 				    listp->ldata.ap_cond = CFGA_COND_FAILING;
3286 				}
3287 				/* keep the cur elem by linking to list_end. */
3288 				listp_end->next = curlp;
3289 				prevlp->next = listp_start;
3290 				return (FPCFGA_OK);
3291 			}
3292 		}
3293 		dyn = dyncomp = NULL;
3294 		prevlp = curlp;
3295 		curlp = curlp->next;
3296 	}
3297 
3298 	prevlp->next = listp_start;
3299 	for (listp = listp_start; listp != NULL; listp = listp->next) {
3300 		listp->ldata.ap_cond = CFGA_COND_FAILING;
3301 	}
3302 
3303 	return (FPCFGA_OK);
3304 
3305 }
3306 
3307 /* fill in device type, vid, pid from properties */
3308 static void
get_hw_info(di_node_t node,cfga_list_data_t * clp)3309 get_hw_info(di_node_t node, cfga_list_data_t *clp)
3310 {
3311 	char *cp = NULL;
3312 	char *inq_vid, *inq_pid;
3313 	int i;
3314 
3315 	/*
3316 	 * if the type is not previously assigned with valid SCSI device type
3317 	 * check devinfo to find the type.
3318 	 * once device is configured it should have a valid device type.
3319 	 * device node is configured but no valid device type is found
3320 	 * the type will be set to unavailable.
3321 	 */
3322 	if (clp->ap_type != NULL) {
3323 		/*
3324 		 * if the type is not one of defined SCSI device type
3325 		 * check devinfo to find the type.
3326 		 *
3327 		 * Note: unknown type is not a valid device type.
3328 		 *	It is added in to the device list table to provide
3329 		 *	constant string of "unknown".
3330 		 */
3331 	    for (i = 0; i < (N_DEVICE_TYPES -1); i++) {
3332 		if (strncmp((char *)clp->ap_type, (char *)device_list[i].name,
3333 			sizeof (clp->ap_type)) == 0) {
3334 			break;
3335 		}
3336 	    }
3337 	    if (i == (N_DEVICE_TYPES - 1)) {
3338 		cp = (char *)get_device_type(node);
3339 		if (cp == NULL) {
3340 			cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
3341 		}
3342 		(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
3343 			S_STR(cp));
3344 	    }
3345 	} else {
3346 		cp = (char *)get_device_type(node);
3347 		if (cp == NULL) {
3348 			cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
3349 		}
3350 		(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
3351 			S_STR(cp));
3352 	}
3353 
3354 	/*
3355 	 * Fill in vendor and product ID.
3356 	 */
3357 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
3358 	    "inquiry-product-id", &inq_pid) == 1) &&
3359 	    (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
3360 	    "inquiry-vendor-id", &inq_vid) == 1)) {
3361 		(void) snprintf(clp->ap_info, sizeof (clp->ap_info),
3362 		    "%s %s", inq_vid, inq_pid);
3363 	}
3364 }
3365 
3366 /*
3367  * Get dtype from "inquiry-device-type" property. If not present,
3368  * derive it from minor node type
3369  */
3370 static const char *
get_device_type(di_node_t node)3371 get_device_type(di_node_t node)
3372 {
3373 	char *name = NULL;
3374 	int *inq_dtype;
3375 	int i;
3376 
3377 	if (node == DI_NODE_NIL) {
3378 		return (NULL);
3379 	}
3380 
3381 	/* first, derive type based on inquiry property */
3382 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
3383 	    &inq_dtype) != -1) {
3384 		int itype = (*inq_dtype) & DTYPE_MASK;
3385 
3386 		for (i = 0; i < N_DEVICE_TYPES; i++) {
3387 			if (itype == device_list[i].itype) {
3388 				name = (char *)device_list[i].name;
3389 				break;
3390 			}
3391 		}
3392 		/*
3393 		 * when found to be unknown type, set name to null to check
3394 		 * device minor node type.
3395 		 */
3396 		if (i == (N_DEVICE_TYPES - 1)) {
3397 			name = NULL;
3398 		}
3399 	}
3400 
3401 	/* if property fails, use minor nodetype */
3402 	if (name == NULL) {
3403 		char *nodetype;
3404 		di_minor_t minor = di_minor_next(node, DI_MINOR_NIL);
3405 
3406 		if ((minor != DI_MINOR_NIL) &&
3407 		    ((nodetype = di_minor_nodetype(minor)) != NULL)) {
3408 			for (i = 0; i < N_DEVICE_TYPES; i++) {
3409 				if (device_list[i].ntype &&
3410 				    (strcmp(nodetype, device_list[i].ntype)
3411 				    == 0)) {
3412 					name = (char *)device_list[i].name;
3413 					break;
3414 				}
3415 			}
3416 		}
3417 	}
3418 
3419 	return (name);
3420 }
3421 
3422 /* Transform list data to stat data */
3423 fpcfga_ret_t
list_ext_postprocess(ldata_list_t ** llpp,int nelem,cfga_list_data_t ** ap_id_list,int * nlistp,char ** errstring)3424 list_ext_postprocess(
3425 	ldata_list_t		**llpp,
3426 	int			nelem,
3427 	cfga_list_data_t	**ap_id_list,
3428 	int			*nlistp,
3429 	char			**errstring)
3430 {
3431 	cfga_list_data_t *ldatap = NULL;
3432 	ldata_list_t *tmplp = NULL;
3433 	int i = -1;
3434 
3435 	*ap_id_list = NULL;
3436 	*nlistp = 0;
3437 
3438 	if (*llpp == NULL || nelem < 0) {
3439 		return (FPCFGA_LIB_ERR);
3440 	}
3441 
3442 	if (nelem == 0) {
3443 		return (FPCFGA_APID_NOEXIST);
3444 	}
3445 
3446 	ldatap = calloc(nelem, sizeof (cfga_list_data_t));
3447 	if (ldatap == NULL) {
3448 		cfga_err(errstring, errno, ERR_LIST, 0);
3449 		return (FPCFGA_LIB_ERR);
3450 	}
3451 
3452 	/* Extract the list_data structures from the linked list */
3453 	tmplp = *llpp;
3454 	for (i = 0; i < nelem && tmplp != NULL; i++) {
3455 		ldatap[i] = tmplp->ldata;
3456 		tmplp = tmplp->next;
3457 	}
3458 
3459 	if (i < nelem || tmplp != NULL) {
3460 		S_FREE(ldatap);
3461 		return (FPCFGA_LIB_ERR);
3462 	}
3463 
3464 	*nlistp = nelem;
3465 	*ap_id_list = ldatap;
3466 
3467 	return (FPCFGA_OK);
3468 }
3469 
3470 /*
3471  * Convert bus state to receptacle state
3472  */
3473 static cfga_stat_t
xport_devinfo_to_recep_state(uint_t xport_di_state)3474 xport_devinfo_to_recep_state(uint_t xport_di_state)
3475 {
3476 	cfga_stat_t rs;
3477 
3478 	switch (xport_di_state) {
3479 	case DI_BUS_QUIESCED:
3480 	case DI_BUS_DOWN:
3481 		rs = CFGA_STAT_DISCONNECTED;
3482 		break;
3483 	/*
3484 	 * NOTE: An explicit flag for active should probably be added to
3485 	 * libdevinfo.
3486 	 */
3487 	default:
3488 		rs = CFGA_STAT_CONNECTED;
3489 		break;
3490 	}
3491 
3492 	return (rs);
3493 }
3494 
3495 /*
3496  * Convert device state to occupant state
3497  * if driver is attached the node is configured.
3498  * if offline or down the node is unconfigured.
3499  * if only driver detached it is none state which is treated the same
3500  * way as configured state.
3501  */
3502 static cfga_stat_t
dev_devinfo_to_occupant_state(uint_t dev_di_state)3503 dev_devinfo_to_occupant_state(uint_t dev_di_state)
3504 {
3505 	/* Driver attached ? */
3506 	if ((dev_di_state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
3507 		return (CFGA_STAT_CONFIGURED);
3508 	}
3509 
3510 	if ((dev_di_state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
3511 	    (dev_di_state & DI_DEVICE_DOWN) == DI_DEVICE_DOWN) {
3512 		return (CFGA_STAT_UNCONFIGURED);
3513 	} else {
3514 		return (CFGA_STAT_NONE);
3515 	}
3516 }
3517 
3518 /*
3519  * Wrapper routine for inserting ldata to make an sorted ldata list.
3520  *
3521  * When show_FCP_dev option is given insert_FCP_dev_ldata() is called.
3522  * Otherwise insert_fc_dev_ldata() is called.
3523  */
3524 static fpcfga_ret_t
insert_ldata_to_ldatalist(const char * port_wwn,int * lun_nump,ldata_list_t * listp,ldata_list_t ** ldatapp)3525 insert_ldata_to_ldatalist(
3526 	const char *port_wwn,
3527 	int *lun_nump,
3528 	ldata_list_t *listp,
3529 	ldata_list_t **ldatapp)
3530 {
3531 
3532 	if (lun_nump == NULL) {
3533 		return (insert_fc_dev_ldata(port_wwn, listp, ldatapp));
3534 	} else {
3535 		return
3536 		(insert_FCP_dev_ldata(port_wwn, *lun_nump, listp, ldatapp));
3537 	}
3538 }
3539 
3540 /*
3541  * Insert an input ldata to ldata list to make sorted ldata list.
3542  */
3543 static fpcfga_ret_t
insert_fc_dev_ldata(const char * port_wwn,ldata_list_t * listp,ldata_list_t ** ldatapp)3544 insert_fc_dev_ldata(
3545 	const char *port_wwn,
3546 	ldata_list_t *listp,
3547 	ldata_list_t **ldatapp)
3548 {
3549 	ldata_list_t *prevlp = NULL, *curlp = NULL;
3550 	char *dyn = NULL, *dyncomp = NULL;
3551 
3552 	if (*ldatapp == NULL) {
3553 		*ldatapp = listp;
3554 		return (FPCFGA_OK);
3555 	}
3556 
3557 	dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
3558 	if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
3559 	if ((dyncomp != NULL) &&
3560 		(strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
3561 			listp->next = *ldatapp;
3562 			*ldatapp = listp;
3563 			return (FPCFGA_OK);
3564 	}
3565 	/* else continue */
3566 
3567 	prevlp = *ldatapp;
3568 	curlp = (*ldatapp)->next;
3569 
3570 	dyn = dyncomp = NULL;
3571 	while (curlp != NULL) {
3572 		dyn = GET_DYN(curlp->ldata.ap_phys_id);
3573 		if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
3574 		if ((dyncomp != NULL) &&
3575 				(strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
3576 			listp->next = prevlp->next;
3577 			prevlp->next = listp;
3578 			return (FPCFGA_OK);
3579 		}
3580 		dyn = dyncomp = NULL;
3581 		prevlp = curlp;
3582 		curlp = curlp->next;
3583 	}
3584 
3585 	/* add the ldata to the end of the list. */
3586 	prevlp->next = listp;
3587 	return (FPCFGA_OK);
3588 }
3589 
3590 /*
3591  * Insert an input ldata to ldata list to make sorted ldata list.
3592  */
3593 static fpcfga_ret_t
insert_FCP_dev_ldata(const char * port_wwn,int lun_num,ldata_list_t * listp,ldata_list_t ** ldatapp)3594 insert_FCP_dev_ldata(
3595 	const char *port_wwn,
3596 	int lun_num,
3597 	ldata_list_t *listp,
3598 	ldata_list_t **ldatapp)
3599 {
3600 	ldata_list_t *prevlp = NULL, *curlp = NULL;
3601 	char *dyn = NULL, *dyncomp = NULL;
3602 	char *lun_dyn = NULL, *lunp = NULL;
3603 
3604 	if (*ldatapp == NULL) {
3605 		*ldatapp = listp;
3606 		return (FPCFGA_OK);
3607 	}
3608 
3609 	dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
3610 	if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
3611 	if ((dyncomp != NULL) &&
3612 		(strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
3613 		lun_dyn = GET_LUN_DYN(dyncomp);
3614 		if (lun_dyn != NULL) {
3615 			lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
3616 			if ((atoi(lunp)) >= lun_num) {
3617 				listp->next = *ldatapp;
3618 				*ldatapp = listp;
3619 				return (FPCFGA_OK);
3620 			}
3621 		}
3622 	} else if ((dyncomp != NULL) &&
3623 			(strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
3624 		listp->next = *ldatapp;
3625 		*ldatapp = listp;
3626 		return (FPCFGA_OK);
3627 	}
3628 
3629 	prevlp = *ldatapp;
3630 	curlp = (*ldatapp)->next;
3631 
3632 	dyn = dyncomp = NULL;
3633 	lun_dyn = lunp = NULL;
3634 	while (curlp != NULL) {
3635 		dyn = GET_DYN(curlp->ldata.ap_phys_id);
3636 		if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
3637 
3638 		if ((dyncomp != NULL) &&
3639 				(strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
3640 			lun_dyn = GET_LUN_DYN(dyncomp);
3641 			if (lun_dyn != NULL) {
3642 				lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
3643 				if ((atoi(lunp)) >= lun_num) {
3644 					listp->next = prevlp->next;
3645 					prevlp->next = listp;
3646 					return (FPCFGA_OK);
3647 				}
3648 			}
3649 			/* else continue */
3650 		} else if ((dyncomp != NULL) &&
3651 				(strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
3652 			listp->next = prevlp->next;
3653 			prevlp->next = listp;
3654 			return (FPCFGA_OK);
3655 		}
3656 		/* else continue */
3657 
3658 		dyn = dyncomp = NULL;
3659 		lun_dyn = lunp = NULL;
3660 		prevlp = curlp;
3661 		curlp = curlp->next;
3662 	}
3663 
3664 	/* add the ldata to the end of the list. */
3665 	prevlp->next = listp;
3666 	return (FPCFGA_OK);
3667 }
3668 
3669 /*
3670  * This function will return the dtype for the given device
3671  * It will first issue a report lun to lun 0 and then it will issue a SCSI
3672  * Inquiry to the first lun returned by report luns.
3673  *
3674  * If everything is successful, the dtype will be returned with the peri
3675  * qualifier masked out.
3676  *
3677  * If either the report lun or the scsi inquiry fails, we will first check
3678  * the return status.  If the return status is SCSI_DEVICE_NOT_TGT, then
3679  * we will assume this is a remote HBA and return an UNKNOWN DTYPE
3680  * for all other failures, we will return a dtype of ERR_INQ_DTYPE
3681  */
3682 static uchar_t
get_inq_dtype(char * xport_phys,char * dyncomp,HBA_HANDLE handle,HBA_PORTATTRIBUTES * portAttrs,HBA_PORTATTRIBUTES * discPortAttrs)3683 get_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
3684     HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs) {
3685 	HBA_STATUS		    status;
3686 	report_lun_resp_t	    *resp_buf;
3687 	int			    num_luns = 0, ret, l_errno;
3688 	uchar_t			    *lun_string;
3689 	uint64_t		    lun = 0;
3690 	struct scsi_inquiry	    inq;
3691 	struct scsi_extended_sense  sense;
3692 	HBA_UINT8		    scsiStatus;
3693 	uint32_t		    inquirySize = sizeof (inq);
3694 	uint32_t		    senseSize = sizeof (sense);
3695 
3696 	memset(&inq, 0, sizeof (inq));
3697 	memset(&sense, 0, sizeof (sense));
3698 	if ((ret = get_report_lun_data(xport_phys, dyncomp,
3699 			    &num_luns, &resp_buf, &sense, &l_errno))
3700 	    != FPCFGA_OK) {
3701 		/*
3702 		 * Checking the sense key data as well as the additional
3703 		 * sense key.  The SES Node is not required to repond
3704 		 * to Report LUN.  In the case of Minnow, the SES node
3705 		 * returns with KEY_ILLEGAL_REQUEST and the additional
3706 		 * sense key of 0x20.  In this case we will blindly
3707 		 * send the SCSI Inquiry call to lun 0
3708 		 *
3709 		 * if we get any other error we will set the inq_type
3710 		 * appropriately
3711 		 */
3712 		if ((sense.es_key == KEY_ILLEGAL_REQUEST) &&
3713 		    (sense.es_add_code == 0x20)) {
3714 			lun = 0;
3715 		} else {
3716 			if (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT) {
3717 				inq.inq_dtype = DTYPE_UNKNOWN;
3718 			} else {
3719 				inq.inq_dtype = ERR_INQ_DTYPE;
3720 			}
3721 			return (inq.inq_dtype);
3722 		}
3723 	} else {
3724 		/* send the inquiry to the first lun */
3725 		lun_string = (uchar_t *)&(resp_buf->lun_string[0]);
3726 		memcpy(&lun, lun_string, sizeof (lun));
3727 		S_FREE(resp_buf);
3728 	}
3729 
3730 	memset(&sense, 0, sizeof (sense));
3731 	status = HBA_ScsiInquiryV2(handle,
3732 	    portAttrs->PortWWN, discPortAttrs->PortWWN, lun, 0, 0,
3733 	    &inq, &inquirySize, &scsiStatus, &sense, &senseSize);
3734 	if (status == HBA_STATUS_OK) {
3735 		inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
3736 	} else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
3737 		inq.inq_dtype = DTYPE_UNKNOWN;
3738 	} else {
3739 		inq.inq_dtype = ERR_INQ_DTYPE;
3740 	}
3741 	return (inq.inq_dtype);
3742 }
3743