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 /* 23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stdarg.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <fcntl.h> 32 #include <errno.h> 33 #include <unistd.h> 34 #include <stropts.h> 35 #include <strings.h> 36 #include <sys/param.h> 37 #include <libdevinfo.h> 38 #include <locale.h> 39 #include <libintl.h> 40 #include <devid.h> 41 #include <sys/libdevid.h> 42 #include <sys/modctl.h> /* for MAXMODCONFNAME */ 43 #include <sys/scsi/adapters/scsi_vhci.h> 44 45 /* 46 * SAVE_DIR is the directory in which system files are saved. 47 * SAVE_DIR must be under the root filesystem, as this program is 48 * typically run before any other filesystems are mounted. 49 */ 50 #define SAVE_DIR "/etc/mpxio" 51 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" 52 53 /* nvlist property names, these are ALL string types */ 54 #define NVL_DEVID "nvl-devid" 55 #define NVL_PATH "nvl-path" 56 #define NVL_PHYSPATH "nvl-physpath" 57 #define NVL_MPXPATH "nvl-mpxiopath" 58 #define NVL_MPXEN "nvl-mpxioenabled" 59 60 #define MPX_LIST 0x01 61 #define MPX_MAP 0x02 62 #define MPX_CAPABLE_CTRL 0x04 63 #define MPX_DEV_PATH 0x06 64 #define MPX_INIT 0x08 65 #define MPX_PHYSICAL 0x10 66 #define MPX_BOOTPATH 0x20 67 #define MPX_UPDATEVFSTAB 0x40 68 #define MPX_GETPATH 0x60 69 #define MPX_USAGE 0x80 70 #define MSG_INFO 0x01 71 #define MSG_ERROR 0x02 72 #define MSG_PANIC 0x04 73 74 #define BOOT_PATH 0x02 75 #define BOOT 0x01 76 #define NONBOOT 0x00 77 78 #define DISPLAY_ONE_PATH 0x00 79 #define DISPLAY_ALL_PATH 0x01 80 81 static di_node_t devinfo_root = DI_NODE_NIL; 82 static char *ondiskname = "/etc/mpxio/devid_path.cache"; 83 84 /* 85 * We use devid-keyed nvlists to keep track of the guid, traditional and 86 * MPxIO-enabled /dev/rdsk paths. Each of these nvlists is eventually 87 * added to our global nvlist and our on-disk nvlist. 88 */ 89 static nvlist_t *mapnvl; 90 static int mpxenabled = 0; 91 static int limctrl = -1; 92 static int mpxprop = 0; 93 static int guid = 0; 94 static char *drvlimit; 95 static int globarg = 0; 96 static int debugflag = 0; 97 static char *devicep; 98 static int readonlyroot = 0; 99 static int cap_N_option = 0; 100 101 static void print_mpx_capable(di_node_t curnode); 102 static int popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, 103 char *strdevid); 104 static int mpxio_nvl_boilerplate(di_node_t curnode); 105 static int validate_devnvl(); 106 static void report_map(char *argdev, int physpath); 107 static void list_devs(int listguids, int ctrl); 108 static void logmsg(int level, const char *msg, ...); 109 static char *find_link(di_node_t cnode); 110 static void usage(); 111 static void parse_args(int argc, char *argv[]); 112 static void get_devid(di_node_t node, ddi_devid_t *thisdevid); 113 static int print_bootpath(); 114 static void vhci_to_phci(char *devpath, char *slice, int d_flag); 115 static int update_vfstab(); 116 static void report_dev_node_name(char *strdevfspath); 117 static void print_node_name(char *drv_name, char *strdevfspath); 118 int 119 main(int argc, char **argv) 120 { 121 struct stat cachestat; 122 int mapfd = 0; 123 int rv = 0; 124 char *ondiskbuf; 125 size_t newsz = 0; 126 127 parse_args(argc, argv); 128 errno = 0; 129 devinfo_root = di_init("/", DINFOCPYALL|DINFOFORCE); 130 logmsg(MSG_INFO, "errno = %d after " 131 "di_init(/,DINFOCPYALL|DINFOFORCE)\n", errno); 132 if (devinfo_root == NULL) { 133 logmsg(MSG_ERROR, 134 gettext("Unable to take device tree snapshot " 135 "(%s: %d)\n"), strerror(errno), errno); 136 return (-1); 137 } 138 logmsg(MSG_INFO, "opened root di_node\n"); 139 140 if (globarg == MPX_CAPABLE_CTRL) { 141 /* we just want to find MPxIO-capable controllers and exit */ 142 if (drvlimit != NULL) { 143 print_mpx_capable(di_drv_first_node(drvlimit, 144 devinfo_root)); 145 } else { 146 print_mpx_capable(di_drv_first_node("fp", 147 devinfo_root)); 148 print_mpx_capable(di_drv_first_node("mpt", 149 devinfo_root)); 150 print_mpx_capable(di_drv_first_node("mpt_sas", 151 devinfo_root)); 152 print_mpx_capable(di_drv_first_node("pmcs", 153 devinfo_root)); 154 } 155 di_fini(devinfo_root); 156 return (0); 157 } 158 159 mapfd = open(ondiskname, O_RDWR|O_CREAT|O_SYNC, S_IRUSR | S_IWUSR); 160 if (mapfd < 0) { 161 /* we could be in single-user, so try for RO */ 162 if ((mapfd = open(ondiskname, O_RDONLY)) < 0) { 163 logmsg(MSG_ERROR, 164 gettext("Unable to open or create %s:%s\n"), 165 ondiskname, strerror(errno)); 166 return (errno); 167 } 168 readonlyroot = 1; 169 } 170 171 if (stat(ondiskname, &cachestat) != 0) { 172 logmsg(MSG_ERROR, 173 gettext("Unable to stat() %s: %s\n"), 174 ondiskname, strerror(errno)); 175 return (errno); 176 } 177 ondiskbuf = calloc(1, cachestat.st_size); 178 if (ondiskbuf == NULL) { 179 logmsg(MSG_ERROR, 180 gettext("Unable to allocate memory for the devid " 181 "cache file: %s\n"), strerror(errno)); 182 return (errno); 183 } 184 rv = read(mapfd, ondiskbuf, cachestat.st_size); 185 if (rv != cachestat.st_size) { 186 logmsg(MSG_ERROR, 187 gettext("Unable to read all of devid cache file (got %d " 188 "from expected %d bytes): %s\n"), 189 rv, cachestat.st_size, strerror(errno)); 190 return (errno); 191 } 192 errno = 0; 193 rv = nvlist_unpack(ondiskbuf, cachestat.st_size, &mapnvl, 0); 194 if (rv) { 195 logmsg(MSG_INFO, 196 "Unable to unpack devid cache file %s: %s (%d)\n", 197 ondiskname, strerror(rv), rv); 198 if (nvlist_alloc(&mapnvl, NV_UNIQUE_NAME, 0) != 0) { 199 logmsg(MSG_ERROR, 200 gettext("Unable to allocate root property" 201 "list\n")); 202 return (errno); 203 } 204 } 205 free(ondiskbuf); 206 207 if (validate_devnvl() < 0) { 208 logmsg(MSG_ERROR, 209 gettext("unable to validate kernel with on-disk devid " 210 "cache file\n")); 211 return (errno); 212 } 213 214 /* 215 * If we're in single-user mode or maintenance mode, we won't 216 * necessarily have a writable root device (ZFSroot; ufs root is 217 * different in that we _do_ have a writable root device. 218 * This causes problems for the devlink calls (see 219 * $SRC/lib/libdevinfo/devinfo_devlink.c) and we do not try to 220 * write out the devnvl if root is readonly. 221 */ 222 if (!readonlyroot) { 223 rv = nvlist_size(mapnvl, &newsz, NV_ENCODE_NATIVE); 224 if (rv) { 225 logmsg(MSG_ERROR, 226 gettext("Unable to determine size of packed " 227 "on-disk devid cache file %s: %s (%d).\n"), 228 ondiskname, strerror(rv), rv); 229 logmsg(MSG_ERROR, gettext("Terminating\n")); 230 nvlist_free(mapnvl); 231 (void) close(mapfd); 232 return (rv); 233 } 234 235 if ((ondiskbuf = calloc(1, newsz)) == NULL) { 236 logmsg(MSG_ERROR, 237 "Unable to allocate space for writing out new " 238 "on-disk devid cache file: %s\n", strerror(errno)); 239 (void) close(mapfd); 240 nvlist_free(mapnvl); 241 return (errno); 242 } 243 244 rv = nvlist_pack(mapnvl, &ondiskbuf, &newsz, 245 NV_ENCODE_NATIVE, 0); 246 if (rv) { 247 logmsg(MSG_ERROR, 248 gettext("Unable to pack on-disk devid cache " 249 "file: %s (%d)\n"), strerror(rv), rv); 250 (void) close(mapfd); 251 free(ondiskbuf); 252 nvlist_free(mapnvl); 253 return (rv); 254 } 255 256 rv = lseek(mapfd, 0, 0); 257 if (rv == -1) { 258 logmsg(MSG_ERROR, 259 gettext("Unable to seek to start of devid cache " 260 "file: %s (%d)\n"), strerror(errno), errno); 261 (void) close(mapfd); 262 free(ondiskbuf); 263 nvlist_free(mapnvl); 264 return (-1); 265 } 266 267 if (write(mapfd, ondiskbuf, newsz) != newsz) { 268 logmsg(MSG_ERROR, 269 gettext("Unable to completely write out " 270 "on-disk devid cache file: %s\n"), strerror(errno)); 271 (void) close(mapfd); 272 nvlist_free(mapnvl); 273 free(ondiskbuf); 274 return (errno); 275 } 276 } /* !readonlyroot */ 277 278 /* Now we can process the command line args */ 279 if (globarg == MPX_PHYSICAL) { 280 report_map(devicep, BOOT); 281 } else if (globarg == MPX_BOOTPATH) { 282 rv = print_bootpath(); 283 di_fini(devinfo_root); 284 return (rv); 285 } else if (globarg == MPX_UPDATEVFSTAB) { 286 rv = update_vfstab(); 287 di_fini(devinfo_root); 288 return (rv); 289 } else if (globarg == MPX_GETPATH) { 290 report_dev_node_name(devicep); 291 } else if (globarg == MPX_DEV_PATH) { 292 report_map(devicep, BOOT_PATH); 293 } else if (globarg != MPX_INIT) { 294 if (globarg & MPX_LIST) 295 list_devs(guid, limctrl); 296 297 if (globarg == MPX_MAP) 298 report_map(devicep, NONBOOT); 299 } else { 300 logmsg(MSG_INFO, "\nprivate devid cache file initialised\n"); 301 } 302 303 nvlist_free(mapnvl); 304 di_fini(devinfo_root); 305 return (0); 306 } 307 308 static void 309 usage() 310 { 311 (void) fprintf(stderr, 312 gettext("usage: stmsboot_util -b | -m devname | " 313 "-l <ctrl> | -L | [-g] | -n | -N | -i | -p devname\n")); 314 (void) fprintf(stderr, "\n\n"); 315 (void) fprintf(stderr, gettext("\t-h\tprint this usage message\n")); 316 (void) fprintf(stderr, gettext("\t-b\tretrieve the system's bootpath " 317 "setting\n")); 318 (void) fprintf(stderr, gettext("\t-m devname\n")); 319 (void) fprintf(stderr, gettext("\t\tReports the current mapping for " 320 "devname\n")); 321 (void) fprintf(stderr, gettext("\t-g\tprint the GUID for MPxIO-capable " 322 "devices. This\n")); 323 (void) fprintf(stderr, gettext("\t\toption is only valid with the -L " 324 "or -l options\n")); 325 (void) fprintf(stderr, gettext("\t-L | -l <ctrl>\n")); 326 (void) fprintf(stderr, gettext("\t\tList the 'native' to 'MPxIO' " 327 "device mappings. If <ctrl>\n")); 328 (void) fprintf(stderr, gettext("\t\tis specified, only print mappings " 329 "for those devices\n")); 330 (void) fprintf(stderr, gettext("\t\tattached via the specified " 331 "controller.\n")); 332 (void) fprintf(stderr, gettext("\t-i\tinitialise the private devid " 333 "cache file and exit\n")); 334 (void) fprintf(stderr, gettext("\t\tThis option excludes all " 335 "others.\n")); 336 (void) fprintf(stderr, gettext("\t-n\tprint the devfs paths for " 337 "multipath-capable\n")); 338 (void) fprintf(stderr, gettext("\t\tcontroller ports.\n")); 339 (void) fprintf(stderr, gettext("\t-N\tprint the device aliases of " 340 "multipath-capable\n")); 341 (void) fprintf(stderr, gettext("\t\tcontroller ports.\n")); 342 (void) fprintf(stderr, gettext("\t-p\tdevname\n")); 343 (void) fprintf(stderr, gettext("\t\tThis option provides the physical " 344 "devfs path for\n")); 345 (void) fprintf(stderr, gettext("\t\ta specific device (devname). Used " 346 "to set the bootpath\n")); 347 (void) fprintf(stderr, gettext("\t\tvariable on x86/x64 systems\n")); 348 (void) fprintf(stderr, gettext("\t-u\ttranslates device mappings in " 349 "/etc/vfstab as \n")); 350 (void) fprintf(stderr, gettext("\t\trequired. The output is written " 351 "to /etc/mpxio/vfstab.new\n\n")); 352 exit(2); 353 } 354 355 static void 356 parse_args(int argc, char *argv[]) 357 { 358 char opt; 359 360 if (argc == 1) 361 usage(); 362 363 /* 364 * -b prints the bootpath property 365 * -d turns on debug mode for this utility (copious output!) 366 * -D drvname 367 * if supplied, indicates that we're going to operate on 368 * devices attached to this driver. 369 * -g if (-l or -L), prints guids for devices rather than paths 370 * -h prints the usage() help text. 371 * -i initialises the cache file and exits. 372 * -l controller 373 * list non-STMS to STMS device name mappings for the specific 374 * controller, when MPxIO is enabled only. 375 * -L list non-STMS to STMS device name mappings for all controllers 376 * when MPxIO is enabled only. 377 * -m devname 378 * prints the device path (/dev/rdsk) that devname maps to 379 * in the currently-running system. 380 * -n 381 * if supplied, returns name of STMS-capable controller nodes. 382 * If the -D drvname option is specified as well, we only report 383 * nodes attached with drvname. 384 * -N 385 * same as the -n option, except that we only print the 386 * node-name (dev_info :: devi_node_name). Multiple instances 387 * through the libdevinfo snapshot are uniqified and separated 388 * by the "|" character for direct use by egrep(1). 389 * -p devname 390 * prints the physical devfs path for devname. Only used to 391 * determine the bootpath. 392 * -u 393 * remaps devices in /etc/vfstab, saving the newly generated 394 * file to /etc/mpxio/vfstab.new. If we have any remapped 395 * devices, exit with status 0, otherwise -1 for error. 396 */ 397 while ((opt = getopt(argc, argv, "bdD:ghil:Lm:nNo:p:q:u")) != EOF) { 398 switch (opt) { 399 case 'b': 400 globarg = MPX_BOOTPATH; 401 break; 402 case 'd': 403 debugflag = 1; 404 break; 405 case 'D': 406 if ((drvlimit = calloc(1, MAXMODCONFNAME)) == NULL) { 407 logmsg(MSG_ERROR, 408 gettext("Unable to allocate memory for a " 409 "driver name: %s\n"), strerror(errno)); 410 exit(errno); 411 } 412 bcopy(optarg, drvlimit, strlen(optarg)); 413 /* update this if adding support for a new driver */ 414 if ((strncmp(drvlimit, "fp", 2) == NULL) && 415 (strncmp(drvlimit, "mpt", 3) == NULL) && 416 (strncmp(drvlimit, "mpt_sas", 7) == NULL) && 417 (strncmp(drvlimit, "pmcs", 4) == NULL)) { 418 logmsg(MSG_ERROR, 419 gettext("invalid parent driver (%s) " 420 "specified"), drvlimit); 421 usage(); 422 } 423 break; 424 case 'h': 425 /* Just drop out and print the usage() output */ 426 globarg = MPX_USAGE; 427 break; 428 case 'i': 429 globarg = MPX_INIT; 430 break; 431 case 'l': 432 globarg |= MPX_LIST; 433 limctrl = (int)atol(optarg); 434 if (limctrl < 0) { 435 logmsg(MSG_INFO, 436 gettext("invalid controller number " 437 "(%d), checking all controllers\n"), 438 limctrl); 439 } 440 break; 441 case 'L': 442 globarg |= MPX_LIST; 443 break; 444 case 'g': 445 guid = 1; 446 break; 447 case 'm': 448 globarg = MPX_MAP; 449 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { 450 logmsg(MSG_ERROR, 451 gettext("Unable to allocate space for a " 452 "device name\n")); 453 exit(errno); 454 } 455 devicep = strdup(optarg); 456 break; 457 case 'N': 458 cap_N_option = 1; 459 globarg = MPX_CAPABLE_CTRL; 460 break; 461 case 'n': 462 globarg = MPX_CAPABLE_CTRL; 463 break; 464 case 'o': 465 globarg = MPX_GETPATH; 466 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { 467 logmsg(MSG_ERROR, 468 gettext("Unable to allocate space for a " 469 "device name\n")); 470 exit(errno); 471 } 472 devicep = strdup(optarg); 473 break; 474 case 'p': 475 globarg = MPX_PHYSICAL; 476 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { 477 logmsg(MSG_ERROR, 478 gettext("Unable to allocate space for a " 479 "device name\n")); 480 exit(errno); 481 } 482 devicep = strdup(optarg); 483 break; 484 case 'q': 485 globarg = MPX_DEV_PATH; 486 if ((devicep = calloc(1, MAXPATHLEN)) == NULL) { 487 logmsg(MSG_ERROR, 488 gettext("Unable to allocate space for a " 489 "device name\n")); 490 exit(errno); 491 } 492 devicep = strdup(optarg); 493 break; 494 case 'u': 495 globarg = MPX_UPDATEVFSTAB; 496 break; 497 default: 498 logmsg(MSG_ERROR, 499 gettext("Invalid command line option (%c)\n"), 500 opt); 501 usage(); 502 } 503 } 504 505 if ((globarg >= MPX_USAGE) || (guid && (globarg != MPX_LIST))) 506 usage(); 507 508 if ((drvlimit != NULL) && 509 ((globarg != MPX_LIST) && 510 (globarg != MPX_CAPABLE_CTRL))) 511 usage(); 512 } 513 514 static void 515 logmsg(int level, const char *msg, ...) 516 { 517 va_list ap; 518 519 if ((level >= MSG_ERROR) || 520 ((debugflag > 0) && (level >= MSG_INFO))) { 521 (void) fprintf(stdout, "stmsboot: "); 522 va_start(ap, msg); 523 (void) vfprintf(stdout, msg, ap); 524 va_end(ap); 525 } 526 } 527 528 /* 529 * It's up to the caller to do any sorting or pretty-printing of the device 530 * mappings we report. Since we're storing the device links as just the cXtYdZ 531 * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain 532 * compatibility with previous versions of this tool. There's a little bit 533 * of footwork involved to make sure that we show all the paths to a device 534 * rather than just the first one we stashed away. 535 */ 536 static void 537 list_devs(int listguids, int ctrl) 538 { 539 nvlist_t *thisdevnvl; 540 nvpair_t *pair; 541 char *diskpath, *livepath, *key, *querydev; 542 char *matchctrl = NULL; 543 char checkctrl[MAXPATHLEN]; 544 int rv; 545 546 if (!mpxenabled) { 547 if (mpxprop) { 548 logmsg(MSG_ERROR, gettext("MPXIO disabled\n")); 549 } else { 550 logmsg(MSG_ERROR, gettext("No STMS devices have " 551 "been found\n")); 552 } 553 return; 554 } 555 556 if (listguids) { 557 (void) printf(gettext("non-STMS device name\t\t\tGUID\n" 558 "------------------------------------------" 559 "------------------------\n")); 560 } else { 561 (void) printf(gettext("non-STMS device name\t\t\t" 562 "STMS device name\n" 563 "------------------------------------------" 564 "------------------------\n")); 565 } 566 567 bzero(checkctrl, MAXPATHLEN); 568 pair = NULL; 569 while ((pair = nvlist_next_nvpair(mapnvl, pair)) 570 != NULL) { 571 boolean_t livescsivhcip = B_FALSE; 572 573 if ((((rv = nvpair_value_string(pair, &querydev)) < 0) || 574 ((key = nvpair_name(pair)) == NULL)) || 575 ((strstr(key, "/pci") != NULL) || 576 (strstr(key, "/sbus") != NULL) || 577 (strstr(key, "/scsi_vhci") != NULL) || 578 (strncmp(key, "id1", 3) == 0))) { 579 logmsg(MSG_INFO, 580 "list_devs: rv = %d; (%s) is not a devlink, " 581 "continuing.\n", rv, 582 (key != NULL) ? key : "null"); 583 querydev = NULL; 584 continue; 585 } 586 587 (void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl); 588 (void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN, 589 &livescsivhcip); 590 (void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH, 591 &livepath); 592 593 if ((!livescsivhcip) || 594 (livescsivhcip && 595 (strncmp(key, livepath, strlen(key)) == 0))) 596 continue; 597 598 (void) nvlist_lookup_string(thisdevnvl, NVL_PATH, 599 &diskpath); 600 601 logmsg(MSG_INFO, 602 "list_devs: %s :: %s ::%s :: MPXEN (%s)\n", 603 key, diskpath, livepath, 604 ((livescsivhcip) ? "TRUE" : "FALSE")); 605 606 if (ctrl > -1) { 607 (void) sprintf(checkctrl, "c%dt", ctrl); 608 matchctrl = strstr(key, checkctrl); 609 if (matchctrl == NULL) 610 continue; 611 } 612 if (listguids != 0) { 613 char *tempguid; 614 ddi_devid_t curdevid; 615 int rv; 616 617 rv = devid_str_decode(querydev, &curdevid, NULL); 618 if (rv == -1) { 619 logmsg(MSG_INFO, "Unable to decode devid %s\n", 620 key); 621 continue; 622 } 623 tempguid = devid_to_guid(curdevid); 624 if (tempguid != NULL) 625 (void) printf("/dev/rdsk/%s\t%s\n", 626 diskpath, tempguid); 627 628 devid_free_guid(tempguid); 629 devid_free(curdevid); 630 continue; 631 } 632 633 (void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n", 634 (strstr(key, diskpath) == NULL) ? key : diskpath, 635 livepath); 636 } 637 } 638 639 /* 640 * We get passed a device name which we search the mapnvl for. If we find 641 * it, we print the mapping as it is found. It is up to the caller of this 642 * utility to do any pretty-printing of the results. If a device listed on 643 * the command line does not exist in the mapnvl, then we print NOT_MAPPED. 644 * Otherwise we print the command-line device name as it maps to what is 645 * stashed in the mapnvl - even if that's a "no change" device mapping. 646 * 647 * Example output (-p maps to physpath=BOOT) 648 * # /lib/mpxio/stmsboot_util -p \ 649 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a 650 * /scsi_vhci/disk@g500000e011e17720:a 651 * 652 * Or the reverse: 653 * # /lib/mpxio/stmsboot_util -p /scsi_vhci/disk@g500000e011e17720:a 654 * /pci@0,0/pci1022,7450@2/pci1000,3060@3/sd@1,0:a 655 * 656 * For the -m option, used when we're trying to find the root device mapping: 657 * 658 * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2 659 * /dev/dsk/c3t500000E011637CF0d0s2 660 */ 661 static void 662 report_map(char *argdev, int physpath) 663 { 664 nvlist_t *thisdev; 665 int rv = 0; 666 char *thisdevid; 667 char *mpxpath = NULL; 668 char *prefixt = NULL; 669 char *prefixp = NULL; 670 char *stripdev = NULL; 671 char *slice = NULL; 672 boolean_t mpxenp; 673 uint_t slicelen = 0; 674 675 mpxenp = B_FALSE; 676 677 if ((prefixt = calloc(1, strlen(argdev) + 1)) == NULL) { 678 logmsg(MSG_INFO, "Unable to allocate memory\n"); 679 (void) printf("NOT_MAPPED\n"); 680 return; 681 } 682 683 (void) strlcpy(prefixt, argdev, strlen(argdev) + 1); 684 685 slice = strrchr(argdev, (physpath == NONBOOT) ? 's' : ':'); 686 if (slice != NULL) { 687 slicelen = strlen(slice); 688 if (slicelen > 3) 689 /* invalid size - max is 3 chars */ 690 slicelen = 0; 691 } 692 693 if ((stripdev = calloc(1, strlen(prefixt) + 1)) == NULL) { 694 logmsg(MSG_INFO, "Unable to allocate memory\n"); 695 (void) printf("NOT_MAPPED\n"); 696 free(prefixt); 697 return; 698 } 699 700 if ((strstr(prefixt, "/scsi_vhci") == NULL) && 701 (strstr(prefixt, "/pci") == NULL) && 702 (strstr(prefixt, "/sbus") == NULL)) { 703 prefixp = strrchr(prefixt, '/'); 704 (void) strlcpy(stripdev, 705 (prefixp == NULL) ? prefixt : prefixp + 1, 706 (prefixp == NULL) ? 707 strlen(prefixt) + 1: strlen(prefixp) + 1); 708 if (prefixp != NULL) 709 prefixt[strlen(argdev) - strlen(prefixp) + 1] = '\0'; 710 } else { 711 if ((physpath != BOOT) && 712 (physpath != BOOT_PATH)) { 713 logmsg(MSG_INFO, "Invalid device path provided\n"); 714 (void) printf("NOT_MAPPED\n"); 715 free(stripdev); 716 free(prefixt); 717 return; 718 } 719 (void) strlcpy(stripdev, argdev, strlen(argdev) + 1); 720 } 721 722 logmsg(MSG_INFO, 723 "stripdev (%s), prefixt(%s), prefixp(%s), slice(%s)\n", 724 (stripdev == NULL) ? "null" : stripdev, 725 (prefixt == NULL) ? "null" : prefixt, 726 (prefixp == NULL) ? "null" : prefixp, 727 (slice == NULL) ? "null" : slice); 728 729 if (slicelen > 0) 730 stripdev[strlen(stripdev) - slicelen] = '\0'; 731 732 /* search for the shortened version */ 733 rv = nvlist_lookup_string(mapnvl, stripdev, &thisdevid); 734 if (rv) { 735 if ((physpath != BOOT) && 736 (physpath != BOOT_PATH)) { 737 logmsg(MSG_INFO, 738 "searched mapnvl for '%s', got %s (%d)\n", 739 stripdev, strerror(rv), rv); 740 (void) printf("NOT_MAPPED\n"); 741 free(stripdev); 742 free(prefixt); 743 return; 744 } 745 } 746 747 logmsg(MSG_INFO, "device %s has devid %s\n", stripdev, thisdevid); 748 749 if (nvlist_lookup_nvlist(mapnvl, thisdevid, &thisdev) != 0) { 750 logmsg(MSG_INFO, "device (%s) in mapnvl but " 751 "not mapped!\n", thisdevid); 752 (void) printf("NOT_MAPPED\n"); 753 free(stripdev); 754 free(prefixt); 755 return; 756 } 757 758 /* quick exit */ 759 if (!mpxenabled && (strstr(argdev, "/pci") != NULL || 760 strstr(argdev, "/sbus") != NULL)) { 761 (void) printf("%s\n", argdev); 762 free(stripdev); 763 free(prefixt); 764 return; 765 } 766 767 (void) nvlist_lookup_boolean_value(thisdev, NVL_MPXEN, &mpxenp); 768 769 if (physpath == BOOT) { 770 (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath); 771 if ((strstr(argdev, "/scsi_vhci") != NULL) && 772 (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) { 773 /* Need to translate vhci to phci */ 774 vhci_to_phci(stripdev, slice, DISPLAY_ONE_PATH); 775 } else { 776 (void) printf("%s%s\n", mpxpath, 777 ((slicelen > 0) && slice != NULL) ? slice : ""); 778 } 779 } else if (physpath == BOOT_PATH) { 780 (void) nvlist_lookup_string(thisdev, NVL_PHYSPATH, &mpxpath); 781 if ((strstr(argdev, "/scsi_vhci") != NULL) && 782 (strncmp(argdev, mpxpath, strlen(mpxpath)) == 0)) { 783 /* Need to translate vhci to phci */ 784 vhci_to_phci(stripdev, slice, DISPLAY_ALL_PATH); 785 } else { 786 (void) printf("%s%s\n", mpxpath, 787 ((slicelen > 0) && slice != NULL) ? slice : ""); 788 } 789 } else { 790 (void) nvlist_lookup_string(thisdev, 791 ((readonlyroot) ? NVL_PHYSPATH : 792 ((mpxenp == B_TRUE) ? NVL_MPXPATH : NVL_PATH)), 793 &mpxpath); 794 logmsg(MSG_INFO, "mpxpath = %s\n", 795 (mpxpath == NULL) ? "null" : mpxpath); 796 if (readonlyroot || 797 (strstr(mpxpath, "/scsi_vhci") != NULL) || 798 (strstr(mpxpath, "/pci") != NULL) || 799 (strstr(mpxpath, "/sbus") != NULL)) { 800 /* 801 * If we see a physical path here it means that 802 * devlinks aren't fully initialised yet, so we 803 * are still in maintenance/single-user mode. 804 */ 805 (void) printf("/devices%s:%c\n", mpxpath, 806 slice[1] + '1'); 807 } else { 808 (void) printf("%s%s%s\n", 809 (prefixt[0] == '/') ? prefixt : "", 810 mpxpath, 811 ((slicelen > 0) && slice != NULL) ? slice : ""); 812 } 813 } 814 free(prefixt); 815 free(stripdev); 816 } 817 818 /* 819 * Validate the in-kernel and on-disk forms of our devid cache, 820 * returns -1 for unfixable error and 0 for success. 821 */ 822 static int 823 validate_devnvl() 824 { 825 di_node_t curnode; 826 int rv1 = -1; 827 int rv2 = -1; 828 829 /* 830 * Method: we walk through the kernel's concept of the device tree 831 * looking for "ssd" then "sd" nodes. 832 * We check to see whether the device's devid is already in our nvlist 833 * (on disk) nvlist cache file. If it is, we check that it's components 834 * match what we've got already and fill any missing fields. 835 * If the devid isn't in our on-disk nvlist already then we add it 836 * and populate the property nvpairs. 837 * 838 * At the end of this function we should have this program's concept 839 * of the devid-keyed nvlist matching what is in the ondisk form which 840 * is ready to be written out. 841 * If we can't do this, then we return -1. 842 */ 843 curnode = di_drv_first_node("ssd", devinfo_root); 844 if (curnode != DI_NODE_NIL) 845 rv1 = mpxio_nvl_boilerplate(curnode); 846 847 curnode = di_drv_first_node("sd", devinfo_root); 848 if (curnode != DI_NODE_NIL) 849 rv2 = mpxio_nvl_boilerplate(curnode); 850 851 if (rv1 + rv2 == -2) 852 return (-1); 853 854 return (0); 855 } 856 857 /* 858 * According to devfs path name, it will print device node name. 859 */ 860 static void 861 print_node_name(char *drv_name, char *strdevfspath) 862 { 863 di_node_t curnode; 864 char *devfspath = NULL; 865 char *node_name = NULL; 866 867 curnode = di_drv_first_node(drv_name, devinfo_root); 868 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) { 869 devfspath = di_devfs_path(curnode); 870 logmsg(MSG_INFO, "find: devfspath %s\n", devfspath); 871 872 if (devfspath == NULL) 873 continue; 874 875 if ((strlen(strdevfspath) == strlen(devfspath)) && 876 (strncmp(strdevfspath, devfspath, 877 strlen(devfspath)) == 0)) { 878 node_name = find_link(curnode); 879 if (node_name == NULL) { 880 (void) printf("NOT MAPPED\n"); 881 } else { 882 (void) printf("%s\n", node_name); 883 } 884 return; 885 } 886 } 887 } 888 889 /* 890 * report device node name, search "ssd" and "sd" nodes, 891 * print the device node name which device path is same as 892 * parameter. 893 */ 894 static void 895 report_dev_node_name(char *strdevfspath) 896 { 897 logmsg(MSG_INFO, "strdevfspath: %s\n", strdevfspath); 898 print_node_name("ssd", strdevfspath); 899 print_node_name("sd", strdevfspath); 900 } 901 902 static int 903 mpxio_nvl_boilerplate(di_node_t curnode) 904 { 905 int rv; 906 char *strdevid; 907 ddi_devid_t curdevid; 908 nvlist_t *newnvl; 909 910 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) { 911 errno = 0; 912 913 curdevid = NULL; 914 get_devid(curnode, &curdevid); 915 if (curdevid == NULL) 916 /* 917 * There's no devid registered for this device 918 * so it's not cool enough to play with us 919 */ 920 continue; 921 922 strdevid = devid_str_encode(curdevid, NULL); 923 /* does this exist in the on-disk cache? */ 924 rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl); 925 if (rv == ENOENT) { 926 logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid); 927 /* no, so alloc a new nvl to store it */ 928 if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) { 929 logmsg(MSG_ERROR, 930 gettext("Unable to allocate space for " 931 "a devid property list: %s\n"), 932 strerror(errno)); 933 return (-1); 934 } 935 } else { 936 if ((rv != ENOTSUP) && (rv != EINVAL)) 937 logmsg(MSG_INFO, 938 "%s exists in ondisknvl, verifying\n", 939 strdevid); 940 } 941 942 if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) { 943 logmsg(MSG_ERROR, 944 gettext("Unable to populate devid nvpair " 945 "for device with devid %s\n"), 946 strdevid); 947 devid_str_free(strdevid); 948 nvlist_free(newnvl); 949 return (-1); 950 } 951 952 /* Now add newnvl into our cache. */ 953 errno = 0; 954 rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl); 955 if (rv) { 956 logmsg(MSG_ERROR, 957 gettext("Unable to add device (devid %s) " 958 "to in-kernel nvl: %s (%d)\n"), 959 strdevid, strerror(rv), rv); 960 devid_str_free(strdevid); 961 nvlist_free(newnvl); 962 return (-1); 963 } 964 logmsg(MSG_INFO, 965 gettext("added device (devid %s) to mapnvl\n\n"), 966 strdevid); 967 devid_str_free(strdevid); 968 } 969 return (0); 970 } 971 972 /* 973 * Operates on a single di_node_t, collecting all the device properties 974 * that we need. devnvl is allocated by the caller, and we add our nvpairs 975 * to it if they don't already exist. 976 * 977 * We are _only_ interested in devices which have a devid. We pull in 978 * devices even when they're excluded via stmsboot -D (driver), because 979 * we don't want to miss out on any devid data that might be handy later. 980 */ 981 static int 982 popcheck_devnvl(di_node_t thisnode, nvlist_t *devnvl, char *strdevid) 983 { 984 char *path = NULL; 985 char *curpath = NULL; 986 char *devfspath = NULL; 987 char *prop = NULL; 988 int scsivhciparent = 0; 989 int rv = 0; 990 boolean_t mpxenp = B_FALSE; 991 992 errno = 0; 993 devfspath = di_devfs_path(thisnode); 994 if (devfspath == NULL) { 995 logmsg(MSG_ERROR, 996 gettext("Unable to determine devfs path for node: %s\n"), 997 strerror(errno)); 998 return (-1); 999 } 1000 1001 /* Add a convenient devfspath to devid inverse map */ 1002 if (nvlist_add_string(mapnvl, devfspath, strdevid) != 0) { 1003 logmsg(MSG_ERROR, 1004 gettext("Unable to add device path %s with devid " 1005 "%s to mapnvl\n"), devfspath, strdevid); 1006 return (-1); 1007 } 1008 if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(thisnode), 1009 "mpxio-disable", &prop) >= 0) { 1010 if (strncmp(prop, "yes", 3) == 0) { 1011 if (!mpxprop) 1012 mpxprop++; 1013 } 1014 } 1015 1016 if (strncmp(di_driver_name(di_parent_node(thisnode)), 1017 "scsi_vhci", 9) == 0) { 1018 scsivhciparent = 1; 1019 if (!mpxenabled) 1020 mpxenabled++; 1021 1022 rv = nvlist_lookup_boolean_value(devnvl, NVL_MPXEN, &mpxenp); 1023 if (rv || (mpxenp == B_FALSE)) { 1024 rv = nvlist_add_boolean_value(devnvl, 1025 NVL_MPXEN, B_TRUE); 1026 if (rv) { 1027 logmsg(MSG_ERROR, 1028 gettext("Unable to add property %s " 1029 "(set to B_TRUE) for device %s: " 1030 "%s (%d)\n"), 1031 NVL_MPXEN, devfspath, 1032 strerror(rv), rv); 1033 return (-1); 1034 } 1035 logmsg(MSG_INFO, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n"); 1036 } 1037 } else { 1038 /* turn _off_ the flag if it was enabled */ 1039 rv = nvlist_add_boolean_value(devnvl, NVL_MPXEN, B_FALSE); 1040 if (rv) { 1041 logmsg(MSG_ERROR, 1042 gettext("Unable to add property %s " 1043 "(set to B_FALSE) for device %s: %s (%d)\n"), 1044 NVL_MPXEN, devfspath, 1045 strerror(rv), rv); 1046 return (-1); 1047 } 1048 logmsg(MSG_INFO, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n"); 1049 } 1050 1051 rv = nvlist_add_string(devnvl, NVL_PHYSPATH, devfspath); 1052 if (rv) { 1053 logmsg(MSG_ERROR, 1054 gettext("Unable to add physical device path (%s) " 1055 "property to nvl\n")); 1056 return (-1); 1057 } 1058 1059 if ((curpath = calloc(1, MAXPATHLEN)) == NULL) { 1060 logmsg(MSG_ERROR, 1061 gettext("Unable to allocate space for current path\n")); 1062 return (-1); 1063 } 1064 curpath = find_link(thisnode); 1065 if (curpath == NULL) { 1066 if (readonlyroot) { 1067 return (0); 1068 } 1069 logmsg(MSG_ERROR, 1070 gettext("Unable to determine device path for node %s\n"), 1071 devfspath); 1072 return (-1); 1073 } 1074 1075 rv = nvlist_lookup_string(devnvl, NVL_MPXPATH, &path); 1076 1077 if (scsivhciparent) { 1078 (void) nvlist_add_string(devnvl, NVL_MPXPATH, curpath); 1079 } else { 1080 (void) nvlist_add_string(devnvl, NVL_PATH, curpath); 1081 path = curpath; 1082 } 1083 1084 /* 1085 * This next block provides the path to devid inverse mapping 1086 * that other functions require 1087 */ 1088 if (path != NULL) { 1089 if (nvlist_add_string(mapnvl, path, strdevid) != 0) { 1090 logmsg(MSG_ERROR, 1091 gettext("Unable to add device %s with devid " 1092 "%s to mapnvl\n"), path, strdevid); 1093 return (-1); 1094 } 1095 logmsg(MSG_INFO, "popcheck_devnvl: added path %s :: %s\n", 1096 path, strdevid); 1097 } 1098 1099 if (nvlist_add_string(mapnvl, curpath, strdevid) != 0) { 1100 logmsg(MSG_ERROR, 1101 gettext("Unable to add device %s with devid " 1102 "%s to mapnvl: %s\n"), 1103 curpath, strdevid, strerror(errno)); 1104 return (-1); 1105 } 1106 logmsg(MSG_INFO, "popcheck_devnvl: added curpath %s :: %s\n", 1107 curpath, strdevid); 1108 1109 return (0); 1110 } 1111 1112 static void 1113 print_mpx_capable(di_node_t curnode) 1114 { 1115 char *prop; 1116 char *path; 1117 char *aliases = NULL; 1118 1119 if (cap_N_option) { 1120 aliases = calloc(1, MAXPATHLEN + 1); 1121 if (aliases == NULL) { 1122 logmsg(MSG_ERROR, 1123 gettext("Unable to allocate memory for a device " 1124 "alias list\n")); 1125 return; 1126 } 1127 } 1128 1129 for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) { 1130 if (di_prop_lookup_strings(DDI_DEV_T_ANY, curnode, 1131 "initiator-port", &prop) >= 0) { 1132 if ((path = di_devfs_path(curnode)) == NULL) { 1133 logmsg(MSG_INFO, 1134 "Unable to find devfs path for device " 1135 "%s: %s\n", &curnode, strerror(errno)); 1136 continue; 1137 } 1138 if (cap_N_option) { 1139 char *nodename = di_node_name(curnode); 1140 /* nodename is never going to be null */ 1141 if (strstr(aliases, nodename) == NULL) 1142 /* haven't seen this nodename before */ 1143 (void) snprintf(aliases, 1144 MAXPATHLEN + 1, "%s|%s", 1145 ((aliases != NULL) ? aliases : ""), 1146 nodename); 1147 } else 1148 (void) printf("%s\n", path); 1149 } 1150 } 1151 if (cap_N_option) 1152 (void) printf("%s\n", aliases); 1153 } 1154 1155 static int 1156 link_cb(di_devlink_t devlink, void *arg) 1157 { 1158 const char *result; 1159 1160 result = di_devlink_path(devlink); 1161 if (result == NULL) { 1162 arg = (void *)"(null)"; 1163 } else { 1164 (void) strlcpy(arg, result, strlen(result)); 1165 } 1166 logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n", 1167 ((result != NULL) ? result : "(null)")); 1168 return (DI_WALK_CONTINUE); 1169 } 1170 1171 static char * 1172 find_link(di_node_t cnode) 1173 { 1174 di_minor_t devminor = DI_MINOR_NIL; 1175 di_devlink_handle_t hdl; 1176 char *devfspath = NULL; 1177 char *minorpath = NULL; 1178 char *linkname = NULL; 1179 char *cbresult = NULL; 1180 1181 devfspath = di_devfs_path(cnode); 1182 if (cnode == DI_NODE_NIL) { 1183 logmsg(MSG_ERROR, 1184 gettext("find_ctrl must be called with non-null " 1185 "di_node_t\n")); 1186 return (NULL); 1187 } 1188 logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath); 1189 1190 if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) || 1191 ((minorpath = calloc(1, MAXPATHLEN)) == NULL) || 1192 ((linkname = calloc(1, MAXPATHLEN)) == NULL)) { 1193 logmsg(MSG_ERROR, "unable to allocate space for dev link\n"); 1194 return (NULL); 1195 } 1196 1197 devminor = di_minor_next(cnode, devminor); 1198 hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK); 1199 if (hdl == NULL) { 1200 logmsg((readonlyroot ? MSG_INFO : MSG_ERROR), 1201 gettext("unable to take devlink snapshot: %s\n"), 1202 strerror(errno)); 1203 return (NULL); 1204 } 1205 1206 linkname = "^dsk/"; 1207 (void) snprintf(minorpath, MAXPATHLEN, "%s:c", devfspath); 1208 1209 errno = 0; 1210 if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK, 1211 (void *)cbresult, link_cb) < 0) { 1212 logmsg(MSG_ERROR, 1213 gettext("Unable to walk devlink snapshot for %s: %s\n"), 1214 minorpath, strerror(errno)); 1215 return (NULL); 1216 } 1217 1218 if (di_devlink_fini(&hdl) < 0) { 1219 logmsg(MSG_ERROR, 1220 gettext("Unable to close devlink snapshot: %s\n"), 1221 strerror(errno)); 1222 } 1223 if (strstr(cbresult, "dsk/") == NULL) 1224 return (devfspath); 1225 1226 bzero(minorpath, MAXPATHLEN); 1227 /* strip off the trailing "s2" */ 1228 bcopy(cbresult, minorpath, strlen(cbresult) - 1); 1229 /* Now strip off the /dev/dsk/ prefix for output flexibility */ 1230 linkname = strrchr(minorpath, '/'); 1231 return (++linkname); 1232 } 1233 1234 /* 1235 * handle case where device has been probed but its target driver is not 1236 * attached so enumeration has not quite finished. Opening the /devices 1237 * pathname will force the kernel to finish the enumeration process and 1238 * let us get the data we need. 1239 */ 1240 static void 1241 get_devid(di_node_t node, ddi_devid_t *thisdevid) 1242 { 1243 int fd; 1244 char realpath[MAXPATHLEN]; 1245 char *openpath = di_devfs_path(node); 1246 1247 errno = 0; 1248 bzero(realpath, MAXPATHLEN); 1249 if (strstr(openpath, "/devices") == NULL) { 1250 (void) snprintf(realpath, MAXPATHLEN, 1251 "/devices%s:c,raw", openpath); 1252 fd = open(realpath, O_RDONLY|O_NDELAY); 1253 } else { 1254 fd = open(openpath, O_RDONLY|O_NDELAY); 1255 } 1256 1257 if (fd < 0) { 1258 logmsg(MSG_INFO, "Unable to open path %s: %s\n", 1259 openpath, strerror(errno)); 1260 return; 1261 } 1262 1263 if (devid_get(fd, thisdevid) != 0) { 1264 logmsg(MSG_INFO, 1265 "'%s' node (%s) without a devid registered\n", 1266 di_driver_name(node), di_devfs_path(node)); 1267 } 1268 (void) close(fd); 1269 } 1270 1271 static int 1272 print_bootpath() 1273 { 1274 char *bootprop = NULL; 1275 1276 if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root, 1277 "bootpath", &bootprop) >= 0) { 1278 (void) printf("%s\n", bootprop); 1279 return (0); 1280 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, devinfo_root, 1281 "boot-path", &bootprop) >= 0) { 1282 (void) printf("%s\n", bootprop); 1283 return (0); 1284 } else { 1285 (void) printf("ERROR: no bootpath/boot-path property found\n"); 1286 return (ENOENT); 1287 } 1288 } 1289 1290 static void 1291 get_phci_driver_name(char *phci_path, char **driver_name) 1292 { 1293 di_node_t phci_node = DI_NODE_NIL; 1294 char *tmp = NULL; 1295 1296 phci_node = di_init(phci_path, DINFOCPYONE); 1297 if (phci_node == DI_NODE_NIL) { 1298 logmsg(MSG_ERROR, 1299 gettext("Unable to take phci snapshot " 1300 "(%s: %d)\n"), strerror(errno), errno); 1301 return; 1302 } 1303 tmp = di_driver_name(phci_node); 1304 if (tmp != NULL) { 1305 (void) strncpy(*driver_name, tmp, 10); 1306 } 1307 di_fini(phci_node); 1308 } 1309 1310 /* 1311 * We only call this routine if we have a scsi_vhci node and must 1312 * determine the actual physical path of its first online client 1313 * path. 1314 */ 1315 static void 1316 vhci_to_phci(char *devpath, char *slice, int d_flag) 1317 { 1318 sv_iocdata_t ioc; 1319 sv_path_info_t *pi; 1320 int vhci_fd; 1321 int rv; 1322 uint_t npaths = 0; 1323 char nodename[MAXPATHLEN]; 1324 char *phci_driver = NULL; 1325 1326 vhci_fd = open(VHCI_CTL_NODE, O_RDWR); 1327 if (vhci_fd < 0) 1328 goto failure; 1329 1330 bzero(&ioc, sizeof (sv_iocdata_t)); 1331 ioc.client = devpath; 1332 ioc.ret_elem = &npaths; 1333 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); 1334 if (rv || npaths == 0) { 1335 logmsg(MSG_INFO, 1336 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, " 1337 "%s (%d)\n", strerror(rv), rv); 1338 goto failure; 1339 } 1340 1341 bzero(&ioc, sizeof (sv_iocdata_t)); 1342 ioc.client = devpath; 1343 ioc.buf_elem = npaths; 1344 ioc.ret_elem = &npaths; 1345 if ((ioc.ret_buf = calloc(npaths, sizeof (sv_path_info_t))) 1346 == NULL) 1347 goto failure; 1348 rv = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc); 1349 if (rv || npaths == 0) { 1350 logmsg(MSG_INFO, 1351 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) " 1352 "failed, %s (%d)\n", strerror(rv), rv); 1353 free(ioc.ret_buf); 1354 goto failure; 1355 } 1356 1357 if (ioc.buf_elem < npaths) 1358 npaths = ioc.buf_elem; 1359 1360 phci_driver = malloc(10); 1361 if (phci_driver == NULL) { 1362 logmsg(MSG_INFO, 1363 "vhci_to_phci: Memory allocation failed\n"); 1364 free(ioc.ret_buf); 1365 goto failure; 1366 } 1367 1368 pi = (sv_path_info_t *)ioc.ret_buf; 1369 while (npaths--) { 1370 bzero(nodename, MAXPATHLEN); 1371 bzero(phci_driver, 10); 1372 1373 get_phci_driver_name(pi->device.ret_phci, 1374 &phci_driver); 1375 logmsg(MSG_INFO, "phci driver name: %s\n", phci_driver); 1376 /* 1377 * A hack, but nicer than a platform-specific ifdef 1378 * fp on SPARC using "ssd" as nodename 1379 * mpt use "sd" when mpxio disabled, use "disk" when 1380 * mpxio is enabled 1381 * for alll other cases, "disk" should be used as the 1382 * nodename 1383 */ 1384 if (strstr(devpath, "ssd") != NULL) { 1385 (void) snprintf(nodename, 5, "ssd"); 1386 } else if (strncmp(phci_driver, "mpt", 10) == 0) { 1387 (void) snprintf(nodename, 5, "sd"); 1388 } else { 1389 (void) snprintf(nodename, 5, "disk"); 1390 } 1391 if ((d_flag == DISPLAY_ONE_PATH) && 1392 (pi->ret_state == MDI_PATHINFO_STATE_ONLINE)) { 1393 (void) printf("%s/%s@%s", pi->device.ret_phci, 1394 nodename, pi->ret_addr); 1395 if ((slice != NULL) && (strlen(slice) <= 3)) { 1396 (void) printf("%s\n", slice); 1397 } else { 1398 (void) printf("\n"); 1399 } 1400 break; 1401 } else if (d_flag == DISPLAY_ALL_PATH) { 1402 (void) printf("%s/%s@%s", pi->device.ret_phci, 1403 nodename, pi->ret_addr); 1404 if ((slice != NULL) && (strlen(slice) <= 3)) { 1405 (void) printf("%s\n", slice); 1406 } else { 1407 (void) printf("\n"); 1408 } 1409 } 1410 pi++; 1411 } 1412 free(ioc.ret_buf); 1413 free(phci_driver); 1414 return; 1415 1416 failure: 1417 (void) printf("NOT_MAPPED\n"); 1418 } 1419 1420 /* 1421 * Write /etc/vfstab to /etc/vfstab.new, with any remapped device 1422 * names substituted. 1423 * 1424 * Returns: 1425 * 0 successful operation 1426 * -1 failed 1427 */ 1428 static int 1429 update_vfstab() 1430 { 1431 FILE *fdin, *fdout; 1432 char *buf, *tmpbuf; 1433 char fname[MAXPATHLEN]; 1434 int rv = -1, rval = -1; 1435 char cdev[MAXPATHLEN]; 1436 char bdev[MAXPATHLEN]; 1437 char mntpt[MAXPATHLEN]; 1438 char fstype[512]; 1439 char fsckpass[512]; 1440 char mntboot[512]; 1441 char mntopt[MAXPATHLEN]; 1442 char fmt[80]; 1443 char *prefixt = NULL; 1444 char *curdev = NULL; 1445 char *thisdevid = NULL; 1446 char *slice = NULL; 1447 nvlist_t *thisdev; 1448 boolean_t devmpx = B_FALSE; 1449 1450 buf = calloc(1, MAXPATHLEN); 1451 tmpbuf = calloc(1, MAXPATHLEN); 1452 if (buf == NULL || tmpbuf == NULL) 1453 return (-1); 1454 1455 (void) snprintf(fname, MAXPATHLEN, "/etc/mpxio/vfstab.new"); 1456 1457 fdin = fopen("/etc/vfstab", "r"); 1458 fdout = fopen(fname, "w+"); 1459 if (fdin == NULL || fdout == NULL) { 1460 logmsg(MSG_INFO, "Unable to open vfstab or create a backup " 1461 "vfstab %s\n"); 1462 return (-1); 1463 } 1464 1465 (void) snprintf(fmt, sizeof (fmt), 1466 "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1, 1467 sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1, 1468 sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1); 1469 1470 while (fgets(buf, MAXPATHLEN, fdin) != NULL) { 1471 if (strlen(buf) == (MAXPATHLEN - 1) && 1472 buf[MAXPATHLEN-2] != '\n') { 1473 logmsg(MSG_ERROR, 1474 gettext("/etc/vfstab line length too long, " 1475 "exceeded %2$d: \"%3$s\"\n"), 1476 MAXPATHLEN - 2, buf); 1477 goto out; 1478 } 1479 1480 prefixt = NULL; 1481 curdev = NULL; 1482 slice = NULL; 1483 thisdevid = NULL; 1484 thisdev = NULL; 1485 1486 /* LINTED - variable format specifier */ 1487 rv = sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass, 1488 mntboot, mntopt); 1489 1490 /* 1491 * Walk through the lines in the input file (/etc/vfstab), 1492 * skipping anything which is _not_ a COGD (common or garden 1493 * disk), ie all the /devices, /system, /dev/md, /dev/vx and 1494 * /dev/zvol and so forth. 1495 */ 1496 if ((rv == 7) && (bdev[0] == '/') && 1497 (strstr(bdev, "/dev/dsk"))) { 1498 slice = strrchr(bdev, 's'); 1499 /* take a copy, strip off /dev/dsk/ */ 1500 prefixt = strrchr(bdev, 'c'); 1501 prefixt[strlen(bdev) - 9 - strlen(slice)] = '\0'; 1502 slice++; /* advance past the s */ 1503 rval = nvlist_lookup_string(mapnvl, prefixt, 1504 &thisdevid); 1505 if (rval) { 1506 /* Whoa, where did this device go?! */ 1507 logmsg(MSG_INFO, 1508 "error looking up device %s\n", prefixt); 1509 /* Comment-out this line in the new version */ 1510 (void) snprintf(tmpbuf, MAXPATHLEN, 1511 "# DEVICE NOT FOUND %s", buf); 1512 (void) fprintf(fdout, "%s", tmpbuf); 1513 continue; 1514 } else { 1515 /* The device exists in our mapnvl */ 1516 (void) nvlist_lookup_nvlist(mapnvl, thisdevid, 1517 &thisdev); 1518 (void) nvlist_lookup_boolean_value(thisdev, 1519 NVL_MPXEN, &devmpx); 1520 (void) nvlist_lookup_string(thisdev, 1521 ((devmpx == B_TRUE) 1522 ? NVL_MPXPATH : NVL_PATH), 1523 &curdev); 1524 } 1525 } 1526 1527 if ((prefixt != NULL) && (curdev != NULL) && 1528 (rv = (strncmp(prefixt, curdev, strlen(prefixt)) != 0))) { 1529 /* Mapping change for this device */ 1530 if (strcmp(fstype, "swap") == 0) { 1531 (void) snprintf(tmpbuf, MAXPATHLEN, 1532 "/dev/dsk/%ss%s\t-\t-\tswap\t" 1533 "%s\t%s\t%s\n", 1534 curdev, slice, fsckpass, mntboot, mntopt); 1535 } else { 1536 (void) snprintf(tmpbuf, MAXPATHLEN, 1537 "/dev/dsk/%ss%s\t/dev/rdsk/%ss%s\t" 1538 "%s\t%s\t%s\t%s\t%s\n", 1539 curdev, slice, curdev, slice, 1540 mntpt, fstype, fsckpass, mntboot, mntopt); 1541 } 1542 errno = 0; 1543 (void) fprintf(fdout, "%s", tmpbuf); 1544 } else { 1545 (void) fprintf(fdout, "%s", buf); 1546 } 1547 1548 errno = 0; 1549 if (fflush(fdout) != 0) { 1550 logmsg(MSG_ERROR, 1551 gettext("fprintf failed to write to %s: %s (%d)\n"), 1552 fname, strerror(errno), errno); 1553 goto out; 1554 } 1555 } 1556 out: 1557 (void) fclose(fdin); 1558 (void) fclose(fdout); 1559 free(buf); 1560 free(tmpbuf); 1561 return (errno); 1562 } 1563