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.
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.
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]
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
29 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <libdevinfo.h>
41 #include <sys/libdevid.h>
42 #include <sys/modctl.h> /* for MAXMODCONFNAME */
43 #include <sys/scsi/adapters/scsi_vhci.h>
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.
50 #define SAVE_DIR "/etc/mpxio"
51 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl"
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"
62 #define MPX_CAPABLE_CTRL 0x04
63 #define MPX_DEV_PATH 0x06
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
71 #define MSG_ERROR 0x02
72 #define MSG_PANIC 0x04
74 #define BOOT_PATH 0x02
78 #define DISPLAY_ONE_PATH 0x00
79 #define DISPLAY_ALL_PATH 0x01
81 static di_node_t devinfo_root
= DI_NODE_NIL
;
82 static char *ondiskname
= "/etc/mpxio/devid_path.cache";
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.
89 static nvlist_t
*mapnvl
;
90 static int mpxenabled
= 0;
91 static int limctrl
= -1;
92 static int mpxprop
= 0;
94 static char *drvlimit
;
95 static int globarg
= 0;
96 static int debugflag
= 0;
98 static int readonlyroot
= 0;
99 static int cap_N_option
= 0;
101 static void print_mpx_capable(di_node_t curnode
);
102 static int popcheck_devnvl(di_node_t thisnode
, nvlist_t
*devnvl
,
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
);
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
);
119 main(int argc
, char **argv
)
121 struct stat cachestat
;
127 parse_args(argc
, argv
);
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
) {
134 gettext("Unable to take device tree snapshot "
135 "(%s: %d)\n"), strerror(errno
), errno
);
138 logmsg(MSG_INFO
, "opened root di_node\n");
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
,
146 print_mpx_capable(di_drv_first_node("fp",
148 print_mpx_capable(di_drv_first_node("mpt",
150 print_mpx_capable(di_drv_first_node("mpt_sas",
152 print_mpx_capable(di_drv_first_node("pmcs",
155 di_fini(devinfo_root
);
159 mapfd
= open(ondiskname
, O_RDWR
|O_CREAT
|O_SYNC
, S_IRUSR
| S_IWUSR
);
161 /* we could be in single-user, so try for RO */
162 if ((mapfd
= open(ondiskname
, O_RDONLY
)) < 0) {
164 gettext("Unable to open or create %s:%s\n"),
165 ondiskname
, strerror(errno
));
171 if (stat(ondiskname
, &cachestat
) != 0) {
173 gettext("Unable to stat() %s: %s\n"),
174 ondiskname
, strerror(errno
));
177 ondiskbuf
= calloc(1, cachestat
.st_size
);
178 if (ondiskbuf
== NULL
) {
180 gettext("Unable to allocate memory for the devid "
181 "cache file: %s\n"), strerror(errno
));
184 rv
= read(mapfd
, ondiskbuf
, cachestat
.st_size
);
185 if (rv
!= cachestat
.st_size
) {
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
));
193 rv
= nvlist_unpack(ondiskbuf
, cachestat
.st_size
, &mapnvl
, 0);
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) {
200 gettext("Unable to allocate root property"
207 if (validate_devnvl() < 0) {
209 gettext("unable to validate kernel with on-disk devid "
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.
223 rv
= nvlist_size(mapnvl
, &newsz
, NV_ENCODE_NATIVE
);
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"));
235 if ((ondiskbuf
= calloc(1, newsz
)) == NULL
) {
237 "Unable to allocate space for writing out new "
238 "on-disk devid cache file: %s\n", strerror(errno
));
244 rv
= nvlist_pack(mapnvl
, &ondiskbuf
, &newsz
,
245 NV_ENCODE_NATIVE
, 0);
248 gettext("Unable to pack on-disk devid cache "
249 "file: %s (%d)\n"), strerror(rv
), rv
);
256 rv
= lseek(mapfd
, 0, 0);
259 gettext("Unable to seek to start of devid cache "
260 "file: %s (%d)\n"), strerror(errno
), errno
);
267 if (write(mapfd
, ondiskbuf
, newsz
) != newsz
) {
269 gettext("Unable to completely write out "
270 "on-disk devid cache file: %s\n"), strerror(errno
));
276 } /* !readonlyroot */
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
);
285 } else if (globarg
== MPX_UPDATEVFSTAB
) {
286 rv
= update_vfstab();
287 di_fini(devinfo_root
);
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
);
297 if (globarg
== MPX_MAP
)
298 report_map(devicep
, NONBOOT
);
300 logmsg(MSG_INFO
, "\nprivate devid cache file initialised\n");
304 di_fini(devinfo_root
);
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 "
318 (void) fprintf(stderr
, gettext("\t-m devname\n"));
319 (void) fprintf(stderr
, gettext("\t\tReports the current mapping for "
321 (void) fprintf(stderr
, gettext("\t-g\tprint the GUID for MPxIO-capable "
323 (void) fprintf(stderr
, gettext("\t\toption is only valid with the -L "
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 "
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 "
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"));
356 parse_args(int argc
, char *argv
[])
364 * -b prints the bootpath property
365 * -d turns on debug mode for this utility (copious output!)
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.
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.
378 * prints the device path (/dev/rdsk) that devname maps to
379 * in the currently-running system.
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.
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).
390 * prints the physical devfs path for devname. Only used to
391 * determine the bootpath.
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.
397 while ((opt
= getopt(argc
, argv
, "bdD:ghil:Lm:nNo:p:q:u")) != EOF
) {
400 globarg
= MPX_BOOTPATH
;
406 if ((drvlimit
= calloc(1, MAXMODCONFNAME
)) == NULL
) {
408 gettext("Unable to allocate memory for a "
409 "driver name: %s\n"), strerror(errno
));
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
)) {
419 gettext("invalid parent driver (%s) "
420 "specified"), drvlimit
);
425 /* Just drop out and print the usage() output */
433 limctrl
= (int)atol(optarg
);
436 gettext("invalid controller number "
437 "(%d), checking all controllers\n"),
449 if ((devicep
= calloc(1, MAXPATHLEN
)) == NULL
) {
451 gettext("Unable to allocate space for a "
455 devicep
= strdup(optarg
);
459 globarg
= MPX_CAPABLE_CTRL
;
462 globarg
= MPX_CAPABLE_CTRL
;
465 globarg
= MPX_GETPATH
;
466 if ((devicep
= calloc(1, MAXPATHLEN
)) == NULL
) {
468 gettext("Unable to allocate space for a "
472 devicep
= strdup(optarg
);
475 globarg
= MPX_PHYSICAL
;
476 if ((devicep
= calloc(1, MAXPATHLEN
)) == NULL
) {
478 gettext("Unable to allocate space for a "
482 devicep
= strdup(optarg
);
485 globarg
= MPX_DEV_PATH
;
486 if ((devicep
= calloc(1, MAXPATHLEN
)) == NULL
) {
488 gettext("Unable to allocate space for a "
492 devicep
= strdup(optarg
);
495 globarg
= MPX_UPDATEVFSTAB
;
499 gettext("Invalid command line option (%c)\n"),
505 if ((globarg
>= MPX_USAGE
) || (guid
&& (globarg
!= MPX_LIST
)))
508 if ((drvlimit
!= NULL
) &&
509 ((globarg
!= MPX_LIST
) &&
510 (globarg
!= MPX_CAPABLE_CTRL
)))
515 logmsg(int level
, const char *msg
, ...)
519 if ((level
>= MSG_ERROR
) ||
520 ((debugflag
> 0) && (level
>= MSG_INFO
))) {
521 (void) fprintf(stdout
, "stmsboot: ");
523 (void) vfprintf(stdout
, msg
, ap
);
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.
537 list_devs(int listguids
, int ctrl
)
539 nvlist_t
*thisdevnvl
;
541 char *diskpath
, *livepath
, *key
, *querydev
;
542 char *matchctrl
= NULL
;
543 char checkctrl
[MAXPATHLEN
];
548 logmsg(MSG_ERROR
, gettext("MPXIO disabled\n"));
550 logmsg(MSG_ERROR
, gettext("No STMS devices have "
557 (void) printf(gettext("non-STMS device name\t\t\tGUID\n"
558 "------------------------------------------"
559 "------------------------\n"));
561 (void) printf(gettext("non-STMS device name\t\t\t"
563 "------------------------------------------"
564 "------------------------\n"));
567 bzero(checkctrl
, MAXPATHLEN
);
569 while ((pair
= nvlist_next_nvpair(mapnvl
, pair
))
571 boolean_t livescsivhcip
= B_FALSE
;
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))) {
580 "list_devs: rv = %d; (%s) is not a devlink, "
582 (key
!= NULL
) ? key
: "null");
587 (void) nvlist_lookup_nvlist(mapnvl
, querydev
, &thisdevnvl
);
588 (void) nvlist_lookup_boolean_value(thisdevnvl
, NVL_MPXEN
,
590 (void) nvlist_lookup_string(thisdevnvl
, NVL_MPXPATH
,
593 if ((!livescsivhcip
) ||
595 (strncmp(key
, livepath
, strlen(key
)) == 0)))
598 (void) nvlist_lookup_string(thisdevnvl
, NVL_PATH
,
602 "list_devs: %s :: %s ::%s :: MPXEN (%s)\n",
603 key
, diskpath
, livepath
,
604 ((livescsivhcip
) ? "TRUE" : "FALSE"));
607 (void) sprintf(checkctrl
, "c%dt", ctrl
);
608 matchctrl
= strstr(key
, checkctrl
);
609 if (matchctrl
== NULL
)
612 if (listguids
!= 0) {
614 ddi_devid_t curdevid
;
617 rv
= devid_str_decode(querydev
, &curdevid
, NULL
);
619 logmsg(MSG_INFO
, "Unable to decode devid %s\n",
623 tempguid
= devid_to_guid(curdevid
);
624 if (tempguid
!= NULL
)
625 (void) printf("/dev/rdsk/%s\t%s\n",
628 devid_free_guid(tempguid
);
629 devid_free(curdevid
);
633 (void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n",
634 (strstr(key
, diskpath
) == NULL
) ? key
: diskpath
,
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.
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
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
656 * For the -m option, used when we're trying to find the root device mapping:
658 * # /lib/mpxio/stmsboot_util -m /dev/dsk/c2t0d0s2
659 * /dev/dsk/c3t500000E011637CF0d0s2
662 report_map(char *argdev
, int physpath
)
667 char *mpxpath
= NULL
;
668 char *prefixt
= NULL
;
669 char *prefixp
= NULL
;
670 char *stripdev
= NULL
;
677 if ((prefixt
= calloc(1, strlen(argdev
) + 1)) == NULL
) {
678 logmsg(MSG_INFO
, "Unable to allocate memory\n");
679 (void) printf("NOT_MAPPED\n");
683 (void) strlcpy(prefixt
, argdev
, strlen(argdev
) + 1);
685 slice
= strrchr(argdev
, (physpath
== NONBOOT
) ? 's' : ':');
687 slicelen
= strlen(slice
);
689 /* invalid size - max is 3 chars */
693 if ((stripdev
= calloc(1, strlen(prefixt
) + 1)) == NULL
) {
694 logmsg(MSG_INFO
, "Unable to allocate memory\n");
695 (void) printf("NOT_MAPPED\n");
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,
707 strlen(prefixt
) + 1: strlen(prefixp
) + 1);
709 prefixt
[strlen(argdev
) - strlen(prefixp
) + 1] = '\0';
711 if ((physpath
!= BOOT
) &&
712 (physpath
!= BOOT_PATH
)) {
713 logmsg(MSG_INFO
, "Invalid device path provided\n");
714 (void) printf("NOT_MAPPED\n");
719 (void) strlcpy(stripdev
, argdev
, strlen(argdev
) + 1);
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
);
730 stripdev
[strlen(stripdev
) - slicelen
] = '\0';
732 /* search for the shortened version */
733 rv
= nvlist_lookup_string(mapnvl
, stripdev
, &thisdevid
);
735 if ((physpath
!= BOOT
) &&
736 (physpath
!= BOOT_PATH
)) {
738 "searched mapnvl for '%s', got %s (%d)\n",
739 stripdev
, strerror(rv
), rv
);
740 (void) printf("NOT_MAPPED\n");
747 logmsg(MSG_INFO
, "device %s has devid %s\n", stripdev
, thisdevid
);
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");
759 if (!mpxenabled
&& (strstr(argdev
, "/pci") != NULL
||
760 strstr(argdev
, "/sbus") != NULL
)) {
761 (void) printf("%s\n", argdev
);
767 (void) nvlist_lookup_boolean_value(thisdev
, NVL_MPXEN
, &mpxenp
);
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
);
776 (void) printf("%s%s\n", mpxpath
,
777 ((slicelen
> 0) && slice
!= NULL
) ? slice
: "");
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
);
786 (void) printf("%s%s\n", mpxpath
,
787 ((slicelen
> 0) && slice
!= NULL
) ? slice
: "");
790 (void) nvlist_lookup_string(thisdev
,
791 ((readonlyroot
) ? NVL_PHYSPATH
:
792 ((mpxenp
== B_TRUE
) ? NVL_MPXPATH
: NVL_PATH
)),
794 logmsg(MSG_INFO
, "mpxpath = %s\n",
795 (mpxpath
== NULL
) ? "null" : mpxpath
);
797 (strstr(mpxpath
, "/scsi_vhci") != NULL
) ||
798 (strstr(mpxpath
, "/pci") != NULL
) ||
799 (strstr(mpxpath
, "/sbus") != NULL
)) {
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.
805 (void) printf("/devices%s:%c\n", mpxpath
,
808 (void) printf("%s%s%s\n",
809 (prefixt
[0] == '/') ? prefixt
: "",
811 ((slicelen
> 0) && slice
!= NULL
) ? slice
: "");
819 * Validate the in-kernel and on-disk forms of our devid cache,
820 * returns -1 for unfixable error and 0 for success.
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.
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.
843 curnode
= di_drv_first_node("ssd", devinfo_root
);
844 if (curnode
!= DI_NODE_NIL
)
845 rv1
= mpxio_nvl_boilerplate(curnode
);
847 curnode
= di_drv_first_node("sd", devinfo_root
);
848 if (curnode
!= DI_NODE_NIL
)
849 rv2
= mpxio_nvl_boilerplate(curnode
);
858 * According to devfs path name, it will print device node name.
861 print_node_name(char *drv_name
, char *strdevfspath
)
864 char *devfspath
= NULL
;
865 char *node_name
= NULL
;
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
);
872 if (devfspath
== NULL
)
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");
882 (void) printf("%s\n", node_name
);
890 * report device node name, search "ssd" and "sd" nodes,
891 * print the device node name which device path is same as
895 report_dev_node_name(char *strdevfspath
)
897 logmsg(MSG_INFO
, "strdevfspath: %s\n", strdevfspath
);
898 print_node_name("ssd", strdevfspath
);
899 print_node_name("sd", strdevfspath
);
903 mpxio_nvl_boilerplate(di_node_t curnode
)
907 ddi_devid_t curdevid
;
910 for (; curnode
!= DI_NODE_NIL
; curnode
= di_drv_next_node(curnode
)) {
914 get_devid(curnode
, &curdevid
);
915 if (curdevid
== NULL
)
917 * There's no devid registered for this device
918 * so it's not cool enough to play with us
922 strdevid
= devid_str_encode(curdevid
, NULL
);
923 /* does this exist in the on-disk cache? */
924 rv
= nvlist_lookup_nvlist(mapnvl
, strdevid
, &newnvl
);
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) {
930 gettext("Unable to allocate space for "
931 "a devid property list: %s\n"),
936 if ((rv
!= ENOTSUP
) && (rv
!= EINVAL
))
938 "%s exists in ondisknvl, verifying\n",
942 if (popcheck_devnvl(curnode
, newnvl
, strdevid
) != 0) {
944 gettext("Unable to populate devid nvpair "
945 "for device with devid %s\n"),
947 devid_str_free(strdevid
);
952 /* Now add newnvl into our cache. */
954 rv
= nvlist_add_nvlist(mapnvl
, strdevid
, newnvl
);
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
);
965 gettext("added device (devid %s) to mapnvl\n\n"),
967 devid_str_free(strdevid
);
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.
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.
982 popcheck_devnvl(di_node_t thisnode
, nvlist_t
*devnvl
, char *strdevid
)
985 char *curpath
= NULL
;
986 char *devfspath
= NULL
;
988 int scsivhciparent
= 0;
990 boolean_t mpxenp
= B_FALSE
;
993 devfspath
= di_devfs_path(thisnode
);
994 if (devfspath
== NULL
) {
996 gettext("Unable to determine devfs path for node: %s\n"),
1001 /* Add a convenient devfspath to devid inverse map */
1002 if (nvlist_add_string(mapnvl
, devfspath
, strdevid
) != 0) {
1004 gettext("Unable to add device path %s with devid "
1005 "%s to mapnvl\n"), devfspath
, strdevid
);
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) {
1016 if (strncmp(di_driver_name(di_parent_node(thisnode
)),
1017 "scsi_vhci", 9) == 0) {
1022 rv
= nvlist_lookup_boolean_value(devnvl
, NVL_MPXEN
, &mpxenp
);
1023 if (rv
|| (mpxenp
== B_FALSE
)) {
1024 rv
= nvlist_add_boolean_value(devnvl
,
1028 gettext("Unable to add property %s "
1029 "(set to B_TRUE) for device %s: "
1031 NVL_MPXEN
, devfspath
,
1035 logmsg(MSG_INFO
, "NVL_MPXEN :: (B_FALSE->B_TRUE)\n");
1038 /* turn _off_ the flag if it was enabled */
1039 rv
= nvlist_add_boolean_value(devnvl
, NVL_MPXEN
, B_FALSE
);
1042 gettext("Unable to add property %s "
1043 "(set to B_FALSE) for device %s: %s (%d)\n"),
1044 NVL_MPXEN
, devfspath
,
1048 logmsg(MSG_INFO
, "NVL_MPXEN :: (B_TRUE-> B_FALSE)\n");
1051 rv
= nvlist_add_string(devnvl
, NVL_PHYSPATH
, devfspath
);
1054 gettext("Unable to add physical device path (%s) "
1055 "property to nvl\n"));
1059 if ((curpath
= calloc(1, MAXPATHLEN
)) == NULL
) {
1061 gettext("Unable to allocate space for current path\n"));
1064 curpath
= find_link(thisnode
);
1065 if (curpath
== NULL
) {
1070 gettext("Unable to determine device path for node %s\n"),
1075 rv
= nvlist_lookup_string(devnvl
, NVL_MPXPATH
, &path
);
1077 if (scsivhciparent
) {
1078 (void) nvlist_add_string(devnvl
, NVL_MPXPATH
, curpath
);
1080 (void) nvlist_add_string(devnvl
, NVL_PATH
, curpath
);
1085 * This next block provides the path to devid inverse mapping
1086 * that other functions require
1089 if (nvlist_add_string(mapnvl
, path
, strdevid
) != 0) {
1091 gettext("Unable to add device %s with devid "
1092 "%s to mapnvl\n"), path
, strdevid
);
1095 logmsg(MSG_INFO
, "popcheck_devnvl: added path %s :: %s\n",
1099 if (nvlist_add_string(mapnvl
, curpath
, strdevid
) != 0) {
1101 gettext("Unable to add device %s with devid "
1102 "%s to mapnvl: %s\n"),
1103 curpath
, strdevid
, strerror(errno
));
1106 logmsg(MSG_INFO
, "popcheck_devnvl: added curpath %s :: %s\n",
1113 print_mpx_capable(di_node_t curnode
)
1117 char *aliases
= NULL
;
1120 aliases
= calloc(1, MAXPATHLEN
+ 1);
1121 if (aliases
== NULL
) {
1123 gettext("Unable to allocate memory for a device "
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
) {
1134 "Unable to find devfs path for device "
1135 "%s: %s\n", &curnode
, strerror(errno
));
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
: ""),
1148 (void) printf("%s\n", path
);
1152 (void) printf("%s\n", aliases
);
1156 link_cb(di_devlink_t devlink
, void *arg
)
1160 result
= di_devlink_path(devlink
);
1161 if (result
== NULL
) {
1162 arg
= (void *)"(null)";
1164 (void) strlcpy(arg
, result
, strlen(result
));
1166 logmsg(MSG_INFO
, "\nlink_cb::linkdata->resultstr = %s\n",
1167 ((result
!= NULL
) ? result
: "(null)"));
1168 return (DI_WALK_CONTINUE
);
1172 find_link(di_node_t cnode
)
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
;
1181 devfspath
= di_devfs_path(cnode
);
1182 if (cnode
== DI_NODE_NIL
) {
1184 gettext("find_ctrl must be called with non-null "
1188 logmsg(MSG_INFO
, "find_link: devfspath %s\n", devfspath
);
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");
1197 devminor
= di_minor_next(cnode
, devminor
);
1198 hdl
= di_devlink_init(di_devfs_minor_path(devminor
), DI_MAKE_LINK
);
1200 logmsg((readonlyroot
? MSG_INFO
: MSG_ERROR
),
1201 gettext("unable to take devlink snapshot: %s\n"),
1207 (void) snprintf(minorpath
, MAXPATHLEN
, "%s:c", devfspath
);
1210 if (di_devlink_walk(hdl
, linkname
, minorpath
, DI_PRIMARY_LINK
,
1211 (void *)cbresult
, link_cb
) < 0) {
1213 gettext("Unable to walk devlink snapshot for %s: %s\n"),
1214 minorpath
, strerror(errno
));
1218 if (di_devlink_fini(&hdl
) < 0) {
1220 gettext("Unable to close devlink snapshot: %s\n"),
1223 if (strstr(cbresult
, "dsk/") == NULL
)
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
);
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.
1241 get_devid(di_node_t node
, ddi_devid_t
*thisdevid
)
1244 char realpath
[MAXPATHLEN
];
1245 char *openpath
= di_devfs_path(node
);
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
);
1254 fd
= open(openpath
, O_RDONLY
|O_NDELAY
);
1258 logmsg(MSG_INFO
, "Unable to open path %s: %s\n",
1259 openpath
, strerror(errno
));
1263 if (devid_get(fd
, thisdevid
) != 0) {
1265 "'%s' node (%s) without a devid registered\n",
1266 di_driver_name(node
), di_devfs_path(node
));
1274 char *bootprop
= NULL
;
1276 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, devinfo_root
,
1277 "bootpath", &bootprop
) >= 0) {
1278 (void) printf("%s\n", bootprop
);
1280 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY
, devinfo_root
,
1281 "boot-path", &bootprop
) >= 0) {
1282 (void) printf("%s\n", bootprop
);
1285 (void) printf("ERROR: no bootpath/boot-path property found\n");
1291 get_phci_driver_name(char *phci_path
, char **driver_name
)
1293 di_node_t phci_node
= DI_NODE_NIL
;
1296 phci_node
= di_init(phci_path
, DINFOCPYONE
);
1297 if (phci_node
== DI_NODE_NIL
) {
1299 gettext("Unable to take phci snapshot "
1300 "(%s: %d)\n"), strerror(errno
), errno
);
1303 tmp
= di_driver_name(phci_node
);
1305 (void) strncpy(*driver_name
, tmp
, 10);
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
1316 vhci_to_phci(char *devpath
, char *slice
, int d_flag
)
1323 char nodename
[MAXPATHLEN
];
1324 char *phci_driver
= NULL
;
1326 vhci_fd
= open(VHCI_CTL_NODE
, O_RDWR
);
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) {
1336 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() failed, "
1337 "%s (%d)\n", strerror(rv
), rv
);
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
)))
1348 rv
= ioctl(vhci_fd
, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
, &ioc
);
1349 if (rv
|| npaths
== 0) {
1351 "SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO ioctl() (#2) "
1352 "failed, %s (%d)\n", strerror(rv
), rv
);
1357 if (ioc
.buf_elem
< npaths
)
1358 npaths
= ioc
.buf_elem
;
1360 phci_driver
= malloc(10);
1361 if (phci_driver
== NULL
) {
1363 "vhci_to_phci: Memory allocation failed\n");
1368 pi
= (sv_path_info_t
*)ioc
.ret_buf
;
1370 bzero(nodename
, MAXPATHLEN
);
1371 bzero(phci_driver
, 10);
1373 get_phci_driver_name(pi
->device
.ret_phci
,
1375 logmsg(MSG_INFO
, "phci driver name: %s\n", phci_driver
);
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
1381 * for alll other cases, "disk" should be used as the
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");
1389 (void) snprintf(nodename
, 5, "disk");
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
);
1398 (void) printf("\n");
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
);
1407 (void) printf("\n");
1417 (void) printf("NOT_MAPPED\n");
1421 * Write /etc/vfstab to /etc/vfstab.new, with any remapped device
1422 * names substituted.
1425 * 0 successful operation
1433 char fname
[MAXPATHLEN
];
1434 int rv
= -1, rval
= -1;
1435 char cdev
[MAXPATHLEN
];
1436 char bdev
[MAXPATHLEN
];
1437 char mntpt
[MAXPATHLEN
];
1441 char mntopt
[MAXPATHLEN
];
1443 char *prefixt
= NULL
;
1444 char *curdev
= NULL
;
1445 char *thisdevid
= NULL
;
1448 boolean_t devmpx
= B_FALSE
;
1450 buf
= calloc(1, MAXPATHLEN
);
1451 tmpbuf
= calloc(1, MAXPATHLEN
);
1452 if (buf
== NULL
|| tmpbuf
== NULL
)
1455 (void) snprintf(fname
, MAXPATHLEN
, "/etc/mpxio/vfstab.new");
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 "
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);
1470 while (fgets(buf
, MAXPATHLEN
, fdin
) != NULL
) {
1471 if (strlen(buf
) == (MAXPATHLEN
- 1) &&
1472 buf
[MAXPATHLEN
-2] != '\n') {
1474 gettext("/etc/vfstab line length too long, "
1475 "exceeded %2$d: \"%3$s\"\n"),
1476 MAXPATHLEN
- 2, buf
);
1486 /* LINTED - variable format specifier */
1487 rv
= sscanf(buf
, fmt
, bdev
, cdev
, mntpt
, fstype
, fsckpass
,
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.
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
,
1506 /* Whoa, where did this device go?! */
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
);
1515 /* The device exists in our mapnvl */
1516 (void) nvlist_lookup_nvlist(mapnvl
, thisdevid
,
1518 (void) nvlist_lookup_boolean_value(thisdev
,
1519 NVL_MPXEN
, &devmpx
);
1520 (void) nvlist_lookup_string(thisdev
,
1522 ? NVL_MPXPATH
: NVL_PATH
),
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"
1534 curdev
, slice
, fsckpass
, mntboot
, mntopt
);
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
);
1543 (void) fprintf(fdout
, "%s", tmpbuf
);
1545 (void) fprintf(fdout
, "%s", buf
);
1549 if (fflush(fdout
) != 0) {
1551 gettext("fprintf failed to write to %s: %s (%d)\n"),
1552 fname
, strerror(errno
), errno
);
1557 (void) fclose(fdin
);
1558 (void) fclose(fdout
);