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 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 2011 by Delphix. All rights reserved.
29 * Copyright 2017 Nexenta Systems, Inc.
30 * Copyright 2021 Oxide Computer Company
31 */
32
33 #include <fcntl.h>
34 #include <libdevinfo.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/sunddi.h>
40 #include <sys/types.h>
41 #include <sys/mkdev.h>
42 #include <ctype.h>
43 #include <libgen.h>
44 #include <unistd.h>
45 #include <devid.h>
46 #include <sys/fs/zfs.h>
47
48 #include "libdiskmgt.h"
49 #include "disks_private.h"
50
51 /* specify which disk links to use in the /dev directory */
52 #define DEVLINK_REGEX "rdsk/.*"
53 #define DEVLINK_FLOPPY_REGEX "rdiskette[0-9]"
54
55 #define FLOPPY_NAME "rdiskette"
56
57 #define MAXPROPLEN 1024
58 #define DEVICE_ID_PROP "devid"
59 #define PROD_ID_PROP "inquiry-product-id"
60 #define PROD_ID_USB_PROP "usb-product-name"
61 #define REMOVABLE_PROP "removable-media"
62 #define HOTPLUGGABLE_PROP "hotpluggable"
63 #define SCSI_OPTIONS_PROP "scsi-options"
64 #define VENDOR_ID_PROP "inquiry-vendor-id"
65 #define VENDOR_ID_USB_PROP "usb-vendor-name"
66 #define WWN_PROP "node-wwn"
67
68 static char *ctrltypes[] = {
69 DDI_NT_FC_ATTACHMENT_POINT,
70 DDI_NT_NVME_ATTACHMENT_POINT,
71 DDI_NT_SATA_ATTACHMENT_POINT,
72 DDI_NT_SATA_NEXUS,
73 DDI_NT_SCSI_ATTACHMENT_POINT,
74 DDI_NT_SCSI_NEXUS,
75 NULL
76 };
77
78 static char *bustypes[] = {
79 "sbus",
80 "pci",
81 "usb",
82 NULL
83 };
84
85 static bus_t *add_bus(struct search_args *args, di_node_t node,
86 di_minor_t minor, controller_t *cp);
87 static controller_t *add_controller(struct search_args *args,
88 di_node_t node, di_minor_t minor);
89 static int add_devpath(di_devlink_t devlink, void *arg);
90 static int add_devs(di_node_t node, di_minor_t minor, void *arg);
91 static int add_disk2controller(disk_t *diskp,
92 struct search_args *args);
93 static int add_disk2path(disk_t *dp, path_t *pp,
94 di_path_state_t st, char *wwn);
95 static int add_int2array(int p, int **parray);
96 static int add_ptr2array(void *p, void ***parray);
97 static char *bus_type(di_node_t node, di_minor_t minor,
98 di_prom_handle_t ph);
99 static void remove_controller(controller_t *cp,
100 controller_t *currp);
101 static void clean_paths(struct search_args *args);
102 static disk_t *create_disk(char *deviceid, char *kernel_name,
103 struct search_args *args);
104 static char *ctype(di_node_t node, di_minor_t minor);
105 static boolean_t disk_is_cdrom(const char *type);
106 static alias_t *find_alias(disk_t *diskp, char *kernel_name);
107 static bus_t *find_bus(struct search_args *args, char *name);
108 static controller_t *find_controller(struct search_args *args, char *name);
109 static disk_t *get_disk_by_deviceid(disk_t *listp, char *devid);
110 static void get_disk_name_from_path(char *path, char *name,
111 int size);
112 static char *get_byte_prop(char *prop_name, di_node_t node);
113 static di_node_t get_parent_bus(di_node_t node,
114 struct search_args *args);
115 static int get_prom_int(char *prop_name, di_node_t node,
116 di_prom_handle_t ph);
117 static char *get_prom_str(char *prop_name, di_node_t node,
118 di_prom_handle_t ph);
119 static int get_prop(char *prop_name, di_node_t node);
120 static char *get_str_prop(char *prop_name, di_node_t node);
121 static int have_disk(struct search_args *args, char *devid,
122 char *kernel_name, disk_t **diskp);
123 static int is_ctds(char *name);
124 static int is_drive(di_minor_t minor);
125 static int is_zvol(di_node_t node, di_minor_t minor);
126 static int is_ctrl(di_node_t node, di_minor_t minor);
127 static int new_alias(disk_t *diskp, char *kernel_path,
128 char *devlink_path, struct search_args *args);
129 static int new_devpath(alias_t *ap, char *devpath);
130 static path_t *new_path(controller_t *cp, disk_t *diskp,
131 di_node_t node, di_path_state_t st, char *wwn);
132 static void remove_invalid_controller(char *name,
133 controller_t *currp, struct search_args *args);
134
135 /*
136 * The functions in this file do a dev tree walk to build up a model of the
137 * disks, controllers and paths on the system. This model is returned in the
138 * args->disk_listp and args->controller_listp members of the args param.
139 * There is no global data for this file so it is thread safe. It is up to
140 * the caller to merge the resulting model with any existing model that is
141 * cached. The caller must also free the memory for this model when it is
142 * no longer needed.
143 */
144 void
findevs(struct search_args * args)145 findevs(struct search_args *args)
146 {
147 di_node_t di_root;
148
149 args->bus_listp = NULL;
150 args->controller_listp = NULL;
151 args->disk_listp = NULL;
152
153 args->ph = DI_PROM_HANDLE_NIL;
154 args->handle = DI_LINK_NIL;
155 args->dev_walk_status = 0;
156
157 /*
158 * Create device information library handles, which must be destroyed
159 * before we return.
160 */
161 if ((args->ph = di_prom_init()) == DI_PROM_HANDLE_NIL ||
162 (args->handle = di_devlink_init(NULL, 0)) == DI_LINK_NIL) {
163 /*
164 * We could not open all of the handles we need, so clean up
165 * and report failure to the caller.
166 */
167 args->dev_walk_status = errno;
168 goto cleanup;
169 }
170
171 /*
172 * Have to make several passes at this with the new devfs caching.
173 * First, we find non-mpxio devices. Then we find mpxio/multipath
174 * devices.
175 */
176 di_root = di_init("/", DINFOCACHE);
177 (void) di_walk_minor(di_root, NULL, 0, args, add_devs);
178 di_fini(di_root);
179
180 di_root = di_init("/", DINFOCPYALL|DINFOPATH);
181 (void) di_walk_minor(di_root, NULL, 0, args, add_devs);
182 di_fini(di_root);
183
184 clean_paths(args);
185
186 cleanup:
187 if (args->ph != DI_PROM_HANDLE_NIL) {
188 di_prom_fini(args->ph);
189 args->ph = DI_PROM_HANDLE_NIL;
190 }
191 if (args->handle != DI_LINK_NIL) {
192 (void) di_devlink_fini(&(args->handle));
193 }
194 }
195
196 /*
197 * Definitions of private functions
198 */
199
200 static bus_t *
add_bus(struct search_args * args,di_node_t node,di_minor_t minor,controller_t * cp)201 add_bus(struct search_args *args, di_node_t node, di_minor_t minor,
202 controller_t *cp)
203 {
204 char *btype;
205 char *devpath;
206 bus_t *bp;
207 char kstat_name[MAXPATHLEN];
208 di_node_t pnode;
209
210 if (node == DI_NODE_NIL) {
211 return (NULL);
212 }
213
214 if ((btype = bus_type(node, minor, args->ph)) == NULL) {
215 return (add_bus(args, di_parent_node(node),
216 di_minor_next(di_parent_node(node), NULL), cp));
217 }
218
219 devpath = di_devfs_path(node);
220
221 if ((bp = find_bus(args, devpath)) != NULL) {
222 di_devfs_path_free((void *) devpath);
223
224 if (cp != NULL) {
225 if (add_ptr2array(cp,
226 (void ***)&bp->controllers) != 0) {
227 args->dev_walk_status = ENOMEM;
228 return (NULL);
229 }
230 }
231 return (bp);
232 }
233
234 /* Special handling for root node. */
235 if (strcmp(devpath, "/") == 0) {
236 di_devfs_path_free((void *) devpath);
237 return (NULL);
238 }
239
240 if (dm_debug) {
241 (void) fprintf(stderr, "INFO: add_bus %s\n", devpath);
242 }
243
244 bp = (bus_t *)calloc(1, sizeof (bus_t));
245 if (bp == NULL) {
246 return (NULL);
247 }
248
249 bp->name = strdup(devpath);
250 di_devfs_path_free((void *) devpath);
251 if (bp->name == NULL) {
252 args->dev_walk_status = ENOMEM;
253 cache_free_bus(bp);
254 return (NULL);
255 }
256
257 bp->btype = strdup(btype);
258 if (bp->btype == NULL) {
259 args->dev_walk_status = ENOMEM;
260 cache_free_bus(bp);
261 return (NULL);
262 }
263
264 (void) snprintf(kstat_name, sizeof (kstat_name), "%s%d",
265 di_node_name(node), di_instance(node));
266
267 if ((bp->kstat_name = strdup(kstat_name)) == NULL) {
268 args->dev_walk_status = ENOMEM;
269 cache_free_bus(bp);
270 return (NULL);
271 }
272
273 /* if parent node is a bus, get its name */
274 if ((pnode = get_parent_bus(node, args)) != NULL) {
275 devpath = di_devfs_path(pnode);
276 bp->pname = strdup(devpath);
277 di_devfs_path_free((void *) devpath);
278 if (bp->pname == NULL) {
279 args->dev_walk_status = ENOMEM;
280 cache_free_bus(bp);
281 return (NULL);
282 }
283
284 } else {
285 bp->pname = NULL;
286 }
287
288 bp->freq = get_prom_int("clock-frequency", node, args->ph);
289
290 bp->controllers = (controller_t **)calloc(1, sizeof (controller_t *));
291 if (bp->controllers == NULL) {
292 args->dev_walk_status = ENOMEM;
293 cache_free_bus(bp);
294 return (NULL);
295 }
296 bp->controllers[0] = NULL;
297
298 if (cp != NULL) {
299 if (add_ptr2array(cp, (void ***)&bp->controllers) != 0) {
300 args->dev_walk_status = ENOMEM;
301 return (NULL);
302 }
303 }
304
305 bp->next = args->bus_listp;
306 args->bus_listp = bp;
307
308 return (bp);
309 }
310
311 static controller_t *
add_controller(struct search_args * args,di_node_t node,di_minor_t minor)312 add_controller(struct search_args *args, di_node_t node, di_minor_t minor)
313 {
314 char *devpath;
315 controller_t *cp;
316 char kstat_name[MAXPATHLEN];
317 char *c_type = DM_CTYPE_UNKNOWN;
318
319 devpath = di_devfs_path(node);
320
321 if ((cp = find_controller(args, devpath)) != NULL) {
322 di_devfs_path_free((void *) devpath);
323 return (cp);
324 }
325
326 /* Special handling for fp attachment node. */
327 if (strcmp(di_node_name(node), "fp") == 0) {
328 di_node_t pnode;
329
330 pnode = di_parent_node(node);
331 if (pnode != DI_NODE_NIL) {
332 di_devfs_path_free((void *) devpath);
333 devpath = di_devfs_path(pnode);
334
335 if ((cp = find_controller(args, devpath)) != NULL) {
336 di_devfs_path_free((void *) devpath);
337 return (cp);
338 }
339
340 /* not in the list, create it */
341 node = pnode;
342 c_type = DM_CTYPE_FIBRE;
343 }
344 }
345
346 if (dm_debug) {
347 (void) fprintf(stderr, "INFO: add_controller %s\n", devpath);
348 }
349
350 cp = (controller_t *)calloc(1, sizeof (controller_t));
351 if (cp == NULL) {
352 return (NULL);
353 }
354
355 cp->name = strdup(devpath);
356 di_devfs_path_free((void *) devpath);
357 if (cp->name == NULL) {
358 cache_free_controller(cp);
359 return (NULL);
360 }
361
362 if (strcmp(c_type, DM_CTYPE_UNKNOWN) == 0) {
363 c_type = ctype(node, minor);
364 }
365 cp->ctype = c_type;
366
367 (void) snprintf(kstat_name, sizeof (kstat_name), "%s%d",
368 di_node_name(node), di_instance(node));
369
370 if ((cp->kstat_name = strdup(kstat_name)) == NULL) {
371 cache_free_controller(cp);
372 return (NULL);
373 }
374
375 if (libdiskmgt_str_eq(cp->ctype, "scsi")) {
376 cp->scsi_options = get_prop(SCSI_OPTIONS_PROP, node);
377 }
378
379 if (libdiskmgt_str_eq(di_node_name(node), "scsi_vhci")) {
380 cp->multiplex = 1;
381 } else {
382 cp->multiplex = 0;
383 }
384
385 cp->freq = get_prom_int("clock-frequency", node, args->ph);
386
387 cp->disks = (disk_t **)calloc(1, sizeof (disk_t *));
388 if (cp->disks == NULL) {
389 cache_free_controller(cp);
390 return (NULL);
391 }
392 cp->disks[0] = NULL;
393
394 cp->next = args->controller_listp;
395 args->controller_listp = cp;
396
397 cp->bus = add_bus(args, di_parent_node(node),
398 di_minor_next(di_parent_node(node), NULL), cp);
399
400 return (cp);
401 }
402
403 static int
add_devpath(di_devlink_t devlink,void * arg)404 add_devpath(di_devlink_t devlink, void *arg)
405 {
406 struct search_args *args;
407 char *devidstr;
408 disk_t *diskp;
409 char kernel_name[MAXPATHLEN];
410
411 args = (struct search_args *)arg;
412
413 /*
414 * Get the diskp value from calling have_disk. Can either be found
415 * by kernel name or devid.
416 */
417
418 diskp = NULL;
419 devidstr = get_str_prop(DEVICE_ID_PROP, args->node);
420 (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
421 di_node_name(args->node), di_instance(args->node));
422
423 (void) have_disk(args, devidstr, kernel_name, &diskp);
424
425 /*
426 * The devlink_path is usually of the form /dev/rdsk/c0t0d0s0.
427 * For diskettes it is /dev/rdiskette*.
428 * On Intel we would also get each fdisk partition as well
429 * (e.g. /dev/rdsk/c0t0d0p0).
430 */
431 if (diskp != NULL) {
432 alias_t *ap;
433 char *devlink_path;
434
435 if (diskp->drv_type != DM_DT_FLOPPY) {
436 /*
437 * Add other controllers for multipath disks.
438 * This will have no effect if the controller
439 * relationship is already set up.
440 */
441 if (add_disk2controller(diskp, args) != 0) {
442 args->dev_walk_status = ENOMEM;
443 }
444 }
445
446 (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
447 di_node_name(args->node), di_instance(args->node));
448 devlink_path = (char *)di_devlink_path(devlink);
449
450 if (dm_debug > 1) {
451 (void) fprintf(stderr,
452 "INFO: devpath %s\n", devlink_path);
453 }
454
455 if ((ap = find_alias(diskp, kernel_name)) == NULL) {
456 if (new_alias(diskp, kernel_name, devlink_path,
457 args) != 0) {
458 args->dev_walk_status = ENOMEM;
459 }
460 } else {
461 /*
462 * It is possible that we have already added this
463 * devpath. Do not add it again. new_devpath will
464 * return a 0 if found, and not add the path.
465 */
466 if (new_devpath(ap, devlink_path) != 0) {
467 args->dev_walk_status = ENOMEM;
468 }
469 }
470 }
471
472 return (DI_WALK_CONTINUE);
473 }
474
475 static int
add_devs(di_node_t node,di_minor_t minor,void * arg)476 add_devs(di_node_t node, di_minor_t minor, void *arg)
477 {
478 struct search_args *args;
479 int result = DI_WALK_CONTINUE;
480
481 args = (struct search_args *)arg;
482
483 if (dm_debug > 1) {
484 /* This is all just debugging code */
485 char *devpath;
486 char dev_name[MAXPATHLEN];
487
488 devpath = di_devfs_path(node);
489 (void) snprintf(dev_name, sizeof (dev_name), "%s:%s", devpath,
490 di_minor_name(minor));
491 di_devfs_path_free((void *) devpath);
492
493 (void) fprintf(stderr,
494 "INFO: dev: %s, node: %s%d, minor: 0x%x, type: %s\n",
495 dev_name, di_node_name(node), di_instance(node),
496 di_minor_spectype(minor),
497 (di_minor_nodetype(minor) != NULL ?
498 di_minor_nodetype(minor) : "NULL"));
499 }
500
501 if (bus_type(node, minor, args->ph) != NULL) {
502 if (add_bus(args, node, minor, NULL) == NULL) {
503 args->dev_walk_status = ENOMEM;
504 result = DI_WALK_TERMINATE;
505 }
506
507 } else if (is_ctrl(node, minor)) {
508 if (add_controller(args, node, minor) == NULL) {
509 args->dev_walk_status = ENOMEM;
510 result = DI_WALK_TERMINATE;
511 }
512
513 } else if (di_minor_spectype(minor) == S_IFCHR &&
514 (is_drive(minor) || is_zvol(node, minor))) {
515 char *devidstr;
516 char kernel_name[MAXPATHLEN];
517 disk_t *diskp;
518
519 (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
520 di_node_name(node), di_instance(node));
521 devidstr = get_str_prop(DEVICE_ID_PROP, node);
522
523 args->node = node;
524 args->minor = minor;
525 /*
526 * Check if we already got this disk and
527 * this is another slice.
528 */
529 if (!have_disk(args, devidstr, kernel_name, &diskp)) {
530 args->dev_walk_status = 0;
531 /*
532 * This is a newly found disk, create the
533 * disk structure.
534 */
535 diskp = create_disk(devidstr, kernel_name, args);
536 if (diskp == NULL) {
537 args->dev_walk_status = ENOMEM;
538 }
539
540 if (diskp->drv_type != DM_DT_FLOPPY) {
541 /* add the controller relationship */
542 if (args->dev_walk_status == 0) {
543 if (add_disk2controller(diskp,
544 args) != 0) {
545 args->dev_walk_status = ENOMEM;
546 }
547 }
548 }
549 }
550 if (is_zvol(node, minor)) {
551 char zvdsk[MAXNAMELEN];
552 char *str;
553 alias_t *ap;
554
555 if (di_prop_lookup_strings(di_minor_devt(minor),
556 node, "name", &str) == -1)
557 return (DI_WALK_CONTINUE);
558 (void) snprintf(zvdsk, MAXNAMELEN, "/dev/zvol/rdsk/%s",
559 str);
560 if ((ap = find_alias(diskp, kernel_name)) == NULL) {
561 if (new_alias(diskp, kernel_name,
562 zvdsk, args) != 0) {
563 args->dev_walk_status = ENOMEM;
564 }
565 } else {
566 /*
567 * It is possible that we have already added
568 * this devpath.
569 * Do not add it again. new_devpath will
570 * return a 0 if found, and not add the path.
571 */
572 if (new_devpath(ap, zvdsk) != 0) {
573 args->dev_walk_status = ENOMEM;
574 }
575 }
576 }
577
578 /* Add the devpaths for the drive. */
579 if (args->dev_walk_status == 0) {
580 char *devpath;
581 char slice_path[MAXPATHLEN];
582 char *pattern;
583
584 /*
585 * We will come through here once for each of
586 * the raw slice device names.
587 */
588 devpath = di_devfs_path(node);
589 (void) snprintf(slice_path,
590 sizeof (slice_path), "%s:%s",
591 devpath, di_minor_name(minor));
592 di_devfs_path_free((void *) devpath);
593
594 if (libdiskmgt_str_eq(di_minor_nodetype(minor),
595 DDI_NT_FD)) {
596 pattern = DEVLINK_FLOPPY_REGEX;
597 } else {
598 pattern = DEVLINK_REGEX;
599 }
600
601 /* Walk the /dev tree to get the devlinks. */
602 (void) di_devlink_walk(args->handle, pattern,
603 slice_path, DI_PRIMARY_LINK, arg, add_devpath);
604 }
605
606 if (args->dev_walk_status != 0) {
607 result = DI_WALK_TERMINATE;
608 }
609 }
610
611 return (result);
612 }
613
614 static int
add_disk2controller(disk_t * diskp,struct search_args * args)615 add_disk2controller(disk_t *diskp, struct search_args *args)
616 {
617 di_node_t pnode;
618 controller_t *cp;
619 di_minor_t minor;
620 di_node_t node;
621 int i;
622
623 node = args->node;
624
625 pnode = di_parent_node(node);
626 if (pnode == DI_NODE_NIL) {
627 return (0);
628 }
629
630 /*
631 * Certain pseudo-device nodes do not all immediately have a valid
632 * parent-node. In particular, lofi (and zfs) would point to the generic
633 * /pseudo node. As a result, if we find a lofi disk, redirect it to the
634 * actual path. If we don't find it in this, then just fall back to the
635 * traditional path.
636 */
637 if (libdiskmgt_str_eq(di_node_name(pnode), "pseudo") &&
638 libdiskmgt_str_eq(di_node_name(node), "lofi")) {
639 di_node_t n;
640
641 n = di_drv_first_node("lofi", pnode);
642 while (n != DI_NODE_NIL) {
643 if (di_instance(n) == 0) {
644 pnode = n;
645 break;
646 }
647
648 n = di_drv_next_node(n);
649 }
650 }
651
652 minor = di_minor_next(pnode, NULL);
653 if (minor == NULL) {
654 return (0);
655 }
656
657 if ((cp = add_controller(args, pnode, minor)) == NULL) {
658 return (ENOMEM);
659 }
660
661 /* check if the disk <-> ctrl assoc is already there */
662 for (i = 0; diskp->controllers[i]; i++) {
663 if (cp == diskp->controllers[i]) {
664 return (0);
665 }
666 }
667
668 /* this is a new controller for this disk */
669
670 /* add the disk to the controller */
671 if (add_ptr2array(diskp, (void ***)&cp->disks) != 0) {
672 return (ENOMEM);
673 }
674
675 /* add the controller to the disk */
676 if (add_ptr2array(cp, (void ***)&diskp->controllers) != 0) {
677 return (ENOMEM);
678 }
679
680 /*
681 * Set up paths for mpxio controlled drives.
682 */
683 if (libdiskmgt_str_eq(di_node_name(pnode), "scsi_vhci")) {
684 /* note: mpxio di_path stuff is all consolidation private */
685 di_path_t pi = DI_PATH_NIL;
686
687 while (
688 (pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
689 int cnt;
690 uchar_t *bytes;
691 char str[MAXPATHLEN];
692 char *wwn;
693
694 di_node_t phci_node = di_path_phci_node(pi);
695
696 /* get the node wwn */
697 cnt = di_path_prop_lookup_bytes(pi, WWN_PROP, &bytes);
698 wwn = NULL;
699 if (cnt > 0) {
700 int i;
701 str[0] = 0;
702
703 for (i = 0; i < cnt; i++) {
704 /*
705 * A byte is only 2 hex chars + null.
706 */
707 char bstr[8];
708
709 (void) snprintf(bstr,
710 sizeof (bstr), "%.2x", bytes[i]);
711 (void) strlcat(str, bstr, sizeof (str));
712 }
713 wwn = str;
714 }
715
716 if (new_path(cp, diskp, phci_node,
717 di_path_state(pi), wwn) == NULL) {
718 return (ENOMEM);
719 }
720 }
721 }
722
723 return (0);
724 }
725
726 static int
add_disk2path(disk_t * dp,path_t * pp,di_path_state_t st,char * wwn)727 add_disk2path(disk_t *dp, path_t *pp, di_path_state_t st, char *wwn)
728 {
729 /* add the disk to the path */
730 if (add_ptr2array(dp, (void ***)&pp->disks) != 0) {
731 cache_free_path(pp);
732 return (0);
733 }
734
735 /* add the path to the disk */
736 if (add_ptr2array(pp, (void ***)&dp->paths) != 0) {
737 cache_free_path(pp);
738 return (0);
739 }
740
741 /* add the path state for this disk */
742 if (add_int2array(st, &pp->states) != 0) {
743 cache_free_path(pp);
744 return (0);
745 }
746
747 /* add the path state for this disk */
748 if (wwn != NULL) {
749 char *wp;
750
751 if ((wp = strdup(wwn)) != NULL) {
752 if (add_ptr2array(wp, (void ***)(&pp->wwns)) != 0) {
753 cache_free_path(pp);
754 return (0);
755 }
756 }
757 }
758
759 return (1);
760 }
761
762 static int
add_int2array(int p,int ** parray)763 add_int2array(int p, int **parray)
764 {
765 int i;
766 int cnt;
767 int *pa;
768 int *new_array;
769
770 pa = *parray;
771
772 cnt = 0;
773 if (pa != NULL) {
774 for (; pa[cnt] != -1; cnt++)
775 ;
776 }
777
778 new_array = (int *)calloc(cnt + 2, sizeof (int *));
779 if (new_array == NULL) {
780 return (ENOMEM);
781 }
782
783 /* copy the existing array */
784 for (i = 0; i < cnt; i++) {
785 new_array[i] = pa[i];
786 }
787
788 new_array[i] = p;
789 new_array[i + 1] = -1;
790
791 free(pa);
792 *parray = new_array;
793
794 return (0);
795 }
796
797 static int
add_ptr2array(void * p,void *** parray)798 add_ptr2array(void *p, void ***parray)
799 {
800 int i;
801 int cnt;
802 void **pa;
803 void **new_array;
804
805 pa = *parray;
806
807 cnt = 0;
808 if (pa != NULL) {
809 for (; pa[cnt]; cnt++)
810 ;
811 }
812
813 new_array = (void **)calloc(cnt + 2, sizeof (void *));
814 if (new_array == NULL) {
815 return (ENOMEM);
816 }
817
818 /* copy the existing array */
819 for (i = 0; i < cnt; i++) {
820 new_array[i] = pa[i];
821 }
822
823 new_array[i] = p;
824 new_array[i + 1] = NULL;
825
826 free(pa);
827 *parray = new_array;
828
829 return (0);
830 }
831
832 /*
833 * This function checks to see if a controller has other associations
834 * that may be valid. If we are calling this function, we have found that
835 * a controller for an mpxio device is showing up independently of the
836 * mpxio controller, noted as /scsi_vhci. This can happen with some FC
837 * cards that have inbound management devices that show up as well, with
838 * the real controller data associated. We do not want to display these
839 * 'devices' as real devices in libdiskmgt.
840 */
841 static void
remove_controller(controller_t * cp,controller_t * currp)842 remove_controller(controller_t *cp, controller_t *currp)
843 {
844 int i;
845
846 if (cp == currp) {
847 if (dm_debug) {
848 (void) fprintf(stderr, "ERROR: removing current"
849 " controller\n");
850 }
851 return;
852 }
853
854 if (cp->disks != NULL && cp->disks[0] != NULL) {
855 if (dm_debug) {
856 (void) fprintf(stderr,
857 "INFO: removing inbound management controller"
858 " with disk ptrs.\n");
859 }
860 /*
861 * loop through the disks and remove the reference to the
862 * controller for this disk structure. The disk itself
863 * is still a valid device, the controller being removed
864 * is a 'path' so any disk that has a reference to it
865 * as a controller needs to have this reference removed.
866 */
867 for (i = 0; cp->disks[i]; i++) {
868 disk_t *dp = cp->disks[i];
869 int j;
870
871 for (j = 0; dp->controllers[j]; j++) {
872 int k;
873
874 if (libdiskmgt_str_eq(dp->controllers[j]->name,
875 cp->name)) {
876
877 if (dm_debug) {
878 (void) fprintf(stderr,
879 "INFO: REMOVING disk %s on "
880 "controller %s\n",
881 dp->kernel_name, cp->name);
882 }
883 for (k = j; dp->controllers[k]; k++) {
884 dp->controllers[k] =
885 dp->controllers[k + 1];
886 }
887 }
888 }
889 }
890 }
891 /*
892 * Paths are removed with the call to cache_free_controller()
893 * below.
894 */
895
896 if (cp->paths != NULL && cp->paths[0] != NULL) {
897 if (dm_debug) {
898 (void) fprintf(stderr,
899 "INFO: removing inbound management controller"
900 " with path ptrs. \n");
901 }
902 }
903 cache_free_controller(cp);
904 }
905
906 /*
907 * If we have a controller in the list that is really a path then we need to
908 * take that controller out of the list since nodes that are paths are not
909 * considered to be controllers.
910 */
911 static void
clean_paths(struct search_args * args)912 clean_paths(struct search_args *args)
913 {
914 controller_t *cp;
915
916 cp = args->controller_listp;
917 while (cp != NULL) {
918 path_t **pp;
919
920 pp = cp->paths;
921 if (pp != NULL) {
922 int i;
923
924 for (i = 0; pp[i]; i++) {
925 remove_invalid_controller(pp[i]->name, cp,
926 args);
927 }
928 }
929 cp = cp->next;
930 }
931 }
932
933 static disk_t *
create_disk(char * deviceid,char * kernel_name,struct search_args * args)934 create_disk(char *deviceid, char *kernel_name, struct search_args *args)
935 {
936 disk_t *diskp;
937 char *type;
938 char *prod_id;
939 char *vendor_id;
940
941 if (dm_debug) {
942 (void) fprintf(stderr, "INFO: create_disk %s\n", kernel_name);
943 }
944
945 diskp = calloc(1, sizeof (disk_t));
946 if (diskp == NULL) {
947 return (NULL);
948 }
949
950 diskp->controllers = (controller_t **)
951 calloc(1, sizeof (controller_t *));
952 if (diskp->controllers == NULL) {
953 cache_free_disk(diskp);
954 return (NULL);
955 }
956 diskp->controllers[0] = NULL;
957
958 diskp->devid = NULL;
959 if (deviceid != NULL) {
960 if ((diskp->device_id = strdup(deviceid)) == NULL) {
961 cache_free_disk(diskp);
962 return (NULL);
963 }
964 (void) devid_str_decode(deviceid, &(diskp->devid), NULL);
965 }
966
967 if (kernel_name != NULL) {
968 diskp->kernel_name = strdup(kernel_name);
969 if (diskp->kernel_name == NULL) {
970 cache_free_disk(diskp);
971 return (NULL);
972 }
973 }
974
975 diskp->paths = NULL;
976 diskp->aliases = NULL;
977
978 diskp->cd_rom = 0;
979 diskp->rpm = 0;
980 diskp->solid_state = -1;
981 type = di_minor_nodetype(args->minor);
982
983 prod_id = get_str_prop(PROD_ID_PROP, args->node);
984 if (prod_id != NULL) {
985 if ((diskp->product_id = strdup(prod_id)) == NULL) {
986 cache_free_disk(diskp);
987 return (NULL);
988 }
989 } else {
990 prod_id = get_str_prop(PROD_ID_USB_PROP, args->node);
991 if (prod_id != NULL) {
992 if ((diskp->product_id = strdup(prod_id)) == NULL) {
993 cache_free_disk(diskp);
994 return (NULL);
995 }
996 }
997 }
998
999 vendor_id = get_str_prop(VENDOR_ID_PROP, args->node);
1000 if (vendor_id != NULL) {
1001 if ((diskp->vendor_id = strdup(vendor_id)) == NULL) {
1002 cache_free_disk(diskp);
1003 return (NULL);
1004 }
1005 } else {
1006 vendor_id = get_str_prop(VENDOR_ID_USB_PROP, args->node);
1007 if (vendor_id != NULL) {
1008 if ((diskp->vendor_id = strdup(vendor_id)) == NULL) {
1009 cache_free_disk(diskp);
1010 return (NULL);
1011 }
1012 }
1013 }
1014
1015 /*
1016 * DVD, CD-ROM, CD-RW, MO, etc. are all reported as CD-ROMS.
1017 * We try to use uscsi later to determine the real type.
1018 * The cd_rom flag tells us that the kernel categorized the drive
1019 * as a CD-ROM. We leave the drv_type as UNKNOWN for now.
1020 * The combination of the cd_rom flag being set with the drv_type of
1021 * unknown is what triggers the uscsi probe in drive.c.
1022 */
1023 if (disk_is_cdrom(type)) {
1024 diskp->drv_type = DM_DT_UNKNOWN;
1025 diskp->cd_rom = 1;
1026 diskp->removable = 1;
1027 } else if (libdiskmgt_str_eq(type, DDI_NT_FD)) {
1028 diskp->drv_type = DM_DT_FLOPPY;
1029 diskp->removable = 1;
1030 } else {
1031 /* not a CD-ROM or Floppy */
1032 diskp->removable = get_prop(REMOVABLE_PROP, args->node);
1033
1034 if (diskp->removable == -1) {
1035 diskp->removable = 0;
1036 diskp->drv_type = DM_DT_FIXED;
1037 }
1038 }
1039
1040 diskp->next = args->disk_listp;
1041 args->disk_listp = diskp;
1042
1043 return (diskp);
1044 }
1045
1046 static char *
ctype(di_node_t node,di_minor_t minor)1047 ctype(di_node_t node, di_minor_t minor)
1048 {
1049 char *type;
1050 char *name;
1051
1052 type = di_minor_nodetype(minor);
1053 name = di_node_name(node);
1054
1055 /* IDE disks use SCSI nexus as the type, so handle this special case */
1056 if ((libdiskmgt_str_eq(type, DDI_NT_SCSI_NEXUS) ||
1057 libdiskmgt_str_eq(type, DDI_PSEUDO)) &&
1058 libdiskmgt_str_eq(name, "ide"))
1059 return (DM_CTYPE_ATA);
1060
1061 if (libdiskmgt_str_eq(type, DDI_NT_FC_ATTACHMENT_POINT) ||
1062 (libdiskmgt_str_eq(type, DDI_NT_NEXUS) &&
1063 libdiskmgt_str_eq(name, "fp")))
1064 return (DM_CTYPE_FIBRE);
1065
1066 if (libdiskmgt_str_eq(type, DDI_NT_NVME_ATTACHMENT_POINT))
1067 return (DM_CTYPE_NVME);
1068
1069 if (libdiskmgt_str_eq(type, DDI_NT_SATA_NEXUS) ||
1070 libdiskmgt_str_eq(type, DDI_NT_SATA_ATTACHMENT_POINT))
1071 return (DM_CTYPE_SATA);
1072
1073 if (libdiskmgt_str_eq(type, DDI_NT_SCSI_NEXUS) ||
1074 libdiskmgt_str_eq(type, DDI_NT_SCSI_ATTACHMENT_POINT))
1075 return (DM_CTYPE_SCSI);
1076
1077 if (libdiskmgt_str_eq(di_minor_name(minor), "scsa2usb"))
1078 return (DM_CTYPE_USB);
1079
1080 if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1081 libdiskmgt_str_eq(name, "xpvd"))
1082 return (DM_CTYPE_XEN);
1083
1084 if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1085 libdiskmgt_str_eq(name, "lofi"))
1086 return (DM_CTYPE_LOFI);
1087
1088 if (dm_debug) {
1089 (void) fprintf(stderr,
1090 "INFO: unknown controller type=%s name=%s\n", type, name);
1091 }
1092
1093 return (DM_CTYPE_UNKNOWN);
1094 }
1095
1096 static boolean_t
disk_is_cdrom(const char * type)1097 disk_is_cdrom(const char *type)
1098 {
1099 return (strncmp(type, DDI_NT_CD, strlen(DDI_NT_CD)) == 0);
1100 }
1101
1102 static alias_t *
find_alias(disk_t * diskp,char * kernel_name)1103 find_alias(disk_t *diskp, char *kernel_name)
1104 {
1105 alias_t *ap;
1106
1107 ap = diskp->aliases;
1108 while (ap != NULL) {
1109 if (libdiskmgt_str_eq(ap->kstat_name, kernel_name)) {
1110 return (ap);
1111 }
1112 ap = ap->next;
1113 }
1114
1115 return (NULL);
1116 }
1117
1118 static bus_t *
find_bus(struct search_args * args,char * name)1119 find_bus(struct search_args *args, char *name)
1120 {
1121 bus_t *listp;
1122
1123 listp = args->bus_listp;
1124 while (listp != NULL) {
1125 if (libdiskmgt_str_eq(listp->name, name)) {
1126 return (listp);
1127 }
1128 listp = listp->next;
1129 }
1130
1131 return (NULL);
1132 }
1133
1134 static controller_t *
find_controller(struct search_args * args,char * name)1135 find_controller(struct search_args *args, char *name)
1136 {
1137 controller_t *listp;
1138
1139 listp = args->controller_listp;
1140 while (listp != NULL) {
1141 if (libdiskmgt_str_eq(listp->name, name)) {
1142 return (listp);
1143 }
1144 listp = listp->next;
1145 }
1146
1147 return (NULL);
1148 }
1149
1150 /*
1151 * Check if we have the drive in our list, based upon the device id.
1152 * We got the device id from the dev tree walk. This is encoded
1153 * using devid_str_encode(3DEVID). In order to check the device ids we need
1154 * to use the devid_compare(3DEVID) function, so we need to decode the
1155 * string representation of the device id.
1156 */
1157 static disk_t *
get_disk_by_deviceid(disk_t * listp,char * devidstr)1158 get_disk_by_deviceid(disk_t *listp, char *devidstr)
1159 {
1160 ddi_devid_t devid;
1161
1162 if (devidstr == NULL || devid_str_decode(devidstr, &devid, NULL) != 0) {
1163 return (NULL);
1164 }
1165
1166 while (listp != NULL) {
1167 if (listp->devid != NULL &&
1168 devid_compare(listp->devid, devid) == 0) {
1169 break;
1170 }
1171 listp = listp->next;
1172 }
1173
1174 devid_free(devid);
1175 return (listp);
1176 }
1177
1178 /*
1179 * Get the base disk name with no path prefix and no slice (if there is one).
1180 * The name parameter should be big enough to hold the name.
1181 * This handles diskette names ok (/dev/rdiskette0) since there is no slice,
1182 * and converts the raw diskette name.
1183 * But, we don't know how to strip off the slice from third party drive
1184 * names. That just means that their drive name will include a slice on
1185 * it.
1186 */
1187 static void
get_disk_name_from_path(char * path,char * name,int size)1188 get_disk_name_from_path(char *path, char *name, int size)
1189 {
1190 char *basep;
1191 int cnt = 0;
1192
1193 basep = strrchr(path, '/');
1194 if (basep == NULL) {
1195 basep = path;
1196 } else {
1197 basep++;
1198 }
1199
1200 size = size - 1; /* leave room for terminating 0 */
1201
1202 if (is_ctds(basep)) {
1203 while (*basep != 0 && *basep != 's' && cnt < size) {
1204 *name++ = *basep++;
1205 cnt++;
1206 }
1207 *name = 0;
1208 } else {
1209 if (strncmp(basep, FLOPPY_NAME,
1210 sizeof (FLOPPY_NAME) - 1) == 0) {
1211 /*
1212 * a floppy, convert rdiskette name to diskette name,
1213 * by skipping over the 'r' for raw diskette
1214 */
1215 basep++;
1216 }
1217
1218 /* not a ctds name, just copy it */
1219 (void) strlcpy(name, basep, size);
1220 }
1221 }
1222
1223 static char *
get_byte_prop(char * prop_name,di_node_t node)1224 get_byte_prop(char *prop_name, di_node_t node)
1225 {
1226 int cnt;
1227 uchar_t *bytes;
1228 int i;
1229 char str[MAXPATHLEN];
1230
1231 cnt = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, prop_name, &bytes);
1232 if (cnt < 1) {
1233 return (NULL);
1234 }
1235
1236 str[0] = 0;
1237 for (i = 0; i < cnt; i++) {
1238 char bstr[8]; /* a byte is only 2 hex chars + null */
1239
1240 (void) snprintf(bstr, sizeof (bstr), "%.2x", bytes[i]);
1241 (void) strlcat(str, bstr, sizeof (str));
1242 }
1243 return (strdup(str));
1244 }
1245
1246 static di_node_t
get_parent_bus(di_node_t node,struct search_args * args)1247 get_parent_bus(di_node_t node, struct search_args *args)
1248 {
1249 di_node_t pnode;
1250
1251 pnode = di_parent_node(node);
1252 if (pnode == DI_NODE_NIL) {
1253 return (NULL);
1254 }
1255
1256 if (bus_type(pnode, di_minor_next(pnode, NULL), args->ph) != NULL) {
1257 return (pnode);
1258 }
1259
1260 return (get_parent_bus(pnode, args));
1261 }
1262
1263 static int
get_prom_int(char * prop_name,di_node_t node,di_prom_handle_t ph)1264 get_prom_int(char *prop_name, di_node_t node, di_prom_handle_t ph)
1265 {
1266 int *n;
1267
1268 if (di_prom_prop_lookup_ints(ph, node, prop_name, &n) == 1) {
1269 return (*n);
1270 }
1271
1272 return (0);
1273 }
1274
1275 static char *
get_prom_str(char * prop_name,di_node_t node,di_prom_handle_t ph)1276 get_prom_str(char *prop_name, di_node_t node, di_prom_handle_t ph)
1277 {
1278 char *str;
1279
1280 if (di_prom_prop_lookup_strings(ph, node, prop_name, &str) == 1) {
1281 return (str);
1282 }
1283
1284 return (NULL);
1285 }
1286
1287 /*
1288 * Get one of the positive int or boolean properties.
1289 */
1290 static int
get_prop(char * prop_name,di_node_t node)1291 get_prop(char *prop_name, di_node_t node)
1292 {
1293 int num;
1294 int *ip;
1295
1296 if ((num = di_prop_lookup_ints(DDI_DEV_T_ANY, node, prop_name, &ip))
1297 >= 0) {
1298 if (num == 0) {
1299 /* boolean */
1300 return (1);
1301 } else if (num == 1) {
1302 /* single int */
1303 return (*ip);
1304 }
1305 }
1306 return (-1);
1307 }
1308
1309 static char *
get_str_prop(char * prop_name,di_node_t node)1310 get_str_prop(char *prop_name, di_node_t node)
1311 {
1312 char *str;
1313
1314 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, prop_name, &str) == 1) {
1315 return (str);
1316 }
1317
1318 return (NULL);
1319 }
1320
1321 /*
1322 * Check if we have the drive in our list, based upon the device id, if the
1323 * drive has a device id, or the kernel name, if it doesn't have a device id.
1324 */
1325 static int
have_disk(struct search_args * args,char * devidstr,char * kernel_name,disk_t ** diskp)1326 have_disk(struct search_args *args, char *devidstr, char *kernel_name,
1327 disk_t **diskp)
1328 {
1329 disk_t *listp;
1330
1331 *diskp = NULL;
1332 listp = args->disk_listp;
1333 if (devidstr != NULL) {
1334 if ((*diskp = get_disk_by_deviceid(listp, devidstr)) != NULL) {
1335 return (1);
1336 }
1337
1338 } else {
1339 /* no devid, try matching the kernel names on the drives */
1340 while (listp != NULL) {
1341 if (libdiskmgt_str_eq(kernel_name,
1342 listp->kernel_name)) {
1343 *diskp = listp;
1344 return (1);
1345 }
1346 listp = listp->next;
1347 }
1348 }
1349 return (0);
1350 }
1351
1352 static char *
bus_type(di_node_t node,di_minor_t minor,di_prom_handle_t ph)1353 bus_type(di_node_t node, di_minor_t minor, di_prom_handle_t ph)
1354 {
1355 char *type;
1356 int i;
1357
1358 type = get_prom_str("device_type", node, ph);
1359 if (type == NULL) {
1360 type = di_node_name(node);
1361 }
1362
1363 for (i = 0; bustypes[i]; i++) {
1364 if (libdiskmgt_str_eq(type, bustypes[i])) {
1365 return (type);
1366 }
1367 }
1368
1369 if (minor != NULL && strcmp(di_minor_nodetype(minor),
1370 DDI_NT_USB_ATTACHMENT_POINT) == 0) {
1371 return ("usb");
1372 }
1373
1374 return (NULL);
1375 }
1376
1377 /*
1378 * If the input name is in c[t]ds format then return 1, otherwise return 0.
1379 */
1380 static int
is_ctds(char * name)1381 is_ctds(char *name)
1382 {
1383 char *p;
1384
1385 p = name;
1386
1387 if (*p++ != 'c') {
1388 return (0);
1389 }
1390 /* skip controller digits */
1391 while (isdigit(*p)) {
1392 p++;
1393 }
1394
1395 /* handle optional target */
1396 if (*p == 't') {
1397 p++;
1398 /* skip over target */
1399 while (isdigit(*p) || isupper(*p)) {
1400 p++;
1401 }
1402 }
1403
1404 if (*p++ != 'd') {
1405 return (0);
1406 }
1407 while (isdigit(*p)) {
1408 p++;
1409 }
1410
1411 if (*p++ != 's') {
1412 return (0);
1413 }
1414
1415 /* check the slice number */
1416 while (isdigit(*p)) {
1417 p++;
1418 }
1419
1420 if (*p != 0) {
1421 return (0);
1422 }
1423
1424 return (1);
1425 }
1426
1427 static int
is_drive(di_minor_t minor)1428 is_drive(di_minor_t minor)
1429 {
1430 return (strncmp(di_minor_nodetype(minor), DDI_NT_BLOCK,
1431 strlen(DDI_NT_BLOCK)) == 0);
1432 }
1433
1434 static int
is_zvol(di_node_t node,di_minor_t minor)1435 is_zvol(di_node_t node, di_minor_t minor)
1436 {
1437 if ((strncmp(di_node_name(node), ZFS_DRIVER, 3) == 0) &&
1438 minor(di_minor_devt(minor)))
1439 return (1);
1440 return (0);
1441 }
1442
1443 static int
is_ctrl(di_node_t node,di_minor_t minor)1444 is_ctrl(di_node_t node, di_minor_t minor)
1445 {
1446 char *type;
1447 char *name;
1448 int type_index;
1449
1450 type = di_minor_nodetype(minor);
1451 type_index = 0;
1452
1453 while (ctrltypes[type_index] != NULL) {
1454 if (libdiskmgt_str_eq(type, ctrltypes[type_index])) {
1455 return (1);
1456 }
1457 type_index++;
1458 }
1459
1460 name = di_node_name(node);
1461 if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1462 (libdiskmgt_str_eq(name, "ide") ||
1463 libdiskmgt_str_eq(name, "xpvd")))
1464 return (1);
1465
1466 if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
1467 libdiskmgt_str_eq(name, "lofi") &&
1468 libdiskmgt_str_eq(di_minor_name(minor), "ctl"))
1469 return (1);
1470
1471 return (0);
1472 }
1473
1474 static int
new_alias(disk_t * diskp,char * kernel_name,char * devlink_path,struct search_args * args)1475 new_alias(disk_t *diskp, char *kernel_name, char *devlink_path,
1476 struct search_args *args)
1477 {
1478 alias_t *aliasp;
1479 char alias[MAXPATHLEN];
1480 di_node_t pnode;
1481
1482 aliasp = malloc(sizeof (alias_t));
1483 if (aliasp == NULL) {
1484 return (ENOMEM);
1485 }
1486
1487 aliasp->alias = NULL;
1488 aliasp->kstat_name = NULL;
1489 aliasp->wwn = NULL;
1490 aliasp->devpaths = NULL;
1491 aliasp->orig_paths = NULL;
1492
1493 get_disk_name_from_path(devlink_path, alias, sizeof (alias));
1494
1495 aliasp->alias = strdup(alias);
1496 if (aliasp->alias == NULL) {
1497 cache_free_alias(aliasp);
1498 return (ENOMEM);
1499 }
1500
1501 if (kernel_name != NULL) {
1502 aliasp->kstat_name = strdup(kernel_name);
1503 if (aliasp->kstat_name == NULL) {
1504 cache_free_alias(aliasp);
1505 return (ENOMEM);
1506 }
1507 } else {
1508 aliasp->kstat_name = NULL;
1509 }
1510
1511 aliasp->lun = get_prop(DM_LUN, args->node);
1512 aliasp->target = get_prop(DM_TARGET, args->node);
1513 aliasp->wwn = get_byte_prop(WWN_PROP, args->node);
1514
1515 pnode = di_parent_node(args->node);
1516 if (pnode != DI_NODE_NIL) {
1517 char prop_name[MAXPROPLEN];
1518
1519 (void) snprintf(prop_name, sizeof (prop_name),
1520 "target%d-sync-speed", aliasp->target);
1521 diskp->sync_speed = get_prop(prop_name, pnode);
1522 (void) snprintf(prop_name, sizeof (prop_name), "target%d-wide",
1523 aliasp->target);
1524 diskp->wide = get_prop(prop_name, pnode);
1525 }
1526
1527 if (new_devpath(aliasp, devlink_path) != 0) {
1528 cache_free_alias(aliasp);
1529 return (ENOMEM);
1530 }
1531
1532 aliasp->next = diskp->aliases;
1533 diskp->aliases = aliasp;
1534
1535 return (0);
1536 }
1537
1538 /*
1539 * Append the new devpath to the end of the devpath list. This is important
1540 * since we may want to use the order of the devpaths to match up the vtoc
1541 * entries.
1542 */
1543 static int
new_devpath(alias_t * ap,char * devpath)1544 new_devpath(alias_t *ap, char *devpath)
1545 {
1546 slice_t *newdp;
1547 slice_t *alistp;
1548
1549 /*
1550 * First, search the alias list to be sure that this devpath is
1551 * not already there.
1552 */
1553
1554 for (alistp = ap->devpaths; alistp != NULL; alistp = alistp->next) {
1555 if (libdiskmgt_str_eq(alistp->devpath, devpath)) {
1556 return (0);
1557 }
1558 }
1559
1560 /*
1561 * Otherwise, not found so add this new devpath to the list.
1562 */
1563
1564 newdp = malloc(sizeof (slice_t));
1565 if (newdp == NULL) {
1566 return (ENOMEM);
1567 }
1568
1569 newdp->devpath = strdup(devpath);
1570 if (newdp->devpath == NULL) {
1571 free(newdp);
1572 return (ENOMEM);
1573 }
1574 newdp->slice_num = -1;
1575 newdp->next = NULL;
1576
1577 if (ap->devpaths == NULL) {
1578 ap->devpaths = newdp;
1579 } else {
1580 /* append the devpath to the end of the list */
1581 slice_t *dp;
1582
1583 dp = ap->devpaths;
1584 while (dp->next != NULL) {
1585 dp = dp->next;
1586 }
1587
1588 dp->next = newdp;
1589 }
1590
1591 return (0);
1592 }
1593
1594 static path_t *
new_path(controller_t * cp,disk_t * dp,di_node_t node,di_path_state_t st,char * wwn)1595 new_path(controller_t *cp, disk_t *dp, di_node_t node, di_path_state_t st,
1596 char *wwn)
1597 {
1598 char *devpath;
1599 path_t *pp;
1600 di_minor_t minor;
1601
1602 /* Special handling for fp attachment node. */
1603 if (strcmp(di_node_name(node), "fp") == 0) {
1604 di_node_t pnode;
1605
1606 pnode = di_parent_node(node);
1607 if (pnode != DI_NODE_NIL) {
1608 node = pnode;
1609 }
1610 }
1611
1612 devpath = di_devfs_path(node);
1613
1614 /* check if the path is already there */
1615 pp = NULL;
1616 if (cp->paths != NULL) {
1617 int i;
1618
1619 for (i = 0; cp->paths[i]; i++) {
1620 if (libdiskmgt_str_eq(devpath, cp->paths[i]->name)) {
1621 pp = cp->paths[i];
1622 break;
1623 }
1624 }
1625 }
1626
1627 if (pp != NULL) {
1628 /* the path exists, add this disk to it */
1629
1630 di_devfs_path_free((void *) devpath);
1631 if (!add_disk2path(dp, pp, st, wwn)) {
1632 return (NULL);
1633 }
1634 return (pp);
1635 }
1636
1637 /* create a new path */
1638
1639 pp = calloc(1, sizeof (path_t));
1640 if (pp == NULL) {
1641 di_devfs_path_free((void *) devpath);
1642 return (NULL);
1643 }
1644
1645 pp->name = strdup(devpath);
1646 di_devfs_path_free((void *) devpath);
1647 if (pp->name == NULL) {
1648 cache_free_path(pp);
1649 return (NULL);
1650 }
1651
1652 /* add the disk to the path */
1653 if (!add_disk2path(dp, pp, st, wwn)) {
1654 return (NULL);
1655 }
1656
1657 /* add the path to the controller */
1658 if (add_ptr2array(pp, (void ***)&cp->paths) != 0) {
1659 cache_free_path(pp);
1660 return (NULL);
1661 }
1662
1663 /* add the controller to the path */
1664 pp->controller = cp;
1665
1666 minor = di_minor_next(node, NULL);
1667 if (minor != NULL) {
1668 pp->ctype = ctype(node, minor);
1669 } else {
1670 pp->ctype = DM_CTYPE_UNKNOWN;
1671 }
1672
1673 return (pp);
1674 }
1675
1676 /*
1677 * We pass in the current controller pointer (currp) so we can double check
1678 * that we aren't corrupting the list by removing the element we are on. This
1679 * should never happen, but it doesn't hurt to double check.
1680 */
1681 static void
remove_invalid_controller(char * name,controller_t * currp,struct search_args * args)1682 remove_invalid_controller(char *name, controller_t *currp,
1683 struct search_args *args)
1684 {
1685 controller_t *cp;
1686 bus_t *bp;
1687 controller_t *prevp;
1688
1689 bp = args->bus_listp;
1690 while (bp != NULL) {
1691 int i;
1692
1693 for (i = 0; bp->controllers[i]; i++) {
1694 if (libdiskmgt_str_eq(bp->controllers[i]->name, name)) {
1695 int j;
1696 /*
1697 * remove pointer to invalid controller.
1698 * (it is a path)
1699 */
1700 for (j = i; bp->controllers[j]; j++) {
1701 bp->controllers[j] =
1702 bp->controllers[j + 1];
1703 }
1704 }
1705 }
1706 bp = bp->next;
1707 }
1708
1709 if (args->controller_listp == NULL) {
1710 return;
1711 }
1712
1713 cp = args->controller_listp;
1714 if (libdiskmgt_str_eq(cp->name, name)) {
1715 args->controller_listp = cp->next;
1716 if (dm_debug) {
1717 (void) fprintf(stderr,
1718 "INFO: Removed controller %s from list\n",
1719 cp->name);
1720 }
1721 remove_controller(cp, currp);
1722 return;
1723 }
1724
1725 prevp = cp;
1726 cp = cp->next;
1727 while (cp != NULL) {
1728 if (libdiskmgt_str_eq(cp->name, name)) {
1729 if (dm_debug) {
1730 (void) fprintf(stderr,
1731 "INFO: Removed controller %s from list\n",
1732 cp->name);
1733 }
1734 prevp->next = cp->next;
1735 remove_controller(cp, currp);
1736 return;
1737 }
1738 prevp = cp;
1739 cp = cp->next;
1740 }
1741 }
1742