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