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