xref: /titanic_51/usr/src/lib/cfgadm_plugins/fp/common/cfga_cs.c (revision 3c709d8a1cbd663cabf900d1c42dc55f422f781c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 #include	"cfga_fp.h"
28 
29 /* define */
30 #define	ALL_APID_LUNS_UNUSABLE	0x10
31 
32 #define	DEFAULT_LUN_COUNT	1024
33 #define	LUN_SIZE		8
34 #define	LUN_HEADER_SIZE		8
35 #define	DEFAULT_LUN_LENGTH	DEFAULT_LUN_COUNT   *	\
36 				LUN_SIZE	    +	\
37 				LUN_HEADER_SIZE
38 
39 /* Some forward declarations */
40 static fpcfga_ret_t do_devctl_dev_create(apid_t *, char *, int,
41 							uchar_t, char **);
42 static fpcfga_ret_t dev_rcm_online(apid_t *, int, cfga_flags_t, char **);
43 static void dev_rcm_online_nonoperationalpath(apid_t *, cfga_flags_t, char **);
44 static fpcfga_ret_t dev_rcm_offline(apid_t *, cfga_flags_t, char **);
45 static fpcfga_ret_t dev_rcm_remove(apid_t *, cfga_flags_t, char **);
46 static fpcfga_ret_t lun_unconf(char *, int, char *, char *, char **);
47 static fpcfga_ret_t dev_unconf(apid_t *, char **, uchar_t *);
48 static fpcfga_ret_t is_xport_phys_in_pathlist(apid_t *, char **);
49 static void copy_pwwn_data_to_str(char *, const uchar_t *);
50 static fpcfga_ret_t unconf_vhci_nodes(di_path_t, di_node_t, char *,
51 			char *, int, int *, int *, char **, cfga_flags_t);
52 static fpcfga_ret_t unconf_non_vhci_nodes(di_node_t, char *, char *,
53 			int, int *, int *, char **, cfga_flags_t);
54 static fpcfga_ret_t unconf_any_devinfo_nodes(apid_t *, cfga_flags_t, char **,
55 			int *, int *);
56 static fpcfga_ret_t handle_devs(cfga_cmd_t, apid_t *, cfga_flags_t,
57 			char **, HBA_HANDLE, int, HBA_PORTATTRIBUTES);
58 
59 
60 /*
61  * This function initiates the creation of the new device node for a given
62  * port WWN.
63  * So, apidt->dyncomp CANNOT be NULL
64  */
65 static fpcfga_ret_t
66 do_devctl_dev_create(apid_t *apidt, char *dev_path, int pathlen,
67 					uchar_t dev_dtype, char **errstring)
68 {
69 	devctl_ddef_t	ddef_hdl;
70 	devctl_hdl_t	bus_hdl, dev_hdl;
71 	char		*drvr_name = "dummy";
72 	la_wwn_t	pwwn;
73 
74 	*dev_path = NULL;
75 	if ((ddef_hdl = devctl_ddef_alloc(drvr_name, 0)) == NULL) {
76 		cfga_err(errstring, errno, ERRARG_DC_DDEF_ALLOC, drvr_name, 0);
77 		return (FPCFGA_LIB_ERR);
78 	}
79 
80 	if (cvt_dyncomp_to_lawwn(apidt->dyncomp, &pwwn)) {
81 		devctl_ddef_free(ddef_hdl);
82 		cfga_err(errstring, 0, ERR_APID_INVAL, 0);
83 		return (FPCFGA_LIB_ERR);
84 	}
85 
86 	if (devctl_ddef_byte_array(ddef_hdl, PORT_WWN_PROP, FC_WWN_SIZE,
87 							pwwn.raw_wwn) == -1) {
88 		devctl_ddef_free(ddef_hdl);
89 		cfga_err(errstring, errno, ERRARG_DC_BYTE_ARRAY,
90 							PORT_WWN_PROP, 0);
91 		return (FPCFGA_LIB_ERR);
92 	}
93 
94 	if ((bus_hdl = devctl_bus_acquire(apidt->xport_phys, 0)) == NULL) {
95 		devctl_ddef_free(ddef_hdl);
96 		cfga_err(errstring, errno, ERRARG_DC_BUS_ACQUIRE,
97 							apidt->xport_phys, 0);
98 		return (FPCFGA_LIB_ERR);
99 	}
100 
101 	/* Let driver handle creation of the new path */
102 	if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl)) {
103 		devctl_ddef_free(ddef_hdl);
104 		devctl_release(bus_hdl);
105 		if (dev_dtype == DTYPE_UNKNOWN) {
106 			/*
107 			 * Unknown DTYPES are devices such as another system's
108 			 * FC HBA port. We have tried to configure it but
109 			 * have failed. Since devices with no device type
110 			 * or an unknown dtype cannot be configured, we will
111 			 * return an appropriate error message.
112 			 */
113 			cfga_err(errstring, errno,
114 			    ERRARG_BUS_DEV_CREATE_UNKNOWN, apidt->dyncomp, 0);
115 		} else {
116 			cfga_err(errstring, errno, ERRARG_BUS_DEV_CREATE,
117 			    apidt->dyncomp, 0);
118 		}
119 		return (FPCFGA_LIB_ERR);
120 	}
121 	devctl_release(bus_hdl);
122 	devctl_ddef_free(ddef_hdl);
123 
124 	devctl_get_pathname(dev_hdl, dev_path, pathlen);
125 	devctl_release(dev_hdl);
126 
127 	return (FPCFGA_OK);
128 }
129 
130 /*
131  * Online, in RCM, all the LUNs for a particular device.
132  * Caller can specify the # of luns in the lunlist that have to be onlined
133  * by passing a count that is not -ve.
134  *
135  * INPUT :
136  * apidt - this is expected to have the list of luns for the device and so
137  *         is assumed to be filled in prior to this call
138  * count - # of LUNs in the list that have to be onlined.
139  * errstring - If non-NULL, it will hold any error messages
140  *
141  * RETURNS :
142  * 0 on success
143  * non-zero otherwise
144  */
145 static fpcfga_ret_t
146 dev_rcm_online(apid_t *apidt, int count, cfga_flags_t flags, char **errstring)
147 {
148 	luninfo_list_t	*lunlistp;
149 	int		i = 0, ret = 0;
150 	fpcfga_ret_t	retval = FPCFGA_OK;
151 
152 	/* This check may be redundant, but safer this way */
153 	if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
154 		/* User has requested not to notify RCM framework */
155 		return (FPCFGA_OK);
156 	}
157 
158 	lunlistp = apidt->lunlist;
159 
160 	for (lunlistp = apidt->lunlist; lunlistp != NULL;
161 					i++, lunlistp = lunlistp->next) {
162 		if ((count >= 0) && (i >= count))
163 			break;
164 		if (fp_rcm_online(lunlistp->path, errstring, flags) !=
165 								FPCFGA_OK) {
166 			ret++;
167 		}
168 	}
169 
170 	if (ret > 0)
171 		retval = FPCFGA_LIB_ERR;
172 
173 	return (retval);
174 }
175 
176 /*
177  * Online in RCM for devices which only have paths
178  * not in ONLINE/STANDBY state
179  */
180 void
181 dev_rcm_online_nonoperationalpath(apid_t *apidt, cfga_flags_t flags,
182     char **errstring)
183 {
184 	luninfo_list_t	*lunlistp;
185 
186 	if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
187 		return;
188 	}
189 
190 	lunlistp = apidt->lunlist;
191 
192 	for (lunlistp = apidt->lunlist; lunlistp != NULL;
193 	    lunlistp = lunlistp->next) {
194 		if ((lunlistp->lun_flag & FLAG_SKIP_ONLINEOTHERS) != 0) {
195 			continue;
196 		}
197 		(void) fp_rcm_online(lunlistp->path, errstring, flags);
198 	}
199 }
200 
201 /*
202  * Offline, in RCM, all the LUNs for a particular device.
203  * This function should not be called for the MPXIO case.
204  *
205  * INPUT :
206  * apidt - this is expected to have the list of luns for the device and so
207  *         is assumed to be filled in prior to this call
208  * errstring - If non-NULL, it will hold any error messages
209  *
210  * RETURNS :
211  * FPCFGA_OK on success
212  * error code otherwise
213  */
214 static fpcfga_ret_t
215 dev_rcm_offline(apid_t *apidt, cfga_flags_t flags, char **errstring)
216 {
217 	int		count = 0;
218 	luninfo_list_t	*lunlistp;
219 
220 	if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
221 		/* User has requested not to notify RCM framework */
222 		return (FPCFGA_OK);
223 	}
224 
225 	for (lunlistp = apidt->lunlist; lunlistp != NULL;
226 						lunlistp = lunlistp->next) {
227 		if ((lunlistp->lun_flag & FLAG_SKIP_RCMOFFLINE) != 0) {
228 			continue;
229 		}
230 		if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
231 		    FLAG_REMOVE_UNUSABLE_FCP_DEV) {
232 			int ret = strncmp(lunlistp->path, SCSI_VHCI_ROOT,
233 				strlen(SCSI_VHCI_ROOT));
234 
235 			if (((ret == 0) &&
236 			    (lunlistp->node_state == DI_PATH_STATE_OFFLINE)) ||
237 			    ((ret != 0) &&
238 			    ((lunlistp->node_state & DI_DEVICE_OFFLINE) ==
239 			    DI_DEVICE_OFFLINE))) {
240 				/* Offline the device through RCM */
241 				if (fp_rcm_offline(lunlistp->path, errstring,
242 					    flags) != 0) {
243 					/*
244 					 * Bring everything back online in
245 					 * rcm and return
246 					 */
247 					(void) dev_rcm_online(apidt, count,
248 								flags, NULL);
249 					return (FPCFGA_LIB_ERR);
250 				}
251 				count++;
252 			}
253 		} else {
254 			/* Offline the device through RCM */
255 			if (fp_rcm_offline(lunlistp->path, errstring,
256 				    flags) != 0) {
257 				/*
258 				 * Bring everything back online in
259 				 * rcm and return
260 				 */
261 				(void) dev_rcm_online(apidt, count, flags,
262 									NULL);
263 				return (FPCFGA_LIB_ERR);
264 			}
265 			count++;
266 		}
267 	}
268 	return (FPCFGA_OK);
269 }
270 
271 /*
272  * Remove, in RCM, all the LUNs for a particular device.
273  * This function should not be called for the MPXIO case.
274  *
275  * INPUT :
276  * apidt - this is expected to have the list of luns for the device and so
277  *         is assumed to be filled in prior to this call
278  * errstring - If non-NULL, it will hold any error messages
279  *
280  * RETURNS :
281  * FPCFGA_OK on success
282  * error code otherwise
283  */
284 static fpcfga_ret_t
285 dev_rcm_remove(apid_t *apidt, cfga_flags_t flags, char **errstring)
286 {
287 	int		count = 0;
288 	luninfo_list_t	*lunlistp;
289 
290 	if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
291 		/* User has requested not to notify RCM framework */
292 		return (FPCFGA_OK);
293 	}
294 
295 	for (lunlistp = apidt->lunlist; lunlistp != NULL;
296 						lunlistp = lunlistp->next) {
297 		if ((lunlistp->lun_flag & FLAG_SKIP_RCMREMOVE) != 0)
298 			continue;
299 		if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
300 		    FLAG_REMOVE_UNUSABLE_FCP_DEV) {
301 			int ret = strncmp(lunlistp->path, SCSI_VHCI_ROOT,
302 				strlen(SCSI_VHCI_ROOT));
303 
304 			if (((ret == 0) &&
305 			    (lunlistp->node_state == DI_PATH_STATE_OFFLINE)) ||
306 			    ((ret != 0) &&
307 			    ((lunlistp->node_state & DI_DEVICE_OFFLINE) ==
308 			    DI_DEVICE_OFFLINE))) {
309 				/* remove the device through RCM */
310 				if (fp_rcm_remove(lunlistp->path, errstring,
311 					    flags) != 0) {
312 					/*
313 					 * Bring everything back online in
314 					 * rcm and return
315 					 */
316 					(void) dev_rcm_online(apidt, count,
317 								flags, NULL);
318 					return (FPCFGA_LIB_ERR);
319 				}
320 				count++;
321 			}
322 		} else {
323 			/* remove the device through RCM */
324 			if (fp_rcm_remove(lunlistp->path, errstring,
325 				flags) != 0) {
326 				/*
327 				 * Bring everything back online in rcm and
328 				 * return
329 				 */
330 				(void) dev_rcm_online(apidt, count, flags,
331 									NULL);
332 				return (FPCFGA_LIB_ERR);
333 			}
334 			count++;
335 		}
336 	}
337 	return (FPCFGA_OK);
338 }
339 
340 static fpcfga_ret_t
341 lun_unconf(char *path, int lunnum, char *xport_phys, char *dyncomp,
342 						char **errstring)
343 {
344 	devctl_hdl_t	hdl;
345 	char		*ptr;		/* To use as scratch/temp pointer */
346 	char		pathname[MAXPATHLEN];
347 
348 	if (path == NULL)
349 		return (FPCFGA_OK);
350 
351 	if (strncmp(path, SCSI_VHCI_ROOT, strlen(SCSI_VHCI_ROOT)) == 0) {
352 		/*
353 		 * We have an MPXIO managed device here.
354 		 * So, we have to concoct a path for the device.
355 		 *
356 		 * xport_phys looks like :
357 		 * /devices/pci@b,2000/pci@1/SUNW,qlc@5/fp@0,0:fc
358 		 */
359 		(void) strlcpy(pathname, xport_phys, MAXPATHLEN);
360 		if ((ptr = strrchr(pathname, ':')) != NULL) {
361 			*ptr = '\0';
362 		}
363 
364 		/*
365 		 * Get pointer to driver name from VHCI path
366 		 * So, if lunlistp->path is
367 		 * /devices/scsi_vhci/ssd@g220000203707a417,
368 		 * we need a pointer to the last '/'
369 		 *
370 		 * Assumption:
371 		 * With MPXIO there will be only one entry per lun
372 		 * So, there will only be one entry in the linked list
373 		 * apidt->lunlist
374 		 */
375 		if ((ptr = strrchr(path, '/')) == NULL) {
376 			/* This shouldn't happen, but anyways ... */
377 			cfga_err(errstring, 0, ERRARG_INVALID_PATH, path, 0);
378 			return (FPCFGA_LIB_ERR);
379 		}
380 
381 		/*
382 		 * Make pathname to look something like :
383 		 * /devices/pci@x,xxxx/pci@x/SUNW,qlc@x/fp@x,x/ssd@w...
384 		 */
385 		strcat(pathname, ptr);
386 
387 		/*
388 		 * apidt_create() will make sure that lunlist->path
389 		 * has a "@<something>" at the end even if the driver
390 		 * state is "detached"
391 		 */
392 		if ((ptr = strrchr(pathname, '@')) == NULL) {
393 			/* This shouldn't happen, but anyways ... */
394 			cfga_err(errstring, 0, ERRARG_INVALID_PATH,
395 						pathname, 0);
396 			return (FPCFGA_LIB_ERR);
397 		}
398 		*ptr = '\0';
399 
400 		/* Now, concoct the path */
401 		sprintf(&pathname[strlen(pathname)], "@w%s,%x",
402 							dyncomp, lunnum);
403 		ptr = pathname;
404 	} else {
405 		/*
406 		 * non-MPXIO path, use the path that is passed in
407 		 */
408 		ptr = path;
409 	}
410 
411 	if ((hdl = devctl_device_acquire(ptr,  0)) == NULL) {
412 		cfga_err(errstring, errno, ERRARG_DEV_ACQUIRE, ptr, 0);
413 		return (FPCFGA_LIB_ERR);
414 	}
415 
416 	if (devctl_device_remove(hdl) != 0) {
417 		devctl_release(hdl);
418 		cfga_err(errstring, errno, ERRARG_DEV_REMOVE, ptr, 0);
419 		return (FPCFGA_LIB_ERR);
420 	}
421 	devctl_release(hdl);
422 
423 	return (FPCFGA_OK);
424 }
425 
426 static fpcfga_ret_t
427 dev_unconf(apid_t *apidt, char **errstring, uchar_t *flag)
428 {
429 	luninfo_list_t	*lunlistp;
430 	fpcfga_ret_t	ret = FPCFGA_OK;
431 	int lun_cnt = 0, unusable_lun_cnt = 0;
432 
433 	for (lunlistp = apidt->lunlist; lunlistp != NULL;
434 	    lunlistp = lunlistp->next) {
435 		lun_cnt++;
436 		/*
437 		 * Unconfigure each LUN.
438 		 * Note that for MPXIO devices, lunlistp->path will be a
439 		 * vHCI path
440 		 */
441 		if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
442 			FLAG_REMOVE_UNUSABLE_FCP_DEV) {
443 		    if (strncmp(lunlistp->path, SCSI_VHCI_ROOT,
444 			strlen(SCSI_VHCI_ROOT)) == 0) {
445 			if (lunlistp->node_state ==
446 				DI_PATH_STATE_OFFLINE) {
447 			    unusable_lun_cnt++;
448 			    if ((ret = lun_unconf(lunlistp->path,
449 				lunlistp->lunnum, apidt->xport_phys,
450 				apidt->dyncomp, errstring)) != FPCFGA_OK) {
451 				return (ret);
452 			    }
453 			}
454 		    } else {
455 			if ((lunlistp->node_state & DI_DEVICE_OFFLINE) ==
456 				DI_DEVICE_OFFLINE) {
457 			    unusable_lun_cnt++;
458 			    if ((ret = lun_unconf(lunlistp->path,
459 				lunlistp->lunnum, apidt->xport_phys,
460 				apidt->dyncomp, errstring)) != FPCFGA_OK) {
461 				return (ret);
462 			    }
463 			}
464 		    }
465 		} else {
466 		/*
467 		 * Unconfigure each LUN.
468 		 * Note that for MPXIO devices, lunlistp->path will be a
469 		 * vHCI path
470 		 */
471 		    if ((ret = lun_unconf(lunlistp->path, lunlistp->lunnum,
472 				apidt->xport_phys, apidt->dyncomp,
473 				errstring)) != FPCFGA_OK) {
474 			return (ret);
475 		    }
476 		}
477 	}
478 
479 	if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
480 			FLAG_REMOVE_UNUSABLE_FCP_DEV) {
481 		/*
482 		 * when all luns are unconfigured
483 		 * indicate to remove repository entry.
484 		 */
485 		if (lun_cnt == unusable_lun_cnt) {
486 			*flag = ALL_APID_LUNS_UNUSABLE;
487 		}
488 	}
489 
490 	return (ret);
491 }
492 
493 /*
494  * Check if the given physical path (the xport_phys) is part of the
495  * pHCI list and if the RCM should be done for a particular pHCI.
496  * Skip non-MPxIO dev node if any.
497  */
498 static fpcfga_ret_t
499 is_xport_phys_in_pathlist(apid_t *apidt, char **errstring)
500 {
501 	di_node_t	root, vhci, node, phci;
502 	di_path_t	path = DI_PATH_NIL;
503 	int		num_active_paths, found = 0;
504 	char		*vhci_path_ptr, *pathname_ptr, pathname[MAXPATHLEN];
505 	char		*phci_path, *node_path;
506 	char		phci_addr[MAXPATHLEN];
507 	char		*xport_phys, *vhci_path, *dyncomp;
508 	luninfo_list_t	*lunlistp, *temp;
509 	int		non_operational_path_count;
510 
511 	/* a safety check */
512 	if ((apidt->dyncomp == NULL) || (*apidt->dyncomp == '\0')) {
513 		return (FPCFGA_LIB_ERR);
514 	}
515 
516 	xport_phys = apidt->xport_phys;
517 	dyncomp = apidt->dyncomp;
518 
519 	lunlistp = apidt->lunlist;
520 	for (lunlistp = apidt->lunlist; lunlistp != NULL;
521 	    lunlistp = lunlistp->next) {
522 
523 		if (strncmp(lunlistp->path, SCSI_VHCI_ROOT,
524 		    strlen(SCSI_VHCI_ROOT)) != 0) {
525 			lunlistp->lun_flag |= FLAG_SKIP_ONLINEOTHERS;
526 			continue;
527 		}
528 
529 		vhci_path = lunlistp->path;
530 
531 		num_active_paths = 0;	/* # of paths in ONLINE/STANDBY */
532 		non_operational_path_count = 0;
533 
534 		if (xport_phys == NULL || vhci_path == NULL) {
535 		    cfga_err(errstring, 0, ERRARG_XPORT_NOT_IN_PHCI_LIST,
536 		    xport_phys, 0);
537 			return (FPCFGA_LIB_ERR);
538 		}
539 
540 		(void) strlcpy(pathname, xport_phys, MAXPATHLEN);
541 		if ((pathname_ptr = strrchr(pathname, ':')) != NULL) {
542 			*pathname_ptr = '\0';
543 		}
544 		/* strip off the /devices/from the path */
545 		pathname_ptr = pathname + strlen(DEVICES_DIR);
546 
547 		root = di_init("/", DINFOCPYALL|DINFOPATH);
548 
549 		if (root == DI_NODE_NIL) {
550 			return (FPCFGA_LIB_ERR);
551 		}
552 
553 		vhci_path_ptr = vhci_path + strlen(DEVICES_DIR);
554 		if ((vhci = di_drv_first_node(SCSI_VHCI_DRVR, root)) ==
555 		    DI_NODE_NIL) {
556 			return (FPCFGA_LIB_ERR);
557 		}
558 		found = 0;
559 		for (node = di_child_node(vhci); node != DI_NODE_NIL;
560 		    node = di_sibling_node(node)) {
561 			if ((node_path = di_devfs_path(node)) != NULL) {
562 				if (strncmp(vhci_path_ptr, node_path,
563 				    strlen(node_path)) != 0) {
564 					di_devfs_path_free(node_path);
565 				} else {
566 					found = 1;
567 					break;
568 				}
569 			}
570 		}
571 		if (found == 0) {
572 			cfga_err(errstring, 0, ERRARG_XPORT_NOT_IN_PHCI_LIST,
573 			    xport_phys, 0);
574 			di_fini(root);
575 			return (FPCFGA_LIB_ERR);
576 		}
577 		/* found vhci_path we are looking for */
578 		di_devfs_path_free(node_path);
579 		found = 0;
580 		for (path = di_path_next_phci(node, DI_PATH_NIL);
581 		    path != DI_PATH_NIL;
582 		    path = di_path_next_phci(node, path)) {
583 			if ((phci = di_path_phci_node(path)) == DI_NODE_NIL) {
584 				cfga_err(errstring, 0,
585 				    ERRARG_XPORT_NOT_IN_PHCI_LIST,
586 				    xport_phys, 0);
587 				di_fini(root);
588 				return (FPCFGA_LIB_ERR);
589 			}
590 			if ((phci_path = di_devfs_path(phci)) == NULL) {
591 				cfga_err(errstring, 0,
592 				    ERRARG_XPORT_NOT_IN_PHCI_LIST,
593 				    xport_phys, 0);
594 				di_fini(root);
595 				return (FPCFGA_LIB_ERR);
596 			}
597 			(void) di_path_addr(path, (char *)phci_addr);
598 			if ((phci_addr == NULL) || (*phci_addr == '\0')) {
599 				cfga_err(errstring, 0,
600 				    ERRARG_XPORT_NOT_IN_PHCI_LIST,
601 				    xport_phys, 0);
602 				di_devfs_path_free(phci_path);
603 				di_fini(root);
604 				return (FPCFGA_LIB_ERR);
605 			}
606 			/*
607 			 * Check if the phci path has the same
608 			 * xport addr and the target addr with current lun
609 			 */
610 			if ((strncmp(phci_path, pathname_ptr,
611 			    strlen(pathname_ptr)) == 0) &&
612 			    (strstr(phci_addr, dyncomp) != NULL)) {
613 				/* SUCCESS Found xport_phys */
614 				found = 1;
615 			} else if ((di_path_state(path) ==
616 			    DI_PATH_STATE_ONLINE) ||
617 			    (di_path_state(path) == DI_PATH_STATE_STANDBY)) {
618 				num_active_paths++;
619 			} else {
620 				/*
621 				 * We have another path not in ONLINE/STANDBY
622 				 * state now, so should do a RCM online after
623 				 * the unconfiguration of current path.
624 				 */
625 				non_operational_path_count++;
626 			}
627 			di_devfs_path_free(phci_path);
628 		}
629 		di_fini(root);
630 		if (found == 1) {
631 			if (num_active_paths != 0) {
632 				/*
633 				 * There are other ONLINE/STANDBY paths,
634 				 * so no need to do the RCM
635 				 */
636 				lunlistp->lun_flag |= FLAG_SKIP_RCMREMOVE;
637 				lunlistp->lun_flag |= FLAG_SKIP_RCMOFFLINE;
638 			}
639 			if (non_operational_path_count == 0) {
640 				lunlistp->lun_flag |= FLAG_SKIP_ONLINEOTHERS;
641 			}
642 		} else {
643 			/*
644 			 * Fail all operations here
645 			 */
646 			cfga_err(errstring, 0, ERRARG_XPORT_NOT_IN_PHCI_LIST,
647 			    xport_phys, 0);
648 			return (FPCFGA_APID_NOEXIST);
649 		}
650 	}
651 
652 	/* Mark duplicated paths for same vhci in the list */
653 	for (lunlistp = apidt->lunlist; lunlistp != NULL;
654 	    lunlistp = lunlistp->next) {
655 		if (strncmp(lunlistp->path, SCSI_VHCI_ROOT,
656 		    strlen(SCSI_VHCI_ROOT)) != 0) {
657 			continue;
658 		}
659 		for (temp = lunlistp->next; temp != NULL;
660 		    temp = temp->next) {
661 			if (strcmp(lunlistp->path, temp->path) == 0) {
662 				/*
663 				 * don't do RCM for dup
664 				 */
665 				lunlistp->lun_flag |= FLAG_SKIP_RCMREMOVE;
666 				lunlistp->lun_flag |= FLAG_SKIP_RCMOFFLINE;
667 				lunlistp->lun_flag |= FLAG_SKIP_ONLINEOTHERS;
668 			}
669 		}
670 	}
671 	return (FPCFGA_OK);
672 }
673 /*
674  * apidt->dyncomp has to be non-NULL by the time this routine is called
675  */
676 fpcfga_ret_t
677 dev_change_state(cfga_cmd_t state_change_cmd, apid_t *apidt, la_wwn_t *pwwn,
678 		cfga_flags_t flags, char **errstring, HBA_HANDLE handle,
679 		HBA_PORTATTRIBUTES portAttrs)
680 {
681 	char			dev_path[MAXPATHLEN];
682 	char			*update_str, *t_apid;
683 	int			optflag = apidt->flags;
684 	int			no_config_attempt = 0;
685 	fpcfga_ret_t		ret;
686 	apid_t			my_apidt;
687 	uchar_t			unconf_flag = 0, peri_qual;
688 	HBA_STATUS		status;
689 	HBA_PORTATTRIBUTES	discPortAttrs;
690 	uint64_t		lun = 0;
691 	struct scsi_inquiry	inq;
692 	struct scsi_extended_sense sense;
693 	HBA_UINT8		scsiStatus;
694 	uint32_t		inquirySize = sizeof (inq),
695 				senseSize = sizeof (sense);
696 	report_lun_resp_t	*resp_buf;
697 	int			i, l_errno, num_luns = 0;
698 	uchar_t			*lun_string;
699 
700 	if ((apidt->dyncomp == NULL) || (*apidt->dyncomp == '\0')) {
701 		/*
702 		 * No dynamic component specified. Just return success.
703 		 * Should not see this case. Just a safety check.
704 		 */
705 		return (FPCFGA_OK);
706 	}
707 
708 	/* Now construct the string we are going to put in the repository */
709 	if ((update_str = calloc(1, (strlen(apidt->xport_phys) +
710 		strlen(DYN_SEP) + strlen(apidt->dyncomp) + 1))) == NULL) {
711 		cfga_err(errstring, errno, ERR_MEM_ALLOC, 0);
712 		return (FPCFGA_LIB_ERR);
713 	}
714 	strcpy(update_str, apidt->xport_phys);
715 	strcat(update_str, DYN_SEP);
716 	strcat(update_str, apidt->dyncomp);
717 
718 	/* If force update of repository is sought, do it first */
719 	if (optflag & FLAG_FORCE_UPDATE_REP) {
720 		/* Ignore any failure in rep update */
721 		(void) update_fabric_wwn_list(
722 			((state_change_cmd == CFGA_CMD_CONFIGURE) ?
723 			ADD_ENTRY : REMOVE_ENTRY),
724 			update_str, errstring);
725 	}
726 
727 	memset(&sense, 0, sizeof (sense));
728 	if ((ret = get_report_lun_data(apidt->xport_phys, apidt->dyncomp,
729 		&num_luns, &resp_buf, &sense, &l_errno)) != FPCFGA_OK) {
730 		/*
731 		 * Checking the sense key data as well as the additional
732 		 * sense key.  The SES Node is not required to repond
733 		 * to Report LUN.  In the case of Minnow, the SES node
734 		 * returns with KEY_ILLEGAL_REQUEST and the additional
735 		 * sense key of 0x20.  In this case we will blindly
736 		 * send the SCSI Inquiry call to lun 0
737 		 *
738 		 * if we get any other error we will set the inq_type
739 		 * appropriately
740 		 */
741 		if ((sense.es_key == KEY_ILLEGAL_REQUEST) &&
742 		    (sense.es_add_code == 0x20)) {
743 			lun = 0;
744 		} else {
745 			if (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT) {
746 				inq.inq_dtype = DTYPE_UNKNOWN;
747 			} else {
748 				/*
749 				 * Failed to get the LUN data for the device
750 				 * If we find that there is a lunlist for this
751 				 * device it could mean that there are dangling
752 				 * devinfo nodes. So, we will go ahead and try
753 				 * to unconfigure them.
754 				 */
755 				if ((apidt->lunlist == NULL) ||
756 				    (state_change_cmd == CFGA_CMD_CONFIGURE)) {
757 					S_FREE(update_str);
758 					status = getPortAttrsByWWN(handle,
759 					    *((HBA_WWN *)(pwwn)),
760 					    &discPortAttrs);
761 					if (status ==
762 					    HBA_STATUS_ERROR_ILLEGAL_WWN) {
763 						return (FPCFGA_APID_NOEXIST);
764 					} else {
765 						cfga_err(errstring, 0,
766 						    ERRARG_FC_REP_LUNS,
767 						    apidt->dyncomp, 0);
768 						return (FPCFGA_LIB_ERR);
769 					}
770 				} else {
771 					/* unconfig with lunlist not empty */
772 					no_config_attempt++;
773 				}
774 			}
775 		}
776 	}
777 	for (i = 0; i < num_luns; i++) {
778 		/*
779 		 * issue the inquiry to the first valid lun found
780 		 * in the lun_string
781 		 */
782 		lun_string = (uchar_t *)&(resp_buf->lun_string[i]);
783 		memcpy(&lun, lun_string, sizeof (lun));
784 
785 		memset(&sense, 0, sizeof (sense));
786 		status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
787 		    *(HBA_WWN *)(pwwn), lun, 0, 0, &inq, &inquirySize,
788 		    &scsiStatus, &sense, &senseSize);
789 		/*
790 		 * if Inquiry is returned correctly, check the
791 		 * peripheral qualifier for the lun.  if it is non-zero
792 		 * then try the SCSI Inquiry on the next lun
793 		 */
794 		if (status == HBA_STATUS_OK) {
795 			peri_qual = inq.inq_dtype & FP_PERI_QUAL_MASK;
796 			if (peri_qual == DPQ_POSSIBLE) {
797 				break;
798 			}
799 		}
800 	}
801 
802 	if (ret == FPCFGA_OK)
803 		S_FREE(resp_buf);
804 
805 	/*
806 	 * If there are no luns on this target, we will attempt to send
807 	 * the SCSI Inquiry to lun 0
808 	 */
809 	if (num_luns == 0) {
810 		lun = 0;
811 		status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
812 		    *(HBA_WWN *)(pwwn), lun, 0, 0, &inq, &inquirySize,
813 		    &scsiStatus, &sense, &senseSize);
814 	}
815 
816 	if (status != HBA_STATUS_OK) {
817 		if (status ==  HBA_STATUS_ERROR_NOT_A_TARGET) {
818 			inq.inq_dtype = DTYPE_UNKNOWN;
819 		} else if (status ==  HBA_STATUS_ERROR_ILLEGAL_WWN) {
820 			free(update_str);
821 			return (FPCFGA_APID_NOEXIST);
822 		} else {
823 			/*
824 			 * Failed to get the inq_dtype of device
825 			 * If we find that there is a lunlist for this
826 			 * device it could mean that there dangling
827 			 * devinfo nodes. So, we will go ahead and try
828 			 * to unconfigure them.  We'll just set the
829 			 * inq_dtype to some invalid value (0xFF)
830 			 */
831 			if ((apidt->lunlist == NULL) ||
832 			    (state_change_cmd == CFGA_CMD_CONFIGURE)) {
833 				cfga_err(errstring, 0,
834 				    ERRARG_FC_INQUIRY,
835 				    apidt->dyncomp, 0);
836 				free(update_str);
837 				return (FPCFGA_LIB_ERR);
838 			} else {
839 				/* unconfig with lunlist not empty */
840 				no_config_attempt++;
841 			}
842 		}
843 	}
844 	switch (state_change_cmd) {
845 	case CFGA_CMD_CONFIGURE:
846 	    if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
847 		portAttrs.PortType != HBA_PORTTYPE_NPORT) {
848 		free(update_str);
849 		return (FPCFGA_OK);
850 	    }
851 
852 	    if (((inq.inq_dtype & DTYPE_MASK) == DTYPE_UNKNOWN) &&
853 		((flags & CFGA_FLAG_FORCE) == 0)) {
854 		/*
855 		 * We assume all DTYPE_UNKNOWNs are HBAs and we wont
856 		 * waste time trying to config them. If they are not
857 		 * HBAs, then there is something wrong since they should
858 		 * have had a valid dtype.
859 		 *
860 		 * However, if the force flag is set (cfgadm -f), we
861 		 * go ahead and try to configure.
862 		 *
863 		 * In this path, however, the force flag is not set.
864 		 */
865 		free(update_str);
866 		return (FPCFGA_OK);
867 	    }
868 
869 	    errno = 0;
870 		/*
871 		 * We'll issue the devctl_bus_dev_create() call even if the
872 		 * path exists in the devinfo tree. This is to take care of
873 		 * the situation where the device may be in a state other
874 		 * than the online and attached state.
875 		 */
876 	    if ((ret = do_devctl_dev_create(apidt, dev_path, MAXPATHLEN,
877 			inq.inq_dtype, errstring)) != FPCFGA_OK) {
878 		/*
879 		 * Could not configure device. To provide a more
880 		 * meaningful error message, first see if the supplied port
881 		 * WWN is there on the fabric. Otherwise print the error
882 		 * message using the information received from the driver
883 		 */
884 		status = getPortAttrsByWWN(handle, *((HBA_WWN *)(pwwn)),
885 		    &discPortAttrs);
886 		S_FREE(update_str);
887 		if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
888 			return (FPCFGA_APID_NOEXIST);
889 		} else {
890 			return (FPCFGA_LIB_ERR);
891 		}
892 	    }
893 
894 	    if (((optflag & (FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) &&
895 		update_fabric_wwn_list(ADD_ENTRY, update_str, errstring)) {
896 		    cfga_err(errstring, 0, ERR_CONF_OK_UPD_REP, 0);
897 	    }
898 
899 	    S_FREE(update_str);
900 
901 	    if ((apidt->flags & FLAG_DISABLE_RCM) == 0) {
902 		/*
903 		 * There may be multiple LUNs associated with the
904 		 * WWN we created nodes for. So, we'll call
905 		 * apidt_create() again and let it build a list of
906 		 * all the LUNs for this WWN using the devinfo tree.
907 		 * We will then online all those devices in RCM
908 		 */
909 		    if ((t_apid = calloc(1, strlen(apidt->xport_phys) +
910 					strlen(DYN_SEP) +
911 					strlen(apidt->dyncomp) + 1)) == NULL) {
912 			    cfga_err(errstring, errno, ERR_MEM_ALLOC, 0);
913 			    return (FPCFGA_LIB_ERR);
914 		    }
915 		    sprintf(t_apid, "%s%s%s", apidt->xport_phys, DYN_SEP,
916 			apidt->dyncomp);
917 		    if ((ret = apidt_create(t_apid, &my_apidt,
918 					errstring)) != FPCFGA_OK) {
919 			    free(t_apid);
920 			    return (ret);
921 		    }
922 
923 		    my_apidt.flags = apidt->flags;
924 		    if ((ret = dev_rcm_online(&my_apidt, -1, flags,
925 					NULL)) != FPCFGA_OK) {
926 			    cfga_err(errstring, 0, ERRARG_RCM_ONLINE,
927 				apidt->lunlist->path, 0);
928 			    apidt_free(&my_apidt);
929 			    free(t_apid);
930 			    return (ret);
931 		    }
932 		    S_FREE(t_apid);
933 		    apidt_free(&my_apidt);
934 	    }
935 	    return (FPCFGA_OK);
936 
937 	case CFGA_CMD_UNCONFIGURE:
938 		if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
939 		    portAttrs.PortType != HBA_PORTTYPE_NPORT) {
940 			free(update_str);
941 			return (FPCFGA_OPNOTSUPP);
942 		}
943 
944 		status = getPortAttrsByWWN(handle, *((HBA_WWN *)(pwwn)),
945 		    &discPortAttrs);
946 		if (apidt->lunlist == NULL) {
947 			/*
948 			 * But first, remove entry from the repository if it is
949 			 * there ... provided the force update flag is not set
950 			 * (in which case the update is already done) or if
951 			 * the no-update flag is not set.
952 			 */
953 			if ((optflag &
954 			(FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) {
955 				if (update_fabric_wwn_list(REMOVE_ENTRY,
956 						update_str, errstring)) {
957 					free(update_str);
958 					cfga_err(errstring, 0,
959 						ERR_UNCONF_OK_UPD_REP, 0);
960 					return
961 					(FPCFGA_UNCONF_OK_UPD_REP_FAILED);
962 				}
963 			}
964 			S_FREE(update_str);
965 			if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
966 				return (FPCFGA_APID_NOEXIST);
967 			}
968 			return (FPCFGA_OK);
969 		}
970 		/*
971 		 * If there are multiple paths to the mpxio
972 		 * device, we will not check in RCM ONLY when there
973 		 * is atleast one other ONLINE/STANDBY path
974 		 */
975 		if (is_xport_phys_in_pathlist(apidt, errstring) !=
976 		    FPCFGA_OK) {
977 			free(update_str);
978 			return (FPCFGA_XPORT_NOT_IN_PHCI_LIST);
979 		}
980 
981 		/*
982 		 * dev_rcm_offline() updates errstring
983 		 */
984 		if ((ret = dev_rcm_offline(apidt, flags, errstring)) !=
985 		    FPCFGA_OK) {
986 			free(update_str);
987 			return (ret);
988 		}
989 		if ((ret = dev_unconf(apidt, errstring, &unconf_flag)) !=
990 		    FPCFGA_OK) {
991 			/* when inq failed don't attempt to reconfigure */
992 		    if (!no_config_attempt) {
993 			(void) do_devctl_dev_create(apidt, dev_path, MAXPATHLEN,
994 				inq.inq_dtype, NULL);
995 			(void) dev_rcm_online(apidt, -1, flags, NULL);
996 		    }
997 		    free(update_str);
998 		    return (ret);
999 		}
1000 		if ((ret = dev_rcm_remove(apidt, flags, errstring)) !=
1001 		    FPCFGA_OK) {
1002 			(void) do_devctl_dev_create(apidt, dev_path, MAXPATHLEN,
1003 				inq.inq_dtype, NULL);
1004 			(void) dev_rcm_online(apidt, -1, flags, NULL);
1005 			free(update_str);
1006 			return (ret);
1007 		}
1008 		/*
1009 		 * If we offlined a lun in RCM when there are multiple paths but
1010 		 * none of them are ONLINE/STANDBY, we have to online it back
1011 		 * in RCM now. This is a try best, will not fail for it.
1012 		 */
1013 		dev_rcm_online_nonoperationalpath(apidt, flags, NULL);
1014 
1015 		/* Update the repository if we havent already done it */
1016 		if ((optflag &
1017 			(FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) {
1018 			if (((optflag & FLAG_REMOVE_UNUSABLE_FCP_DEV) !=
1019 				    FLAG_REMOVE_UNUSABLE_FCP_DEV) ||
1020 			    (((optflag & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
1021 				FLAG_REMOVE_UNUSABLE_FCP_DEV) &&
1022 			    (unconf_flag == ALL_APID_LUNS_UNUSABLE))) {
1023 				if (update_fabric_wwn_list(REMOVE_ENTRY,
1024 					    update_str, errstring)) {
1025 				    free(update_str);
1026 				    cfga_err(errstring, errno,
1027 					ERR_UNCONF_OK_UPD_REP, 0);
1028 				    return (FPCFGA_UNCONF_OK_UPD_REP_FAILED);
1029 				}
1030 			}
1031 		}
1032 		free(update_str);
1033 		return (FPCFGA_OK);
1034 
1035 	default:
1036 		free(update_str);
1037 		return (FPCFGA_OPNOTSUPP);
1038 	}
1039 }
1040 
1041 /*
1042  * This function copies a port_wwn got by reading the property on a device
1043  * node (from_ptr in the function below) on to an array (to_ptr) so that it is
1044  * readable.
1045  *
1046  * Caller responsible to allocate enough memory in "to_ptr"
1047  */
1048 static void
1049 copy_pwwn_data_to_str(char *to_ptr, const uchar_t *from_ptr)
1050 {
1051 	if ((to_ptr == NULL) || (from_ptr == NULL))
1052 		return;
1053 
1054 	(void) sprintf(to_ptr, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
1055 	from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
1056 	from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
1057 }
1058 
1059 static fpcfga_ret_t
1060 unconf_vhci_nodes(di_path_t pnode, di_node_t fp_node, char *xport_phys,
1061 	char *dyncomp, int unusable_flag,
1062 	int *num_devs, int *failure_count, char **errstring,
1063 	cfga_flags_t flags)
1064 {
1065 	int		iret1, iret2, *lunnump;
1066 	char		*ptr;		/* scratch pad */
1067 	char		*node_path, *vhci_path, *update_str;
1068 	char		port_wwn[WWN_SIZE*2+1], pathname[MAXPATHLEN];
1069 	uchar_t		*port_wwn_data = NULL;
1070 	di_node_t	client_node;
1071 
1072 	while (pnode != DI_PATH_NIL) {
1073 
1074 		(*num_devs)++;
1075 
1076 
1077 		if ((node_path = di_devfs_path(fp_node)) == NULL) {
1078 			cfga_err(errstring, 0, ERRARG_DEVINFO,
1079 							xport_phys, 0);
1080 			(*failure_count)++;
1081 			pnode = di_path_next_client(fp_node, pnode);
1082 			continue;
1083 		}
1084 
1085 		iret1 = di_path_prop_lookup_bytes(pnode, PORT_WWN_PROP,
1086 			&port_wwn_data);
1087 
1088 		iret2 = di_path_prop_lookup_ints(pnode, LUN_PROP, &lunnump);
1089 
1090 		if ((iret1 == -1) || (iret2 == -1)) {
1091 			cfga_err(errstring, 0, ERRARG_DI_GET_PROP,
1092 								node_path, 0);
1093 			di_devfs_path_free(node_path);
1094 			node_path = NULL;
1095 			(*failure_count)++;
1096 			pnode = di_path_next_client(fp_node, pnode);
1097 			continue;
1098 		}
1099 
1100 		copy_pwwn_data_to_str(port_wwn, port_wwn_data);
1101 
1102 		if ((client_node = di_path_client_node(pnode)) ==
1103 								DI_NODE_NIL) {
1104 			(*failure_count)++;
1105 			di_devfs_path_free(node_path);
1106 			node_path = NULL;
1107 			pnode = di_path_next_client(fp_node, pnode);
1108 			continue;
1109 		}
1110 
1111 		if ((vhci_path = di_devfs_path(client_node)) == NULL) {
1112 			(*failure_count)++;
1113 			di_devfs_path_free(node_path);
1114 			node_path = NULL;
1115 			pnode = di_path_next_client(fp_node, pnode);
1116 			continue;
1117 		}
1118 
1119 		if ((ptr = strrchr(vhci_path, '@')) != NULL) {
1120 			*ptr = '\0';
1121 		}
1122 
1123 		if ((ptr = strrchr(vhci_path, '/')) == NULL) {
1124 			(*failure_count)++;
1125 			di_devfs_path_free(node_path);
1126 			node_path = NULL;
1127 			pnode = di_path_next_client(fp_node, pnode);
1128 			continue;
1129 		}
1130 
1131 		sprintf(pathname, "%s%s/%s@w%s,%x", DEVICES_DIR, node_path,
1132 					++ptr, port_wwn, *lunnump);
1133 
1134 		di_devfs_path_free(node_path);
1135 		di_devfs_path_free(vhci_path);
1136 		node_path = vhci_path = NULL;
1137 
1138 		/*
1139 		 * Try to offline in RCM first and if that is successful,
1140 		 * unconfigure the LUN. If offlining in RCM fails, then
1141 		 * update the failure_count which gets passed back to caller
1142 		 *
1143 		 * Here we got to check if unusable_flag is set or not.
1144 		 * If set, then unconfigure only those luns which are in
1145 		 * node_state DI_PATH_STATE_OFFLINE. If not set, unconfigure
1146 		 * all luns.
1147 		 */
1148 		if ((unusable_flag & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
1149 		    FLAG_REMOVE_UNUSABLE_FCP_DEV) {
1150 			if (pnode->path_state == DI_PATH_STATE_OFFLINE) {
1151 				if (fp_rcm_offline(pathname, errstring,
1152 				    flags) != 0) {
1153 					(*failure_count)++;
1154 					pnode = di_path_next_client(fp_node,
1155 					    pnode);
1156 					continue;
1157 				} else if (lun_unconf(pathname, *lunnump,
1158 				    xport_phys,dyncomp, errstring)
1159 				    != FPCFGA_OK) {
1160 					(void) fp_rcm_online(pathname,
1161 					    NULL, flags);
1162 					(*failure_count)++;
1163 					pnode = di_path_next_client(fp_node,
1164 					    pnode);
1165 					continue;
1166 				} else if (fp_rcm_remove(pathname, errstring,
1167 				    flags) != 0) {
1168 					/*
1169 					 * Bring everything back online
1170 					 * in rcm and continue
1171 					 */
1172 					(void) fp_rcm_online(pathname,
1173 					    NULL, flags);
1174 					(*failure_count)++;
1175 					pnode = di_path_next_client(fp_node,
1176 					    pnode);
1177 					continue;
1178 				}
1179 			} else {
1180 				pnode = di_path_next(fp_node, pnode);
1181 				continue;
1182 			}
1183 		} else {
1184 			if (fp_rcm_offline(pathname, errstring, flags) != 0) {
1185 				(*failure_count)++;
1186 				pnode = di_path_next_client(fp_node, pnode);
1187 				continue;
1188 			} else if (lun_unconf(pathname, *lunnump, xport_phys,
1189 			    dyncomp, errstring) != FPCFGA_OK) {
1190 				(void) fp_rcm_online(pathname, NULL, flags);
1191 				(*failure_count)++;
1192 				pnode = di_path_next_client(fp_node, pnode);
1193 				continue;
1194 			} else if (fp_rcm_remove(pathname, errstring,
1195 			    flags) != 0) {
1196 				/*
1197 				 * Bring everything back online
1198 				 * in rcm and continue
1199 				 */
1200 				(void) fp_rcm_online(pathname, NULL, flags);
1201 				(*failure_count)++;
1202 				pnode = di_path_next_client(fp_node, pnode);
1203 				continue;
1204 			}
1205 		}
1206 
1207 		/* Update the repository only on a successful unconfigure */
1208 		if ((update_str = calloc(1, strlen(xport_phys) +
1209 					strlen(DYN_SEP) +
1210 					strlen(port_wwn) + 1)) == NULL) {
1211 			cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
1212 			(*failure_count)++;
1213 			pnode = di_path_next_client(fp_node, pnode);
1214 			continue;
1215 		}
1216 
1217 		/* Init the string to be removed from repository */
1218 		sprintf(update_str, "%s%s%s", xport_phys, DYN_SEP, port_wwn);
1219 
1220 		if (update_fabric_wwn_list(REMOVE_ENTRY, update_str,
1221 								errstring)) {
1222 			S_FREE(update_str);
1223 			cfga_err(errstring, errno,
1224 					ERR_UNCONF_OK_UPD_REP, 0);
1225 			(*failure_count)++;
1226 			/* Cleanup and continue from here just for clarity */
1227 			pnode = di_path_next_client(fp_node, pnode);
1228 			continue;
1229 		}
1230 
1231 		S_FREE(update_str);
1232 		pnode = di_path_next_client(fp_node, pnode);
1233 	}
1234 
1235 	return (FPCFGA_OK);
1236 }
1237 
1238 static fpcfga_ret_t
1239 unconf_non_vhci_nodes(di_node_t dnode, char *xport_phys, char *dyncomp,
1240 	int unusable_flag, int *num_devs, int *failure_count,
1241 	char **errstring, cfga_flags_t flags)
1242 {
1243 	int	ret1, ret2, *lunnump;
1244 	char	pathname[MAXPATHLEN];
1245 	char	*node_path, *update_str;
1246 	char	port_wwn[WWN_SIZE*2+1];
1247 	uchar_t	*port_wwn_data = NULL;
1248 
1249 	while (dnode != DI_NODE_NIL) {
1250 
1251 		(*num_devs)++;
1252 
1253 		/* Get the physical path for this node */
1254 		if ((node_path = di_devfs_path(dnode)) == NULL) {
1255 			/*
1256 			 * We don't try to offline in RCM here because we
1257 			 * don't know the path to offline. Just continue to
1258 			 * the next node.
1259 			 */
1260 			cfga_err(errstring, 0, ERRARG_DEVINFO, xport_phys, 0);
1261 			(*failure_count)++;
1262 			dnode = di_sibling_node(dnode);
1263 			continue;
1264 		}
1265 
1266 		/* Now get the LUN # of this device thru the property */
1267 		ret1 = di_prop_lookup_ints(DDI_DEV_T_ANY, dnode,
1268 							LUN_PROP, &lunnump);
1269 
1270 		/* Next get the port WWN of the device */
1271 		ret2 = di_prop_lookup_bytes(DDI_DEV_T_ANY, dnode,
1272 						PORT_WWN_PROP, &port_wwn_data);
1273 
1274 		/* A failure in any of the above is not good */
1275 		if ((ret1 == -1) || (ret2 == -1)) {
1276 			/*
1277 			 * We don't try to offline in RCM here because we
1278 			 * don't know the path to offline. Just continue to
1279 			 * the next node.
1280 			 */
1281 			cfga_err(errstring, 0,
1282 					ERRARG_DI_GET_PROP, node_path, 0);
1283 			di_devfs_path_free(node_path);
1284 			node_path = NULL;
1285 			(*failure_count)++;
1286 			dnode = di_sibling_node(dnode);
1287 			continue;
1288 		}
1289 
1290 		/* Prepend the "/devices" prefix to the path and copy it */
1291 		sprintf(pathname, "%s%s", DEVICES_DIR, node_path);
1292 		di_devfs_path_free(node_path);
1293 		node_path = NULL;
1294 
1295 		copy_pwwn_data_to_str(port_wwn, port_wwn_data);
1296 
1297 		if (strstr(pathname, "@w") == NULL) {
1298 			/*
1299 			 * If the driver is detached, some part of the path
1300 			 * may be missing and so we'll manually construct it
1301 			 */
1302 			sprintf(&pathname[strlen(pathname)], "@w%s,%x",
1303 							port_wwn, *lunnump);
1304 		}
1305 
1306 		/*
1307 		 * Try to offline in RCM first and if that is successful,
1308 		 * unconfigure the LUN. If offlining in RCM fails, then
1309 		 * update the failure count
1310 		 *
1311 		 * Here we got to check if unusable_flag is set or not.
1312 		 * If set, then unconfigure only those luns which are in
1313 		 * node_state DI_DEVICE_OFFLINE or DI_DEVICE_DOWN.
1314 		 * If not set, unconfigure all luns.
1315 		 */
1316 		if ((unusable_flag & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
1317 		    FLAG_REMOVE_UNUSABLE_FCP_DEV) {
1318 			if ((dnode->node_state == DI_DEVICE_OFFLINE) ||
1319 			    (dnode->node_state == DI_DEVICE_DOWN)) {
1320 				if (fp_rcm_offline(pathname, errstring,
1321 				    flags) != 0) {
1322 					(*failure_count)++;
1323 					dnode = di_sibling_node(dnode);
1324 					continue;
1325 				} else if (lun_unconf(pathname, *lunnump,
1326 				    xport_phys,dyncomp, errstring)
1327 				    != FPCFGA_OK) {
1328 					(void) fp_rcm_online(pathname,
1329 					    NULL, flags);
1330 					(*failure_count)++;
1331 					dnode = di_sibling_node(dnode);
1332 					continue;
1333 				} else if (fp_rcm_remove(pathname, errstring,
1334 				    flags) != 0) {
1335 					/*
1336 					 * Bring everything back online
1337 					 * in rcm and continue
1338 					 */
1339 					(void) fp_rcm_online(pathname,
1340 					    NULL, flags);
1341 					(*failure_count)++;
1342 					dnode = di_sibling_node(dnode);
1343 					continue;
1344 				}
1345 			} else {
1346 				dnode = di_sibling_node(dnode);
1347 				continue;
1348 			}
1349 		} else {
1350 			if (fp_rcm_offline(pathname, errstring, flags) != 0) {
1351 				(*failure_count)++;
1352 				dnode = di_sibling_node(dnode);
1353 				continue;
1354 			} else if (lun_unconf(pathname, *lunnump, xport_phys,
1355 			    dyncomp, errstring) != FPCFGA_OK) {
1356 				(void) fp_rcm_online(pathname, NULL, flags);
1357 				(*failure_count)++;
1358 				dnode = di_sibling_node(dnode);
1359 				continue;
1360 			} else if (fp_rcm_remove(pathname, errstring,
1361 			    flags) != 0) {
1362 				/*
1363 				 * Bring everything back online
1364 				 * in rcm and continue
1365 				 */
1366 				(void) fp_rcm_online(pathname, NULL, flags);
1367 				(*failure_count)++;
1368 				dnode = di_sibling_node(dnode);
1369 				continue;
1370 			}
1371 		}
1372 
1373 		/* Update the repository only on a successful unconfigure */
1374 		if ((update_str = calloc(1, strlen(xport_phys) +
1375 					strlen(DYN_SEP) +
1376 					strlen(port_wwn) + 1)) == NULL) {
1377 			cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
1378 			(*failure_count)++;
1379 			dnode = di_sibling_node(dnode);
1380 			continue;
1381 		}
1382 
1383 		/* Init the string to be removed from repository */
1384 		sprintf(update_str, "%s%s%s", xport_phys, DYN_SEP, port_wwn);
1385 
1386 		if (update_fabric_wwn_list(REMOVE_ENTRY, update_str,
1387 								errstring)) {
1388 			S_FREE(update_str);
1389 			cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
1390 			(*failure_count)++;
1391 			dnode = di_sibling_node(dnode);
1392 			continue;
1393 		}
1394 
1395 		S_FREE(update_str);
1396 		dnode = di_sibling_node(dnode);
1397 	}
1398 
1399 	return (FPCFGA_OK);
1400 }
1401 
1402 /*
1403  * INPUT:
1404  * apidt - Pointer to apid_t structure with data filled in
1405  * flags - Flags for special handling
1406  *
1407  * OUTPUT:
1408  * errstring - Applicable only on a failure from plugin
1409  * num_devs  - Incremented per lun
1410  * failure_count - Incremented on any failed operation on lun
1411  *
1412  * RETURNS:
1413  * non-FPCFGA_OK on any validation check error. If this value is returned, no
1414  *             devices were handled.  Consequently num_devs and failure_count
1415  *             will not be incremented.
1416  * FPCFGA_OK This return value doesn't mean that all devices were successfully
1417  *             unconfigured, you have to check failure_count.
1418  */
1419 static fpcfga_ret_t
1420 unconf_any_devinfo_nodes(apid_t *apidt, cfga_flags_t flags, char **errstring,
1421 				int *num_devs, int *failure_count)
1422 {
1423 	char		*node_path = NULL;
1424 	char		pathname[MAXPATHLEN], *ptr;	/* scratch pad */
1425 	di_node_t	root_node, direct_node, fp_node;
1426 	di_path_t	path_node = DI_PATH_NIL;
1427 
1428 	/*
1429 	 * apidt->xport_phys is something like :
1430 	 * /devices/pci@.../SUNW,qlc@../fp@0,0:fc
1431 	 * Make sure we copy both the devinfo and pathinfo nodes
1432 	 */
1433 	(void) strlcpy(pathname, apidt->xport_phys, MAXPATHLEN);
1434 
1435 	/* Now get rid of the ':' at the end */
1436 	if ((ptr = strstr(pathname, MINOR_SEP)) != NULL)
1437 		*ptr = '\0';
1438 
1439 	if (strncmp(pathname, DEVICES_DIR, strlen(DEVICES_DIR))) {
1440 		cfga_err(errstring, 0, ERRARG_INVALID_PATH, pathname, 0);
1441 		return (FPCFGA_INVALID_PATH);
1442 	}
1443 
1444 	if ((root_node = di_init("/", DINFOCPYALL | DINFOPATH)) ==
1445 								DI_NODE_NIL) {
1446 		cfga_err(errstring, errno, ERRARG_DEVINFO,
1447 							apidt->xport_phys, 0);
1448 		return (FPCFGA_LIB_ERR);
1449 	}
1450 
1451 	if ((fp_node = di_drv_first_node("fp", root_node)) == DI_NODE_NIL) {
1452 		cfga_err(errstring, errno, ERRARG_DEVINFO,
1453 							apidt->xport_phys, 0);
1454 		di_fini(root_node);
1455 		return (FPCFGA_LIB_ERR);
1456 	}
1457 
1458 	/*
1459 	 * Search all the fp nodes to see if any match the one we are trying
1460 	 * to unconfigure
1461 	 */
1462 
1463 	/* Skip the "/devices" prefix */
1464 	ptr = pathname + strlen(DEVICES_DIR);
1465 
1466 	while (fp_node != DI_NODE_NIL) {
1467 		node_path = di_devfs_path(fp_node);
1468 		if (strcmp(node_path, ptr) == 0) {
1469 			/* Found the fp node. 'pathname' has the full path */
1470 			di_devfs_path_free(node_path);
1471 			node_path = NULL;
1472 			break;
1473 		}
1474 		fp_node = di_drv_next_node(fp_node);
1475 		di_devfs_path_free(node_path);
1476 	}
1477 
1478 	if (fp_node == DI_NODE_NIL) {
1479 		cfga_err(errstring, 0, ERRARG_NOT_IN_DEVINFO,
1480 							apidt->xport_phys, 0);
1481 		di_fini(root_node);
1482 		return (FPCFGA_LIB_ERR);
1483 	}
1484 
1485 	direct_node = di_child_node(fp_node);
1486 	path_node = di_path_next_client(fp_node, path_node);
1487 
1488 	if ((direct_node == DI_NODE_NIL) && (path_node == DI_PATH_NIL)) {
1489 		/* No devinfo or pathinfo nodes. Great ! Just return success */
1490 		di_fini(root_node);
1491 		return (FPCFGA_OK);
1492 	}
1493 
1494 	/* First unconfigure any non-MPXIO nodes */
1495 	unconf_non_vhci_nodes(direct_node, apidt->xport_phys, apidt->dyncomp,
1496 	    apidt->flags, num_devs, failure_count, errstring, flags);
1497 
1498 	/*
1499 	 * Now we will traverse any path info nodes that are there
1500 	 *
1501 	 * Only MPXIO devices have pathinfo nodes
1502 	 */
1503 	unconf_vhci_nodes(path_node, fp_node, apidt->xport_phys, apidt->dyncomp,
1504 	    apidt->flags, num_devs, failure_count, errstring, flags);
1505 
1506 	di_fini(root_node);
1507 
1508 	/*
1509 	 * We don't want to check the return value of unconf_non_vhci_nodes()
1510 	 * and unconf_vhci_nodes().  But instead, we are interested only in
1511 	 * consistently incrementing num_devs and failure_count so that we can
1512 	 * compare them.
1513 	 */
1514 	return (FPCFGA_OK);
1515 }
1516 
1517 /*
1518  * This function handles configuring/unconfiguring all the devices w.r.t
1519  * the FCA port specified by apidt.
1520  *
1521  * In the unconfigure case, it first unconfigures all the devices that are
1522  * seen through the given port at that moment and then unconfigures all the
1523  * devices that still (somehow) have devinfo nodes on the system for that FCA
1524  * port.
1525  *
1526  * INPUT:
1527  * cmd - CFGA_CMD_CONFIGURE or CFGA_CMD_UNCONFIGURE
1528  * apidt - Pointer to apid_t structure with data filled in
1529  * flags - Flags for special handling
1530  *
1531  * OUTPUT:
1532  * errstring - Applicable only on a failure from plugin
1533  *
1534  * RETURNS:
1535  * FPCFGA_OK on success
1536  * non-FPCFGA_OK otherwise
1537  */
1538 static fpcfga_ret_t
1539 handle_devs(cfga_cmd_t cmd, apid_t *apidt, cfga_flags_t flags,
1540 	char **errstring, HBA_HANDLE handle, int portIndex,
1541 	HBA_PORTATTRIBUTES portAttrs)
1542 {
1543 	int		num_devs = 0, dev_cs_failed = 0;
1544 	char		port_wwn[WWN_S_LEN];
1545 	la_wwn_t	pwwn;
1546 	apid_t		my_apidt = {NULL};
1547 	char		*my_apid;
1548 	HBA_PORTATTRIBUTES	discPortAttrs;
1549 	int			discIndex;
1550 	fpcfga_ret_t		rval = FPCFGA_OK;
1551 
1552 	if ((my_apid = calloc(
1553 		1, strlen(apidt->xport_phys) + strlen(DYN_SEP) +
1554 		(2 * FC_WWN_SIZE) + 1)) == NULL) {
1555 		cfga_err(errstring, errno, ERR_MEM_ALLOC, 0);
1556 		return (FPCFGA_LIB_ERR);
1557 	}
1558 
1559 	num_devs = portAttrs.NumberofDiscoveredPorts;
1560 	for (discIndex = 0; discIndex < portAttrs.NumberofDiscoveredPorts;
1561 		discIndex++) {
1562 	    if (getDiscPortAttrs(handle, portIndex,
1563 		discIndex, &discPortAttrs)) {
1564 		dev_cs_failed++;
1565 		/* Move on to the next target */
1566 		continue;
1567 	    }
1568 	    (void) sprintf(port_wwn, "%016llx",
1569 		wwnConversion(discPortAttrs.PortWWN.wwn));
1570 		/*
1571 		 * Construct a fake apid string similar to the one the
1572 		 * plugin gets from the framework and have apidt_create()
1573 		 * fill in the apid_t structure.
1574 		 */
1575 	    strcpy(my_apid, apidt->xport_phys);
1576 	    strcat(my_apid, DYN_SEP);
1577 	    strcat(my_apid, port_wwn);
1578 	    if (apidt_create(my_apid, &my_apidt, errstring) != FPCFGA_OK) {
1579 		dev_cs_failed++;
1580 		continue;
1581 	    }
1582 	    my_apidt.flags = apidt->flags;
1583 
1584 	    memcpy(&pwwn, &(discPortAttrs.PortWWN), sizeof (la_wwn_t));
1585 	    if (dev_change_state(cmd, &my_apidt, &pwwn,
1586 		flags, errstring, handle, portAttrs) != FPCFGA_OK) {
1587 		dev_cs_failed++;
1588 	    }
1589 	    apidt_free(&my_apidt);
1590 	}
1591 
1592 	S_FREE(my_apid);
1593 
1594 	/*
1595 	 * We have now handled all the devices that are currently visible
1596 	 * through the given FCA port. But, it is possible that there are
1597 	 * some devinfo nodes hanging around. For the unconfigure operation,
1598 	 * this has to be looked into too.
1599 	 */
1600 	if (cmd == CFGA_CMD_UNCONFIGURE) {
1601 		/* dev_cs_failed will be updated to indicate any failures */
1602 		rval = unconf_any_devinfo_nodes(apidt, flags, errstring,
1603 		    &num_devs, &dev_cs_failed);
1604 	}
1605 
1606 	if (rval == FPCFGA_OK) {
1607 		if (dev_cs_failed == 0)
1608 			return (FPCFGA_OK);
1609 
1610 		/*
1611 		 * For the discovered ports, num_devs is counted on target
1612 		 * basis, but for invisible targets, num_devs is counted on
1613 		 * lun basis.
1614 		 *
1615 		 * But if dev_cs_failed and num_devs are incremented
1616 		 * consistently, comparation of these two counters is still
1617 		 * meaningful.
1618 		 */
1619 		if (dev_cs_failed == num_devs) {
1620 			/* Failed on all devices seen through this FCA port */
1621 			cfga_err(errstring, 0,
1622 			((cmd == CFGA_CMD_CONFIGURE) ?
1623 				ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
1624 			return (FPCFGA_LIB_ERR);
1625 		} else {
1626 			/* Failed only on some of the devices */
1627 			cfga_err(errstring, 0, ERR_PARTIAL_SUCCESS, 0);
1628 			return (FPCFGA_LIB_ERR);
1629 		}
1630 	} else {
1631 		if (dev_cs_failed == num_devs) {
1632 			/* Failed on all devices seen through this FCA port */
1633 			cfga_err(errstring, 0,
1634 			((cmd == CFGA_CMD_CONFIGURE) ?
1635 				ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
1636 			return (FPCFGA_LIB_ERR);
1637 		} else {
1638 			/* Failed only on some of the devices */
1639 			cfga_err(errstring, 0, ERR_PARTIAL_SUCCESS, 0);
1640 			return (FPCFGA_LIB_ERR);
1641 		}
1642 	}
1643 
1644 	/*
1645 	 * Should never get here
1646 	 */
1647 }
1648 
1649 fpcfga_ret_t
1650 fca_change_state(cfga_cmd_t state_change_cmd, apid_t *apidt,
1651 		cfga_flags_t flags, char **errstring)
1652 {
1653 	fpcfga_ret_t	ret;
1654 	HBA_HANDLE	handle;
1655 	HBA_PORTATTRIBUTES	portAttrs;
1656 	int			portIndex;
1657 
1658 	if ((ret = findMatchingAdapterPort(apidt->xport_phys, &handle,
1659 	    &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
1660 		return (ret);
1661 	}
1662 
1663 	/*
1664 	 * Bail out if not fabric/public loop
1665 	 */
1666 	switch (state_change_cmd) {
1667 	case CFGA_CMD_CONFIGURE:
1668 	    if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
1669 		portAttrs.PortType != HBA_PORTTYPE_NPORT) {
1670 			HBA_CloseAdapter(handle);
1671 			HBA_FreeLibrary();
1672 			return (FPCFGA_OK);
1673 	    }
1674 	    break;
1675 
1676 	case CFGA_CMD_UNCONFIGURE:
1677 	    if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
1678 		portAttrs.PortType != HBA_PORTTYPE_NPORT) {
1679 		HBA_CloseAdapter(handle);
1680 		HBA_FreeLibrary();
1681 		return (FPCFGA_OPNOTSUPP);
1682 	    }
1683 	    break;
1684 	default:
1685 		HBA_CloseAdapter(handle);
1686 		HBA_FreeLibrary();
1687 		return (FPCFGA_LIB_ERR);
1688 	}
1689 	ret = (handle_devs(state_change_cmd, apidt, flags, errstring,
1690 	    handle, portIndex, portAttrs));
1691 	HBA_CloseAdapter(handle);
1692 	HBA_FreeLibrary();
1693 	return (ret);
1694 }
1695