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
do_devctl_dev_create(apid_t * apidt,char * dev_path,int pathlen,uchar_t dev_dtype,char ** errstring)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 = '\0';
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
dev_rcm_online(apid_t * apidt,int count,cfga_flags_t flags,char ** errstring)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
dev_rcm_online_nonoperationalpath(apid_t * apidt,cfga_flags_t flags,char ** errstring)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
dev_rcm_offline(apid_t * apidt,cfga_flags_t flags,char ** errstring)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
dev_rcm_remove(apid_t * apidt,cfga_flags_t flags,char ** errstring)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
lun_unconf(char * path,int lunnum,char * xport_phys,char * dyncomp,char ** errstring)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
dev_unconf(apid_t * apidt,char ** errstring,uchar_t * flag)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
is_xport_phys_in_pathlist(apid_t * apidt,char ** errstring)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
dev_change_state(cfga_cmd_t state_change_cmd,apid_t * apidt,la_wwn_t * pwwn,cfga_flags_t flags,char ** errstring,HBA_HANDLE handle,HBA_PORTATTRIBUTES portAttrs)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
copy_pwwn_data_to_str(char * to_ptr,const uchar_t * from_ptr)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
unconf_vhci_nodes(di_path_t pnode,di_node_t fp_node,char * xport_phys,char * dyncomp,int unusable_flag,int * num_devs,int * failure_count,char ** errstring,cfga_flags_t flags)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
unconf_non_vhci_nodes(di_node_t dnode,char * xport_phys,char * dyncomp,int unusable_flag,int * num_devs,int * failure_count,char ** errstring,cfga_flags_t flags)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
unconf_any_devinfo_nodes(apid_t * apidt,cfga_flags_t flags,char ** errstring,int * num_devs,int * failure_count)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
handle_devs(cfga_cmd_t cmd,apid_t * apidt,cfga_flags_t flags,char ** errstring,HBA_HANDLE handle,int portIndex,HBA_PORTATTRIBUTES portAttrs)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
fca_change_state(cfga_cmd_t state_change_cmd,apid_t * apidt,cfga_flags_t flags,char ** errstring)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