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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2017, Joyent, Inc.
25 */
26
27 /*
28 * Functions in this file are shared between the disk and ses enumerators.
29 *
30 * A topo_list_t of all disks is returned by a successful disk_list_gather()
31 * call, and the list is freed by a disk_list_free(). To create a 'disk' topo
32 * node below a specific 'bay' parent node either disk_declare_path() or
33 * disk_declare_addr() are called. The caller determines which 'disk' is
34 * in which 'bay'. A disk's 'label' and 'authority' information come from
35 * its parent 'bay' node.
36 */
37
38 #include <ctype.h>
39 #include <strings.h>
40 #include <libdevinfo.h>
41 #include <devid.h>
42 #include <sys/libdevid.h>
43 #include <pthread.h>
44 #include <inttypes.h>
45 #include <sys/dkio.h>
46 #include <sys/scsi/scsi_types.h>
47 #include <fm/topo_mod.h>
48 #include <fm/topo_list.h>
49 #include <fm/libdiskstatus.h>
50 #include <sys/fm/protocol.h>
51 #include <sys/scsi/generic/inquiry.h>
52 #include "disk.h"
53
54 /* common callback information for di_walk_node() and di_devlink_walk */
55 typedef struct disk_cbdata {
56 topo_mod_t *dcb_mod;
57 topo_list_t *dcb_list;
58
59 di_devlink_handle_t dcb_devhdl;
60 dev_di_node_t *dcb_dnode; /* for di_devlink_walk only */
61 } disk_cbdata_t;
62
63 /*
64 * Given a /devices path for a whole disk, appending this extension gives the
65 * path to a raw device that can be opened.
66 */
67 #if defined(__i386) || defined(__amd64)
68 #define PHYS_EXTN ":q,raw"
69 #elif defined(__sparc) || defined(__sparcv9)
70 #define PHYS_EXTN ":c,raw"
71 #else
72 #error Unknown architecture
73 #endif
74
75 /*
76 * Methods for disks. This is used by the disk-transport module to
77 * generate ereports based off SCSI disk status.
78 */
79 static int disk_status(topo_mod_t *, tnode_t *, topo_version_t,
80 nvlist_t *, nvlist_t **);
81
82 static const topo_method_t disk_methods[] = {
83 { TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
84 TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
85 disk_status },
86 { NULL }
87 };
88
89 static const topo_pgroup_info_t io_pgroup = {
90 TOPO_PGROUP_IO,
91 TOPO_STABILITY_PRIVATE,
92 TOPO_STABILITY_PRIVATE,
93 1
94 };
95
96 static const topo_pgroup_info_t disk_auth_pgroup = {
97 FM_FMRI_AUTHORITY,
98 TOPO_STABILITY_PRIVATE,
99 TOPO_STABILITY_PRIVATE,
100 1
101 };
102
103 static const topo_pgroup_info_t storage_pgroup = {
104 TOPO_PGROUP_STORAGE,
105 TOPO_STABILITY_PRIVATE,
106 TOPO_STABILITY_PRIVATE,
107 1
108 };
109
110 /*
111 * Set the properties of the disk node, from dev_di_node_t data.
112 * Properties include:
113 * group: protocol properties: resource, asru, label, fru
114 * group: authority properties: product-id, chasis-id, server-id
115 * group: io properties: devfs-path, devid
116 * group: storage properties:
117 * - logical-disk, disk-model, disk-manufacturer, serial-number
118 * - firmware-revision, capacity-in-bytes
119 *
120 * NOTE: the io and storage groups won't be present if the dnode passed in is
121 * NULL. This happens when a disk is found through ses, but is not enumerated
122 * in the devinfo tree.
123 */
124 static int
disk_set_props(topo_mod_t * mod,tnode_t * parent,tnode_t * dtn,dev_di_node_t * dnode)125 disk_set_props(topo_mod_t *mod, tnode_t *parent,
126 tnode_t *dtn, dev_di_node_t *dnode)
127 {
128 nvlist_t *asru = NULL;
129 char *label = NULL;
130 nvlist_t *fmri = NULL;
131 int err;
132
133 /* pull the label property down from our parent 'bay' node */
134 if (topo_node_label(parent, &label, &err) != 0) {
135 topo_mod_dprintf(mod, "disk_set_props: "
136 "label error %s\n", topo_strerror(err));
137 goto error;
138 }
139 if (topo_node_label_set(dtn, label, &err) != 0) {
140 topo_mod_dprintf(mod, "disk_set_props: "
141 "label_set error %s\n", topo_strerror(err));
142 goto error;
143 }
144
145 /* get the resource fmri, and use it as the fru */
146 if (topo_node_resource(dtn, &fmri, &err) != 0) {
147 topo_mod_dprintf(mod, "disk_set_props: "
148 "resource error: %s\n", topo_strerror(err));
149 goto error;
150 }
151 if (topo_node_fru_set(dtn, fmri, 0, &err) != 0) {
152 topo_mod_dprintf(mod, "disk_set_props: "
153 "fru_set error: %s\n", topo_strerror(err));
154 goto error;
155 }
156
157 /* create/set the authority group */
158 if ((topo_pgroup_create(dtn, &disk_auth_pgroup, &err) != 0) &&
159 (err != ETOPO_PROP_DEFD)) {
160 topo_mod_dprintf(mod, "disk_set_props: "
161 "create disk_auth error %s\n", topo_strerror(err));
162 goto error;
163 }
164
165 /* create the storage group */
166 if (topo_pgroup_create(dtn, &storage_pgroup, &err) != 0) {
167 topo_mod_dprintf(mod, "disk_set_props: "
168 "create storage error %s\n", topo_strerror(err));
169 goto error;
170 }
171
172 /* no dnode was found for this disk - skip the io and storage groups */
173 if (dnode == NULL) {
174 err = 0;
175 goto out;
176 }
177
178 /* form and set the asru */
179 if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
180 dnode->ddn_dpath, dnode->ddn_devid)) == NULL) {
181 err = ETOPO_FMRI_UNKNOWN;
182 topo_mod_dprintf(mod, "disk_set_props: "
183 "asru error %s\n", topo_strerror(err));
184 goto error;
185 }
186 if (topo_node_asru_set(dtn, asru, 0, &err) != 0) {
187 topo_mod_dprintf(mod, "disk_set_props: "
188 "asru_set error %s\n", topo_strerror(err));
189 goto error;
190 }
191
192 /* create/set the devfs-path and devid in the io group */
193 if (topo_pgroup_create(dtn, &io_pgroup, &err) != 0) {
194 topo_mod_dprintf(mod, "disk_set_props: "
195 "create io error %s\n", topo_strerror(err));
196 goto error;
197 }
198
199 if (topo_prop_set_string(dtn, TOPO_PGROUP_IO, TOPO_IO_DEV_PATH,
200 TOPO_PROP_IMMUTABLE, dnode->ddn_dpath, &err) != 0) {
201 topo_mod_dprintf(mod, "disk_set_props: "
202 "set dev error %s\n", topo_strerror(err));
203 goto error;
204 }
205
206 if (dnode->ddn_devid && topo_prop_set_string(dtn, TOPO_PGROUP_IO,
207 TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, dnode->ddn_devid, &err) != 0) {
208 topo_mod_dprintf(mod, "disk_set_props: "
209 "set devid error %s\n", topo_strerror(err));
210 goto error;
211 }
212
213 if (dnode->ddn_ppath_count != 0 &&
214 topo_prop_set_string_array(dtn, TOPO_PGROUP_IO, TOPO_IO_PHYS_PATH,
215 TOPO_PROP_IMMUTABLE, (const char **)dnode->ddn_ppath,
216 dnode->ddn_ppath_count, &err) != 0) {
217 topo_mod_dprintf(mod, "disk_set_props: "
218 "set phys-path error %s\n", topo_strerror(err));
219 goto error;
220 }
221
222 /* set the storage group public /dev name */
223 if (dnode->ddn_lpath != NULL &&
224 topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
225 TOPO_STORAGE_LOGICAL_DISK_NAME, TOPO_PROP_IMMUTABLE,
226 dnode->ddn_lpath, &err) != 0) {
227 topo_mod_dprintf(mod, "disk_set_props: "
228 "set disk_name error %s\n", topo_strerror(err));
229 goto error;
230 }
231
232 /* populate other misc storage group properties */
233 if (dnode->ddn_mfg && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
234 TOPO_STORAGE_MANUFACTURER, TOPO_PROP_IMMUTABLE,
235 dnode->ddn_mfg, &err) != 0)) {
236 topo_mod_dprintf(mod, "disk_set_props: "
237 "set mfg error %s\n", topo_strerror(err));
238 goto error;
239 }
240 if (dnode->ddn_model && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
241 TOPO_STORAGE_MODEL, TOPO_PROP_IMMUTABLE,
242 dnode->ddn_model, &err) != 0)) {
243 topo_mod_dprintf(mod, "disk_set_props: "
244 "set model error %s\n", topo_strerror(err));
245 goto error;
246 }
247 if (dnode->ddn_serial && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
248 TOPO_STORAGE_SERIAL_NUM, TOPO_PROP_IMMUTABLE,
249 dnode->ddn_serial, &err) != 0)) {
250 topo_mod_dprintf(mod, "disk_set_props: "
251 "set serial error %s\n", topo_strerror(err));
252 goto error;
253 }
254 if (dnode->ddn_firm && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
255 TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
256 dnode->ddn_firm, &err) != 0)) {
257 topo_mod_dprintf(mod, "disk_set_props: "
258 "set firm error %s\n", topo_strerror(err));
259 goto error;
260 }
261 if (dnode->ddn_cap && (topo_prop_set_string(dtn, TOPO_PGROUP_STORAGE,
262 TOPO_STORAGE_CAPACITY, TOPO_PROP_IMMUTABLE,
263 dnode->ddn_cap, &err) != 0)) {
264 topo_mod_dprintf(mod, "disk_set_props: "
265 "set cap error %s\n", topo_strerror(err));
266 goto error;
267 }
268 err = 0;
269
270 out:
271 nvlist_free(fmri);
272 if (label)
273 topo_mod_strfree(mod, label);
274 nvlist_free(asru);
275 return (err);
276
277 error: err = topo_mod_seterrno(mod, err);
278 goto out;
279 }
280
281 /*
282 * Trim leading and trailing whitespace from the string.
283 */
284 static char *
disk_trim_whitespace(topo_mod_t * mod,const char * begin)285 disk_trim_whitespace(topo_mod_t *mod, const char *begin)
286 {
287 const char *end;
288 char *buf;
289 size_t count;
290
291 if (begin == NULL)
292 return (NULL);
293
294 end = begin + strlen(begin);
295
296 while (begin < end && isspace(*begin))
297 begin++;
298 while (begin < end && isspace(*(end - 1)))
299 end--;
300
301 count = end - begin;
302 if ((buf = topo_mod_alloc(mod, count + 1)) == NULL)
303 return (NULL);
304
305 (void) strlcpy(buf, begin, count + 1);
306
307 return (buf);
308 }
309
310 /*
311 * Manufacturing strings can contain characters that are invalid for use in hc
312 * authority names. This trims leading and trailing whitespace, and
313 * substitutes any characters known to be bad.
314 */
315 char *
disk_auth_clean(topo_mod_t * mod,const char * str)316 disk_auth_clean(topo_mod_t *mod, const char *str)
317 {
318 char *buf, *p;
319
320 if (str == NULL)
321 return (NULL);
322
323 if ((buf = topo_mod_strdup(mod, str)) == NULL)
324 return (NULL);
325
326 while ((p = strpbrk(buf, " :=")) != NULL)
327 *p = '-';
328
329 return (buf);
330 }
331
332 /* create the disk topo node */
333 static int
disk_tnode_create(topo_mod_t * mod,tnode_t * parent,dev_di_node_t * dnode,const char * name,topo_instance_t i,tnode_t ** rval)334 disk_tnode_create(topo_mod_t *mod, tnode_t *parent,
335 dev_di_node_t *dnode, const char *name, topo_instance_t i, tnode_t **rval)
336 {
337 int len;
338 nvlist_t *fmri;
339 tnode_t *dtn;
340 char *part = NULL;
341 nvlist_t *auth;
342 char *mfg, *model, *firm, *serial;
343
344 *rval = NULL;
345 if (dnode != NULL) {
346 mfg = disk_auth_clean(mod, dnode->ddn_mfg);
347 model = disk_auth_clean(mod, dnode->ddn_model);
348 firm = disk_auth_clean(mod, dnode->ddn_firm);
349 serial = disk_auth_clean(mod, dnode->ddn_serial);
350 } else {
351 mfg = model = firm = serial = NULL;
352 }
353
354 /* form 'part=' of fmri as "<mfg>-<model>" */
355 if (mfg != NULL && model != NULL) {
356 len = strlen(mfg) + 1 + strlen(model) + 1;
357 if ((part = topo_mod_alloc(mod, len)) != NULL)
358 (void) snprintf(part, len, "%s-%s",
359 mfg, model);
360 }
361
362 auth = topo_mod_auth(mod, parent);
363 fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, NULL,
364 auth, part ? part : model, firm, serial);
365 nvlist_free(auth);
366
367 topo_mod_strfree(mod, part);
368 topo_mod_strfree(mod, mfg);
369 topo_mod_strfree(mod, model);
370 topo_mod_strfree(mod, firm);
371 topo_mod_strfree(mod, serial);
372
373 if (fmri == NULL) {
374 topo_mod_dprintf(mod, "disk_tnode_create: "
375 "hcfmri (%s%d/%s%d) error %s\n",
376 topo_node_name(parent), topo_node_instance(parent),
377 name, i, topo_strerror(topo_mod_errno(mod)));
378 return (-1);
379 }
380
381 if ((dtn = topo_node_bind(mod, parent, name, i, fmri)) == NULL) {
382 if (topo_mod_errno(mod) == EMOD_NODE_BOUND) {
383 /*
384 * if disk 0 is already there then we're done
385 */
386 nvlist_free(fmri);
387 return (0);
388 }
389 topo_mod_dprintf(mod, "disk_tnode_create: "
390 "bind (%s%d/%s%d) error %s\n",
391 topo_node_name(parent), topo_node_instance(parent),
392 name, i, topo_strerror(topo_mod_errno(mod)));
393 nvlist_free(fmri);
394 return (-1);
395 }
396 nvlist_free(fmri);
397
398 /* add the properties of the disk */
399 if (disk_set_props(mod, parent, dtn, dnode) != 0) {
400 topo_mod_dprintf(mod, "disk_tnode_create: "
401 "disk_set_props (%s%d/%s%d) error %s\n",
402 topo_node_name(parent), topo_node_instance(parent),
403 name, i, topo_strerror(topo_mod_errno(mod)));
404 topo_node_unbind(dtn);
405 return (-1);
406 }
407 *rval = dtn;
408 return (0);
409 }
410
411 static int
disk_declare(topo_mod_t * mod,tnode_t * parent,dev_di_node_t * dnode,tnode_t ** childp)412 disk_declare(topo_mod_t *mod, tnode_t *parent, dev_di_node_t *dnode,
413 tnode_t **childp)
414 {
415 tnode_t *dtn = NULL;
416 int rval;
417
418 rval = disk_tnode_create(mod, parent, dnode, DISK, 0, &dtn);
419 if (dtn == NULL) {
420 if (rval == 0)
421 return (0);
422 topo_mod_dprintf(mod, "disk_declare: "
423 "disk_tnode_create error %s\n",
424 topo_strerror(topo_mod_errno(mod)));
425 return (-1);
426 }
427
428 /* register disk_methods against the disk topo node */
429 if (topo_method_register(mod, dtn, disk_methods) != 0) {
430 topo_mod_dprintf(mod, "disk_declare: "
431 "topo_method_register error %s\n",
432 topo_strerror(topo_mod_errno(mod)));
433 topo_node_unbind(dtn);
434 return (-1);
435 }
436 if (childp != NULL)
437 *childp = dtn;
438 return (0);
439 }
440
441 int
disk_declare_path(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * path)442 disk_declare_path(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
443 const char *path)
444 {
445 dev_di_node_t *dnode;
446 int i;
447
448 /*
449 * Check for match using physical phci (ddn_ppath). Use
450 * di_devfs_path_match so generic.vs.non-generic names match.
451 */
452 for (dnode = topo_list_next(listp); dnode != NULL;
453 dnode = topo_list_next(dnode)) {
454 if (dnode->ddn_ppath == NULL)
455 continue;
456
457 for (i = 0; i < dnode->ddn_ppath_count; i++) {
458 if (di_devfs_path_match(dnode->ddn_ppath[0], path))
459 return (disk_declare(mod, parent, dnode, NULL));
460 }
461 }
462
463 topo_mod_dprintf(mod, "disk_declare_path: "
464 "failed to find disk matching path %s", path);
465 return (0);
466 }
467
468 int
disk_declare_addr(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * addr,tnode_t ** childp)469 disk_declare_addr(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
470 const char *addr, tnode_t **childp)
471 {
472 dev_di_node_t *dnode;
473 int i;
474
475 /* Check for match using addr. */
476 for (dnode = topo_list_next(listp); dnode != NULL;
477 dnode = topo_list_next(dnode)) {
478 if (dnode->ddn_target_port == NULL)
479 continue;
480
481 for (i = 0; i < dnode->ddn_ppath_count; i++) {
482 if ((dnode->ddn_target_port[i] != NULL) &&
483 (strncmp(dnode->ddn_target_port[i], addr,
484 strcspn(dnode->ddn_target_port[i], ":"))) == 0) {
485 topo_mod_dprintf(mod, "disk_declare_addr: "
486 "found disk matching addr %s", addr);
487 return (disk_declare(mod, parent, dnode,
488 childp));
489 }
490 }
491 }
492
493 topo_mod_dprintf(mod, "disk_declare_addr: "
494 "failed to find disk matching addr %s", addr);
495
496 return (1);
497 }
498
499 /*
500 * Try to find a disk based on the bridge-port property. This is most often used
501 * for SATA devices which are attached to a SAS controller and are therefore
502 * behind a SATL bridge port. SES only knows of devices based on this SAS WWN,
503 * not based on any SATA GUIDs.
504 */
505 int
disk_declare_bridge(topo_mod_t * mod,tnode_t * parent,topo_list_t * listp,const char * addr,tnode_t ** childp)506 disk_declare_bridge(topo_mod_t *mod, tnode_t *parent, topo_list_t *listp,
507 const char *addr, tnode_t **childp)
508 {
509 dev_di_node_t *dnode;
510 int i;
511
512 /* Check for match using addr. */
513 for (dnode = topo_list_next(listp); dnode != NULL;
514 dnode = topo_list_next(dnode)) {
515 if (dnode->ddn_bridge_port == NULL)
516 continue;
517
518 for (i = 0; i < dnode->ddn_ppath_count; i++) {
519 if ((dnode->ddn_bridge_port[i] != NULL) &&
520 (strncmp(dnode->ddn_bridge_port[i], addr,
521 strcspn(dnode->ddn_bridge_port[i], ":"))) == 0) {
522 topo_mod_dprintf(mod, "disk_declare_bridge: "
523 "found disk matching bridge %s", addr);
524 return (disk_declare(mod, parent, dnode,
525 childp));
526 }
527 }
528 }
529
530 topo_mod_dprintf(mod, "disk_declare_bridge: "
531 "failed to find disk matching bridge %s", addr);
532
533 return (1);
534 }
535
536 /*
537 * Used to declare a disk that has been discovered through other means (usually
538 * ses), that is not enumerated in the devinfo tree.
539 */
540 int
disk_declare_non_enumerated(topo_mod_t * mod,tnode_t * parent,tnode_t ** childp)541 disk_declare_non_enumerated(topo_mod_t *mod, tnode_t *parent, tnode_t **childp)
542 {
543 return (disk_declare(mod, parent, NULL, childp));
544 }
545
546 /* di_devlink callback for dev_di_node_add */
547 static int
disk_devlink_callback(di_devlink_t dl,void * arg)548 disk_devlink_callback(di_devlink_t dl, void *arg)
549 {
550 disk_cbdata_t *cbp = (disk_cbdata_t *)arg;
551 topo_mod_t *mod = cbp->dcb_mod;
552 dev_di_node_t *dnode = cbp->dcb_dnode;
553 const char *devpath;
554 char *ctds, *slice;
555
556 devpath = di_devlink_path(dl);
557 if ((dnode == NULL) || (devpath == NULL))
558 return (DI_WALK_TERMINATE);
559
560 /* trim the slice off the public name */
561 if (((ctds = strrchr(devpath, '/')) != NULL) &&
562 ((slice = strchr(ctds, 's')) != NULL))
563 *slice = '\0';
564
565 /* Establish the public /dev name (no slice) */
566 dnode->ddn_lpath = topo_mod_strdup(mod, ctds ? ctds + 1 : devpath);
567
568 if (ctds && slice)
569 *slice = 's';
570 return (DI_WALK_TERMINATE);
571 }
572
573 static void
dev_di_node_free(topo_mod_t * mod,dev_di_node_t * dnode)574 dev_di_node_free(topo_mod_t *mod, dev_di_node_t *dnode)
575 {
576 int i;
577
578 /* free the stuff we point to */
579 if (dnode->ddn_devid)
580 topo_mod_strfree(mod, dnode->ddn_devid);
581 for (i = 0; i < dnode->ddn_ppath_count; i++) {
582 /* topo_mod_strfree does NULL checking. */
583 topo_mod_strfree(mod, dnode->ddn_ppath[i]);
584 topo_mod_strfree(mod, dnode->ddn_target_port[i]);
585 topo_mod_strfree(mod, dnode->ddn_attached_port[i]);
586 topo_mod_strfree(mod, dnode->ddn_bridge_port[i]);
587 }
588 topo_mod_free(mod, dnode->ddn_ppath,
589 dnode->ddn_ppath_count * sizeof (char *));
590 topo_mod_free(mod, dnode->ddn_target_port,
591 dnode->ddn_ppath_count * sizeof (char *));
592 topo_mod_free(mod, dnode->ddn_attached_port,
593 dnode->ddn_ppath_count * sizeof (char *));
594 topo_mod_free(mod, dnode->ddn_bridge_port,
595 dnode->ddn_ppath_count * sizeof (char *));
596 topo_mod_strfree(mod, dnode->ddn_dpath);
597 topo_mod_strfree(mod, dnode->ddn_lpath);
598
599 topo_mod_strfree(mod, dnode->ddn_mfg);
600 topo_mod_strfree(mod, dnode->ddn_model);
601 topo_mod_strfree(mod, dnode->ddn_serial);
602 topo_mod_strfree(mod, dnode->ddn_firm);
603 topo_mod_strfree(mod, dnode->ddn_cap);
604
605 /* free self */
606 topo_mod_free(mod, dnode, sizeof (dev_di_node_t));
607 }
608
609 static int
dev_di_node_add(di_node_t node,char * devid,disk_cbdata_t * cbp)610 dev_di_node_add(di_node_t node, char *devid, disk_cbdata_t *cbp)
611 {
612 topo_mod_t *mod = cbp->dcb_mod;
613 dev_di_node_t *dnode;
614 di_path_t pnode;
615 char *path;
616 int mlen;
617 char *minorpath;
618 char *extn = ":a";
619 char *s;
620 int64_t *nblocksp;
621 uint64_t nblocks;
622 int *dblksizep;
623 uint_t dblksize;
624 char lentry[MAXPATHLEN];
625 int pathcount;
626 int *inq_dtype, itype;
627 int i;
628
629 if (devid) {
630 /*
631 * Check for list duplicate using devid search.
632 * Note if there is no devid, then we can end up with duplicates
633 * in the list, but this doesn't do any harm.
634 */
635 for (dnode = topo_list_next(cbp->dcb_list);
636 dnode != NULL; dnode = topo_list_next(dnode)) {
637 if (dnode->ddn_devid &&
638 devid_str_compare(dnode->ddn_devid, devid) == 0) {
639 topo_mod_dprintf(mod, "dev_di_node_add: "
640 "already there %s\n", devid);
641 return (0);
642 }
643 }
644 }
645
646 if ((dnode = topo_mod_zalloc(mod, sizeof (dev_di_node_t))) == NULL)
647 return (-1);
648
649 if (devid) {
650 /* Establish the devid. */
651 dnode->ddn_devid = topo_mod_strdup(mod, devid);
652 if (dnode->ddn_devid == NULL)
653 goto error;
654 }
655
656 /* Establish the devinfo dpath */
657 if ((path = di_devfs_path(node)) == NULL) {
658 (void) topo_mod_seterrno(mod, errno);
659 goto error;
660 }
661
662 dnode->ddn_dpath = topo_mod_strdup(mod, path);
663 di_devfs_path_free(path);
664 if (dnode->ddn_dpath == NULL)
665 goto error;
666
667 /*
668 * Establish the physical ppath and target ports. If the device is
669 * non-mpxio then dpath and ppath are the same, and the target port is a
670 * property of the device node.
671 *
672 * If dpath is a client node under scsi_vhci, then iterate over all
673 * paths and get their physical paths and target port properrties.
674 * di_path_client_next_path call below will
675 * return non-NULL, and ppath is set to the physical path to the first
676 * pathinfo node.
677 *
678 * NOTE: It is possible to get a generic.vs.non-generic path
679 * for di_devfs_path.vs.di_path_devfs_path like:
680 * xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
681 * pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
682 * To resolve this issue disk_declare_path() needs to use the
683 * special di_devfs_path_match() interface.
684 */
685 pathcount = 0;
686 pnode = NULL;
687 while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
688 pathcount++;
689 }
690
691 if (pathcount == 0) {
692 if ((dnode->ddn_ppath =
693 topo_mod_zalloc(mod, sizeof (char *))) == NULL)
694 goto error;
695
696 dnode->ddn_ppath_count = 1;
697 if ((dnode->ddn_ppath[0] = topo_mod_strdup(mod,
698 dnode->ddn_dpath)) == NULL)
699 goto error;
700
701 if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
702 sizeof (char *))) == NULL)
703 goto error;
704
705 if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
706 sizeof (char *))) == NULL)
707 goto error;
708
709 if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
710 sizeof (char *))) == NULL)
711 goto error;
712
713 /* There should be only one target port for a devinfo node. */
714 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
715 SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
716 if ((dnode->ddn_target_port[0] =
717 topo_mod_strdup(mod,
718 scsi_wwnstr_skip_ua_prefix(s))) ==
719 NULL)
720 goto error;
721 }
722
723 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
724 SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
725 /* There should be one attached port if any. */
726 if ((dnode->ddn_attached_port[0] =
727 topo_mod_strdup(mod,
728 scsi_wwnstr_skip_ua_prefix(s))) ==
729 NULL)
730 goto error;
731 }
732
733 if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
734 SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
735 /* There should be one bridge port if any. */
736 if ((dnode->ddn_bridge_port[0] =
737 topo_mod_strdup(mod,
738 scsi_wwnstr_skip_ua_prefix(s))) ==
739 NULL)
740 goto error;
741 }
742
743 } else {
744 /* processing a scsi_vhci device. */
745 if ((dnode->ddn_ppath = topo_mod_zalloc(mod,
746 pathcount * sizeof (char *))) == NULL)
747 goto error;
748
749 dnode->ddn_ppath_count = pathcount;
750
751 if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
752 pathcount * sizeof (char *))) == NULL)
753 goto error;
754
755 if ((dnode->ddn_attached_port = topo_mod_zalloc(mod,
756 pathcount * sizeof (char *))) == NULL)
757 goto error;
758
759 if ((dnode->ddn_bridge_port = topo_mod_zalloc(mod,
760 pathcount * sizeof (char *))) == NULL)
761 goto error;
762
763 pnode = NULL;
764 pathcount = 0;
765 while ((pnode = di_path_client_next_path(node,
766 pnode)) != NULL) {
767 if ((path = di_path_devfs_path(pnode)) == NULL) {
768 (void) topo_mod_seterrno(mod, errno);
769 goto error;
770 }
771
772 dnode->ddn_ppath[pathcount] =
773 topo_mod_strdup(mod, path);
774 di_devfs_path_free(path);
775 if (dnode->ddn_ppath[pathcount] == NULL)
776 goto error;
777
778 if ((di_path_prop_lookup_strings(pnode,
779 SCSI_ADDR_PROP_TARGET_PORT, &s)) == 1) {
780 if ((dnode->ddn_target_port[pathcount] =
781 topo_mod_strdup(mod,
782 scsi_wwnstr_skip_ua_prefix(s))) ==
783 NULL)
784 goto error;
785 }
786
787 if ((di_path_prop_lookup_strings(pnode,
788 SCSI_ADDR_PROP_ATTACHED_PORT, &s)) == 1) {
789 if ((dnode->ddn_attached_port[pathcount] =
790 topo_mod_strdup(mod,
791 scsi_wwnstr_skip_ua_prefix(s))) ==
792 NULL)
793 goto error;
794 }
795
796 if ((di_path_prop_lookup_strings(pnode,
797 SCSI_ADDR_PROP_BRIDGE_PORT, &s)) == 1) {
798 if ((dnode->ddn_bridge_port[pathcount] =
799 topo_mod_strdup(mod,
800 scsi_wwnstr_skip_ua_prefix(s))) ==
801 NULL)
802 goto error;
803 }
804
805 pathcount++;
806 }
807 }
808
809 /*
810 * Find the public /dev name for a disk by adding a minor name and using
811 * di_devlink interface for reverse translation (use devinfo path).
812 */
813 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
814 &inq_dtype) > 0) {
815 dnode->ddn_dtype = *inq_dtype;
816 itype = (*inq_dtype) & DTYPE_MASK;
817 if (itype == DTYPE_DIRECT) {
818 mlen = strlen(dnode->ddn_dpath) + strlen(extn) + 1;
819 if ((minorpath = topo_mod_alloc(mod, mlen)) == NULL)
820 goto error;
821 (void) snprintf(minorpath, mlen, "%s%s",
822 dnode->ddn_dpath, extn);
823 cbp->dcb_dnode = dnode;
824 (void) di_devlink_walk(cbp->dcb_devhdl, "^dsk/",
825 minorpath, DI_PRIMARY_LINK, cbp,
826 disk_devlink_callback);
827 topo_mod_free(mod, minorpath, mlen);
828 if (dnode->ddn_lpath == NULL) {
829 topo_mod_dprintf(mod, "dev_di_node_add: "
830 "failed to determine logical path");
831 }
832 }
833 } else {
834 dnode->ddn_dtype = DTYPE_UNKNOWN;
835 }
836
837 /* cache various bits of optional information about the device. */
838 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
839 INQUIRY_VENDOR_ID, &s) > 0) {
840 if ((dnode->ddn_mfg = disk_trim_whitespace(mod, s)) == NULL)
841 goto error;
842 }
843 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
844 INQUIRY_PRODUCT_ID, &s) > 0) {
845 if ((dnode->ddn_model = disk_trim_whitespace(mod, s)) == NULL)
846 goto error;
847 }
848 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
849 INQUIRY_REVISION_ID, &s) > 0) {
850 if ((dnode->ddn_firm = disk_trim_whitespace(mod, s)) == NULL)
851 goto error;
852 }
853 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
854 INQUIRY_SERIAL_NO, &s) > 0) {
855 if ((dnode->ddn_serial = disk_trim_whitespace(mod, s)) == NULL)
856 goto error;
857 }
858 if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
859 "device-nblocks", &nblocksp) > 0) {
860 nblocks = (uint64_t)*nblocksp;
861 /*
862 * To save kernel memory, the driver may not define
863 * "device-dblksize" when its value is default DEV_BSIZE.
864 */
865 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
866 "device-dblksize", &dblksizep) > 0)
867 dblksize = (uint_t)*dblksizep;
868 else
869 dblksize = DEV_BSIZE; /* default value */
870 (void) snprintf(lentry, sizeof (lentry),
871 "%" PRIu64, nblocks * dblksize);
872 if ((dnode->ddn_cap = topo_mod_strdup(mod, lentry)) == NULL)
873 goto error;
874 }
875
876 topo_mod_dprintf(mod, "dev_di_node_add: "
877 "adding %s\n", devid ? dnode->ddn_devid : "NULL devid");
878 topo_mod_dprintf(mod, " "
879 " %s\n", dnode->ddn_dpath);
880 for (i = 0; i < dnode->ddn_ppath_count; i++) {
881 topo_mod_dprintf(mod, " "
882 " %s\n", dnode->ddn_ppath[i]);
883 }
884 topo_list_append(cbp->dcb_list, dnode);
885 return (0);
886
887 error:
888 dev_di_node_free(mod, dnode);
889 return (-1);
890 }
891
892 /* di_walk_node callback for disk_list_gather */
893 static int
dev_walk_di_nodes(di_node_t node,void * arg)894 dev_walk_di_nodes(di_node_t node, void *arg)
895 {
896 char *devidstr = NULL;
897 char *s;
898 int *val;
899
900 /*
901 * If it's not a scsi_vhci client and doesn't have a target_port
902 * property and doesn't have a target property then it's not a storage
903 * device and we're not interested.
904 */
905 if (di_path_client_next_path(node, NULL) == NULL &&
906 di_prop_lookup_strings(DDI_DEV_T_ANY, node,
907 SCSI_ADDR_PROP_TARGET_PORT, &s) <= 0 &&
908 di_prop_lookup_ints(DDI_DEV_T_ANY, node,
909 SCSI_ADDR_PROP_TARGET, &val) <= 0) {
910 return (DI_WALK_CONTINUE);
911 }
912 (void) di_prop_lookup_strings(DDI_DEV_T_ANY, node,
913 DEVID_PROP_NAME, &devidstr);
914
915 /* create/find the devid scsi topology node */
916 (void) dev_di_node_add(node, devidstr, arg);
917
918 return (DI_WALK_CONTINUE);
919 }
920
921 int
dev_list_gather(topo_mod_t * mod,topo_list_t * listp)922 dev_list_gather(topo_mod_t *mod, topo_list_t *listp)
923 {
924 di_node_t devtree;
925 di_devlink_handle_t devhdl;
926 disk_cbdata_t dcb;
927
928 if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
929 topo_mod_dprintf(mod, "disk_list_gather: "
930 "topo_mod_devinfo() failed");
931 return (-1);
932 }
933
934 if ((devhdl = di_devlink_init(NULL, 0)) == DI_NODE_NIL) {
935 topo_mod_dprintf(mod, "disk_list_gather: "
936 "di_devlink_init() failed");
937 return (-1);
938 }
939
940 dcb.dcb_mod = mod;
941 dcb.dcb_list = listp;
942 dcb.dcb_devhdl = devhdl;
943
944 /* walk the devinfo snapshot looking for disk nodes */
945 (void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
946 dev_walk_di_nodes);
947
948 (void) di_devlink_fini(&devhdl);
949
950 return (0);
951 }
952
953 void
dev_list_free(topo_mod_t * mod,topo_list_t * listp)954 dev_list_free(topo_mod_t *mod, topo_list_t *listp)
955 {
956 dev_di_node_t *dnode;
957
958 while ((dnode = topo_list_next(listp)) != NULL) {
959 /* order of delete/free is important */
960 topo_list_delete(listp, dnode);
961 dev_di_node_free(mod, dnode);
962 }
963 }
964
965 /*
966 * Query the current disk status. If successful, the disk status is returned
967 * as an nvlist consisting of at least the following members:
968 *
969 * protocol string Supported protocol (currently "scsi")
970 *
971 * status nvlist Arbitrary protocol-specific information
972 * about the current state of the disk.
973 *
974 * faults nvlist A list of supported faults. Each
975 * element of this list is a boolean value.
976 * An element's existence indicates that
977 * the drive supports detecting this fault,
978 * and the value indicates the current
979 * state of the fault.
980 *
981 * <fault-name> nvlist For each fault named in 'faults', a
982 * nvlist describing protocol-specific
983 * attributes of the fault.
984 *
985 * This method relies on the libdiskstatus library to query this information.
986 */
987 static int
disk_status(topo_mod_t * mod,tnode_t * nodep,topo_version_t vers,nvlist_t * in_nvl,nvlist_t ** out_nvl)988 disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
989 nvlist_t *in_nvl, nvlist_t **out_nvl)
990 {
991 disk_status_t *dsp;
992 char *devpath, *fullpath;
993 size_t pathlen;
994 nvlist_t *status;
995 int err;
996
997 *out_nvl = NULL;
998
999 if (vers != TOPO_METH_DISK_STATUS_VERSION)
1000 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
1001
1002 /*
1003 * If the caller specifies the "path" parameter, then this indicates
1004 * that we should use this instead of deriving it from the topo node
1005 * itself.
1006 */
1007 if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
1008 devpath = NULL;
1009 } else {
1010 /*
1011 * Get the /devices path and attempt to open the disk status
1012 * handle.
1013 */
1014 if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
1015 TOPO_IO_DEV_PATH, &devpath, &err) != 0)
1016 return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
1017
1018 /*
1019 * Note that sizeof(string) includes the terminating NULL byte
1020 */
1021 pathlen = strlen(devpath) + sizeof ("/devices") +
1022 sizeof (PHYS_EXTN) - 1;
1023
1024 if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
1025 return (topo_mod_seterrno(mod, EMOD_NOMEM));
1026
1027 (void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
1028 PHYS_EXTN);
1029
1030 topo_mod_strfree(mod, devpath);
1031 }
1032
1033 if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
1034 if (devpath)
1035 topo_mod_free(mod, fullpath, pathlen);
1036 return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
1037 EMOD_NOMEM : EMOD_METHOD_NOTSUP));
1038 }
1039
1040 if (devpath)
1041 topo_mod_free(mod, fullpath, pathlen);
1042
1043 if ((status = disk_status_get(dsp)) == NULL) {
1044 err = (disk_status_errno(dsp) == EDS_NOMEM ?
1045 EMOD_NOMEM : EMOD_METHOD_NOTSUP);
1046 disk_status_close(dsp);
1047 return (topo_mod_seterrno(mod, err));
1048 }
1049
1050 *out_nvl = status;
1051 disk_status_close(dsp);
1052 return (0);
1053 }
1054