xref: /illumos-gate/usr/src/lib/cfgadm_plugins/fp/common/cfga_cs.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 
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 *, char **, cfga_flags_t);
52 static fpcfga_ret_t unconf_non_vhci_nodes(di_node_t, char *, char *,
53 			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 *num_devs, int *failure_count, char **errstring,
1062 	cfga_flags_t flags)
1063 {
1064 	int		iret1, iret2, *lunnump;
1065 	char		*ptr;		/* scratch pad */
1066 	char		*node_path, *vhci_path, *update_str;
1067 	char		port_wwn[WWN_SIZE*2+1], pathname[MAXPATHLEN];
1068 	uchar_t		*port_wwn_data = NULL;
1069 	di_node_t	client_node;
1070 
1071 	while (pnode != DI_PATH_NIL) {
1072 
1073 		(*num_devs)++;
1074 
1075 
1076 		if ((node_path = di_devfs_path(fp_node)) == NULL) {
1077 			cfga_err(errstring, 0, ERRARG_DEVINFO,
1078 							xport_phys, 0);
1079 			(*failure_count)++;
1080 			pnode = di_path_next_client(fp_node, pnode);
1081 			continue;
1082 		}
1083 
1084 		iret1 = di_path_prop_lookup_bytes(pnode, PORT_WWN_PROP,
1085 			&port_wwn_data);
1086 
1087 		iret2 = di_path_prop_lookup_ints(pnode, LUN_PROP, &lunnump);
1088 
1089 		if ((iret1 == -1) || (iret2 == -1)) {
1090 			cfga_err(errstring, 0, ERRARG_DI_GET_PROP,
1091 								node_path, 0);
1092 			di_devfs_path_free(node_path);
1093 			node_path = NULL;
1094 			(*failure_count)++;
1095 			pnode = di_path_next_client(fp_node, pnode);
1096 			continue;
1097 		}
1098 
1099 		copy_pwwn_data_to_str(port_wwn, port_wwn_data);
1100 
1101 		if ((client_node = di_path_client_node(pnode)) ==
1102 								DI_NODE_NIL) {
1103 			(*failure_count)++;
1104 			di_devfs_path_free(node_path);
1105 			node_path = NULL;
1106 			pnode = di_path_next_client(fp_node, pnode);
1107 			continue;
1108 		}
1109 
1110 		if ((vhci_path = di_devfs_path(client_node)) == NULL) {
1111 			(*failure_count)++;
1112 			di_devfs_path_free(node_path);
1113 			node_path = NULL;
1114 			pnode = di_path_next_client(fp_node, pnode);
1115 			continue;
1116 		}
1117 
1118 		if ((ptr = strrchr(vhci_path, '@')) != NULL) {
1119 			*ptr = '\0';
1120 		}
1121 
1122 		if ((ptr = strrchr(vhci_path, '/')) == NULL) {
1123 			(*failure_count)++;
1124 			di_devfs_path_free(node_path);
1125 			node_path = NULL;
1126 			pnode = di_path_next_client(fp_node, pnode);
1127 			continue;
1128 		}
1129 
1130 		sprintf(pathname, "%s%s/%s@w%s,%x", DEVICES_DIR, node_path,
1131 					++ptr, port_wwn, *lunnump);
1132 
1133 		di_devfs_path_free(node_path);
1134 		di_devfs_path_free(vhci_path);
1135 		node_path = vhci_path = NULL;
1136 
1137 		/*
1138 		 * Try to offline in RCM first and if that is successful,
1139 		 * unconfigure the LUN. If offlining in RCM fails, then
1140 		 * update the failure_count which gets passed back to caller
1141 		 */
1142 		if (fp_rcm_offline(pathname, errstring, flags) != 0) {
1143 			(*failure_count)++;
1144 			pnode = di_path_next_client(fp_node, pnode);
1145 			continue;
1146 		} else if (lun_unconf(pathname, *lunnump, xport_phys,
1147 				dyncomp, errstring) != FPCFGA_OK) {
1148 			(void) fp_rcm_online(pathname, NULL, flags);
1149 			(*failure_count)++;
1150 			pnode = di_path_next_client(fp_node, pnode);
1151 			continue;
1152 		} else if (fp_rcm_remove(pathname, errstring, flags) != 0) {
1153 			/*
1154 			 * Bring everything back online in rcm and continue
1155 			 */
1156 			(void) fp_rcm_online(pathname, NULL, flags);
1157 			(*failure_count)++;
1158 			pnode = di_path_next_client(fp_node, pnode);
1159 			continue;
1160 		}
1161 
1162 		/* Update the repository only on a successful unconfigure */
1163 		if ((update_str = calloc(1, strlen(xport_phys) +
1164 					strlen(DYN_SEP) +
1165 					strlen(port_wwn) + 1)) == NULL) {
1166 			cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
1167 			(*failure_count)++;
1168 			pnode = di_path_next_client(fp_node, pnode);
1169 			continue;
1170 		}
1171 
1172 		/* Init the string to be removed from repository */
1173 		sprintf(update_str, "%s%s%s", xport_phys, DYN_SEP, port_wwn);
1174 
1175 		if (update_fabric_wwn_list(REMOVE_ENTRY, update_str,
1176 								errstring)) {
1177 			S_FREE(update_str);
1178 			cfga_err(errstring, errno,
1179 					ERR_UNCONF_OK_UPD_REP, 0);
1180 			(*failure_count)++;
1181 			/* Cleanup and continue from here just for clarity */
1182 			pnode = di_path_next_client(fp_node, pnode);
1183 			continue;
1184 		}
1185 
1186 		S_FREE(update_str);
1187 		pnode = di_path_next_client(fp_node, pnode);
1188 	}
1189 
1190 	return (FPCFGA_OK);
1191 }
1192 
1193 static fpcfga_ret_t
1194 unconf_non_vhci_nodes(di_node_t dnode, char *xport_phys, char *dyncomp,
1195 	int *num_devs, int *failure_count, char **errstring, cfga_flags_t flags)
1196 {
1197 	int	ret1, ret2, *lunnump;
1198 	char	pathname[MAXPATHLEN];
1199 	char	*node_path, *update_str;
1200 	char	port_wwn[WWN_SIZE*2+1];
1201 	uchar_t	*port_wwn_data = NULL;
1202 
1203 	while (dnode != DI_NODE_NIL) {
1204 
1205 		(*num_devs)++;
1206 
1207 		/* Get the physical path for this node */
1208 		if ((node_path = di_devfs_path(dnode)) == NULL) {
1209 			/*
1210 			 * We don't try to offline in RCM here because we
1211 			 * don't know the path to offline. Just continue to
1212 			 * the next node.
1213 			 */
1214 			cfga_err(errstring, 0, ERRARG_DEVINFO, xport_phys, 0);
1215 			(*failure_count)++;
1216 			dnode = di_sibling_node(dnode);
1217 			continue;
1218 		}
1219 
1220 		/* Now get the LUN # of this device thru the property */
1221 		ret1 = di_prop_lookup_ints(DDI_DEV_T_ANY, dnode,
1222 							LUN_PROP, &lunnump);
1223 
1224 		/* Next get the port WWN of the device */
1225 		ret2 = di_prop_lookup_bytes(DDI_DEV_T_ANY, dnode,
1226 						PORT_WWN_PROP, &port_wwn_data);
1227 
1228 		/* A failure in any of the above is not good */
1229 		if ((ret1 == -1) || (ret2 == -1)) {
1230 			/*
1231 			 * We don't try to offline in RCM here because we
1232 			 * don't know the path to offline. Just continue to
1233 			 * the next node.
1234 			 */
1235 			cfga_err(errstring, 0,
1236 					ERRARG_DI_GET_PROP, node_path, 0);
1237 			di_devfs_path_free(node_path);
1238 			node_path = NULL;
1239 			(*failure_count)++;
1240 			dnode = di_sibling_node(dnode);
1241 			continue;
1242 		}
1243 
1244 		/* Prepend the "/devices" prefix to the path and copy it */
1245 		sprintf(pathname, "%s%s", DEVICES_DIR, node_path);
1246 		di_devfs_path_free(node_path);
1247 		node_path = NULL;
1248 
1249 		copy_pwwn_data_to_str(port_wwn, port_wwn_data);
1250 
1251 		if (strstr(pathname, "@w") == NULL) {
1252 			/*
1253 			 * If the driver is detached, some part of the path
1254 			 * may be missing and so we'll manually construct it
1255 			 */
1256 			sprintf(&pathname[strlen(pathname)], "@w%s,%x",
1257 							port_wwn, *lunnump);
1258 		}
1259 
1260 		/*
1261 		 * Try to offline in RCM first and if that is successful,
1262 		 * unconfigure the LUN. If offlining in RCM fails, then
1263 		 * update the failure count
1264 		 */
1265 		if (fp_rcm_offline(pathname, errstring, flags) != 0) {
1266 			(*failure_count)++;
1267 			dnode = di_sibling_node(dnode);
1268 			continue;
1269 		} else if (lun_unconf(pathname, *lunnump, xport_phys,
1270 					dyncomp, errstring) != FPCFGA_OK) {
1271 			(void) fp_rcm_online(pathname, NULL, flags);
1272 			(*failure_count)++;
1273 			dnode = di_sibling_node(dnode);
1274 			continue;
1275 		} else if (fp_rcm_remove(pathname, errstring, flags) != 0) {
1276 			/*
1277 			 * Bring everything back online in rcm and continue
1278 			 */
1279 			(void) fp_rcm_online(pathname, NULL, flags);
1280 			(*failure_count)++;
1281 			dnode = di_sibling_node(dnode);
1282 			continue;
1283 		}
1284 
1285 		/* Update the repository only on a successful unconfigure */
1286 		if ((update_str = calloc(1, strlen(xport_phys) +
1287 					strlen(DYN_SEP) +
1288 					strlen(port_wwn) + 1)) == NULL) {
1289 			cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
1290 			(*failure_count)++;
1291 			dnode = di_sibling_node(dnode);
1292 			continue;
1293 		}
1294 
1295 		/* Init the string to be removed from repository */
1296 		sprintf(update_str, "%s%s%s", xport_phys, DYN_SEP, port_wwn);
1297 
1298 		if (update_fabric_wwn_list(REMOVE_ENTRY, update_str,
1299 								errstring)) {
1300 			S_FREE(update_str);
1301 			cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
1302 			(*failure_count)++;
1303 			dnode = di_sibling_node(dnode);
1304 			continue;
1305 		}
1306 
1307 		S_FREE(update_str);
1308 		dnode = di_sibling_node(dnode);
1309 	}
1310 
1311 	return (FPCFGA_OK);
1312 }
1313 
1314 /*
1315  * INPUT:
1316  * apidt - Pointer to apid_t structure with data filled in
1317  * flags - Flags for special handling
1318  *
1319  * OUTPUT:
1320  * errstring - Applicable only on a failure from plugin
1321  * num_devs  - Incremented per lun
1322  * failure_count - Incremented on any failed operation on lun
1323  *
1324  * RETURNS:
1325  * non-FPCFGA_OK on any validation check error. If this value is returned, no
1326  *             devices were handled.  Consequently num_devs and failure_count
1327  *             will not be incremented.
1328  * FPCFGA_OK This return value doesn't mean that all devices were successfully
1329  *             unconfigured, you have to check failure_count.
1330  */
1331 static fpcfga_ret_t
1332 unconf_any_devinfo_nodes(apid_t *apidt, cfga_flags_t flags, char **errstring,
1333 				int *num_devs, int *failure_count)
1334 {
1335 	char		*node_path = NULL;
1336 	char		pathname[MAXPATHLEN], *ptr;	/* scratch pad */
1337 	di_node_t	root_node, direct_node, fp_node;
1338 	di_path_t	path_node = DI_PATH_NIL;
1339 
1340 	/*
1341 	 * apidt->xport_phys is something like :
1342 	 * /devices/pci@.../SUNW,qlc@../fp@0,0:fc
1343 	 * Make sure we copy both the devinfo and pathinfo nodes
1344 	 */
1345 	(void) strlcpy(pathname, apidt->xport_phys, MAXPATHLEN);
1346 
1347 	/* Now get rid of the ':' at the end */
1348 	if ((ptr = strstr(pathname, MINOR_SEP)) != NULL)
1349 		*ptr = '\0';
1350 
1351 	if (strncmp(pathname, DEVICES_DIR, strlen(DEVICES_DIR))) {
1352 		cfga_err(errstring, 0, ERRARG_INVALID_PATH, pathname, 0);
1353 		return (FPCFGA_INVALID_PATH);
1354 	}
1355 
1356 	if ((root_node = di_init("/", DINFOCPYALL | DINFOPATH)) ==
1357 								DI_NODE_NIL) {
1358 		cfga_err(errstring, errno, ERRARG_DEVINFO,
1359 							apidt->xport_phys, 0);
1360 		return (FPCFGA_LIB_ERR);
1361 	}
1362 
1363 	if ((fp_node = di_drv_first_node("fp", root_node)) == DI_NODE_NIL) {
1364 		cfga_err(errstring, errno, ERRARG_DEVINFO,
1365 							apidt->xport_phys, 0);
1366 		di_fini(root_node);
1367 		return (FPCFGA_LIB_ERR);
1368 	}
1369 
1370 	/*
1371 	 * Search all the fp nodes to see if any match the one we are trying
1372 	 * to unconfigure
1373 	 */
1374 
1375 	/* Skip the "/devices" prefix */
1376 	ptr = pathname + strlen(DEVICES_DIR);
1377 
1378 	while (fp_node != DI_NODE_NIL) {
1379 		node_path = di_devfs_path(fp_node);
1380 		if (strcmp(node_path, ptr) == 0) {
1381 			/* Found the fp node. 'pathname' has the full path */
1382 			di_devfs_path_free(node_path);
1383 			node_path = NULL;
1384 			break;
1385 		}
1386 		fp_node = di_drv_next_node(fp_node);
1387 		di_devfs_path_free(node_path);
1388 	}
1389 
1390 	if (fp_node == DI_NODE_NIL) {
1391 		cfga_err(errstring, 0, ERRARG_NOT_IN_DEVINFO,
1392 							apidt->xport_phys, 0);
1393 		di_fini(root_node);
1394 		return (FPCFGA_LIB_ERR);
1395 	}
1396 
1397 	direct_node = di_child_node(fp_node);
1398 	path_node = di_path_next_client(fp_node, path_node);
1399 
1400 	if ((direct_node == DI_NODE_NIL) && (path_node == DI_PATH_NIL)) {
1401 		/* No devinfo or pathinfo nodes. Great ! Just return success */
1402 		di_fini(root_node);
1403 		return (FPCFGA_OK);
1404 	}
1405 
1406 	/* First unconfigure any non-MPXIO nodes */
1407 	unconf_non_vhci_nodes(direct_node, apidt->xport_phys, apidt->dyncomp,
1408 				num_devs, failure_count, errstring, flags);
1409 
1410 	/*
1411 	 * Now we will traverse any path info nodes that are there
1412 	 *
1413 	 * Only MPXIO devices have pathinfo nodes
1414 	 */
1415 	unconf_vhci_nodes(path_node, fp_node, apidt->xport_phys, apidt->dyncomp,
1416 				num_devs, failure_count, errstring, flags);
1417 
1418 	di_fini(root_node);
1419 
1420 	/*
1421 	 * We don't want to check the return value of unconf_non_vhci_nodes()
1422 	 * and unconf_vhci_nodes().  But instead, we are interested only in
1423 	 * consistently incrementing num_devs and failure_count so that we can
1424 	 * compare them.
1425 	 */
1426 	return (FPCFGA_OK);
1427 }
1428 
1429 /*
1430  * This function handles configuring/unconfiguring all the devices w.r.t
1431  * the FCA port specified by apidt.
1432  *
1433  * In the unconfigure case, it first unconfigures all the devices that are
1434  * seen through the given port at that moment and then unconfigures all the
1435  * devices that still (somehow) have devinfo nodes on the system for that FCA
1436  * port.
1437  *
1438  * INPUT:
1439  * cmd - CFGA_CMD_CONFIGURE or CFGA_CMD_UNCONFIGURE
1440  * apidt - Pointer to apid_t structure with data filled in
1441  * flags - Flags for special handling
1442  *
1443  * OUTPUT:
1444  * errstring - Applicable only on a failure from plugin
1445  *
1446  * RETURNS:
1447  * FPCFGA_OK on success
1448  * non-FPCFGA_OK otherwise
1449  */
1450 static fpcfga_ret_t
1451 handle_devs(cfga_cmd_t cmd, apid_t *apidt, cfga_flags_t flags,
1452 	char **errstring, HBA_HANDLE handle, int portIndex,
1453 	HBA_PORTATTRIBUTES portAttrs)
1454 {
1455 	int		num_devs = 0, dev_cs_failed = 0;
1456 	char		port_wwn[WWN_S_LEN];
1457 	la_wwn_t	pwwn;
1458 	apid_t		my_apidt = {NULL};
1459 	char		*my_apid;
1460 	HBA_PORTATTRIBUTES	discPortAttrs;
1461 	int			discIndex;
1462 	fpcfga_ret_t		rval = FPCFGA_OK;
1463 
1464 	if ((my_apid = calloc(
1465 		1, strlen(apidt->xport_phys) + strlen(DYN_SEP) +
1466 		(2 * FC_WWN_SIZE) + 1)) == NULL) {
1467 		cfga_err(errstring, errno, ERR_MEM_ALLOC, 0);
1468 		return (FPCFGA_LIB_ERR);
1469 	}
1470 
1471 	num_devs = portAttrs.NumberofDiscoveredPorts;
1472 	for (discIndex = 0; discIndex < portAttrs.NumberofDiscoveredPorts;
1473 		discIndex++) {
1474 	    if (getDiscPortAttrs(handle, portIndex,
1475 		discIndex, &discPortAttrs)) {
1476 		dev_cs_failed++;
1477 		/* Move on to the next target */
1478 		continue;
1479 	    }
1480 	    (void) sprintf(port_wwn, "%016llx",
1481 		wwnConversion(discPortAttrs.PortWWN.wwn));
1482 		/*
1483 		 * Construct a fake apid string similar to the one the
1484 		 * plugin gets from the framework and have apidt_create()
1485 		 * fill in the apid_t structure.
1486 		 */
1487 	    strcpy(my_apid, apidt->xport_phys);
1488 	    strcat(my_apid, DYN_SEP);
1489 	    strcat(my_apid, port_wwn);
1490 	    if (apidt_create(my_apid, &my_apidt, errstring) != FPCFGA_OK) {
1491 		dev_cs_failed++;
1492 		continue;
1493 	    }
1494 	    my_apidt.flags = apidt->flags;
1495 
1496 	    memcpy(&pwwn, &(discPortAttrs.PortWWN), sizeof (la_wwn_t));
1497 	    if (dev_change_state(cmd, &my_apidt, &pwwn,
1498 		flags, errstring, handle, portAttrs) != FPCFGA_OK) {
1499 		dev_cs_failed++;
1500 	    }
1501 	    apidt_free(&my_apidt);
1502 	}
1503 
1504 	S_FREE(my_apid);
1505 
1506 	/*
1507 	 * We have now handled all the devices that are currently visible
1508 	 * through the given FCA port. But, it is possible that there are
1509 	 * some devinfo nodes hanging around. For the unconfigure operation,
1510 	 * this has to be looked into too.
1511 	 */
1512 	if (cmd == CFGA_CMD_UNCONFIGURE) {
1513 		/* dev_cs_failed will be updated to indicate any failures */
1514 		rval = unconf_any_devinfo_nodes(apidt, flags, errstring,
1515 		    &num_devs, &dev_cs_failed);
1516 	}
1517 
1518 	if (rval == FPCFGA_OK) {
1519 		if (dev_cs_failed == 0)
1520 			return (FPCFGA_OK);
1521 
1522 		/*
1523 		 * For the discovered ports, num_devs is counted on target
1524 		 * basis, but for invisible targets, num_devs is counted on
1525 		 * lun basis.
1526 		 *
1527 		 * But if dev_cs_failed and num_devs are incremented
1528 		 * consistently, comparation of these two counters is still
1529 		 * meaningful.
1530 		 */
1531 		if (dev_cs_failed == num_devs) {
1532 			/* Failed on all devices seen through this FCA port */
1533 			cfga_err(errstring, 0,
1534 			((cmd == CFGA_CMD_CONFIGURE) ?
1535 				ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
1536 			return (FPCFGA_LIB_ERR);
1537 		} else {
1538 			/* Failed only on some of the devices */
1539 			cfga_err(errstring, 0, ERR_PARTIAL_SUCCESS, 0);
1540 			return (FPCFGA_LIB_ERR);
1541 		}
1542 	} else {
1543 		if (dev_cs_failed == num_devs) {
1544 			/* Failed on all devices seen through this FCA port */
1545 			cfga_err(errstring, 0,
1546 			((cmd == CFGA_CMD_CONFIGURE) ?
1547 				ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
1548 			return (FPCFGA_LIB_ERR);
1549 		} else {
1550 			/* Failed only on some of the devices */
1551 			cfga_err(errstring, 0, ERR_PARTIAL_SUCCESS, 0);
1552 			return (FPCFGA_LIB_ERR);
1553 		}
1554 	}
1555 
1556 	/*
1557 	 * Should never get here
1558 	 */
1559 }
1560 
1561 fpcfga_ret_t
1562 fca_change_state(cfga_cmd_t state_change_cmd, apid_t *apidt,
1563 		cfga_flags_t flags, char **errstring)
1564 {
1565 	fpcfga_ret_t	ret;
1566 	HBA_HANDLE	handle;
1567 	HBA_PORTATTRIBUTES	portAttrs;
1568 	int			portIndex;
1569 
1570 	if ((ret = findMatchingAdapterPort(apidt->xport_phys, &handle,
1571 	    &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
1572 		return (ret);
1573 	}
1574 
1575 	/*
1576 	 * Bail out if not fabric/public loop
1577 	 */
1578 	switch (state_change_cmd) {
1579 	case CFGA_CMD_CONFIGURE:
1580 	    if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
1581 		portAttrs.PortType != HBA_PORTTYPE_NPORT) {
1582 			HBA_CloseAdapter(handle);
1583 			HBA_FreeLibrary();
1584 			return (FPCFGA_OK);
1585 	    }
1586 	    break;
1587 
1588 	case CFGA_CMD_UNCONFIGURE:
1589 	    if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
1590 		portAttrs.PortType != HBA_PORTTYPE_NPORT) {
1591 		HBA_CloseAdapter(handle);
1592 		HBA_FreeLibrary();
1593 		return (FPCFGA_OPNOTSUPP);
1594 	    }
1595 	    break;
1596 	default:
1597 		HBA_CloseAdapter(handle);
1598 		HBA_FreeLibrary();
1599 		return (FPCFGA_LIB_ERR);
1600 	}
1601 	ret = (handle_devs(state_change_cmd, apidt, flags, errstring,
1602 	    handle, portIndex, portAttrs));
1603 	HBA_CloseAdapter(handle);
1604 	HBA_FreeLibrary();
1605 	return (ret);
1606 }
1607