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