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