1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2017, Joyent, Inc. 25 * Copyright 2019 by Western Digital Corporation 26 */ 27 28 #include <assert.h> 29 #include <fm/libtopo.h> 30 #include <fm/topo_mod.h> 31 #include <sys/fm/protocol.h> 32 #include <string.h> 33 34 #define TOPO_PGROUP_IPMI "ipmi" 35 #define TOPO_PROP_IPMI_ENTITY_REF "entity_ref" 36 #define TOPO_PROP_IPMI_ENTITY_PRESENT "entity_present" 37 #define FAC_PROV_IPMI "fac_prov_ipmi" 38 39 typedef struct ipmi_enum_data { 40 topo_mod_t *ed_mod; 41 tnode_t *ed_pnode; 42 const char *ed_name; 43 char *ed_label; 44 uint8_t ed_entity; 45 topo_instance_t ed_instance; 46 ipmi_sdr_fru_locator_t *ed_frusdr; 47 } ipmi_enum_data_t; 48 49 static int ipmi_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 50 nvlist_t **); 51 static int ipmi_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 52 nvlist_t **); 53 static int ipmi_enum(topo_mod_t *, tnode_t *, const char *, 54 topo_instance_t, topo_instance_t, void *, void *); 55 static int ipmi_post_process(topo_mod_t *, tnode_t *); 56 57 extern int ipmi_fru_label(topo_mod_t *mod, tnode_t *node, 58 topo_version_t vers, nvlist_t *in, nvlist_t **out); 59 60 extern int ipmi_fru_fmri(topo_mod_t *mod, tnode_t *node, 61 topo_version_t vers, nvlist_t *in, nvlist_t **out); 62 63 static const topo_method_t ipmi_methods[] = { 64 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 65 TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ipmi_present }, 66 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 67 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 68 ipmi_unusable }, 69 { "ipmi_fru_label", "Property method", 0, 70 TOPO_STABILITY_INTERNAL, ipmi_fru_label}, 71 { "ipmi_fru_fmri", "Property method", 0, 72 TOPO_STABILITY_INTERNAL, ipmi_fru_fmri}, 73 { TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC, 74 TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL, 75 topo_method_sensor_failure }, 76 { NULL } 77 }; 78 79 const topo_modops_t ipmi_ops = { ipmi_enum, NULL }; 80 81 const topo_modinfo_t ipmi_info = 82 { "ipmi", FM_FMRI_SCHEME_HC, TOPO_VERSION, &ipmi_ops }; 83 84 /* Common code used by topo methods below to find an IPMI entity */ 85 static int 86 ipmi_find_entity(topo_mod_t *mod, tnode_t *tn, ipmi_handle_t **ihpp, 87 ipmi_entity_t **epp, char **namep, ipmi_sdr_t **sdrpp) 88 { 89 ipmi_handle_t *ihp; 90 ipmi_entity_t *ep; 91 int err; 92 char *name = NULL, **names; 93 ipmi_sdr_t *sdrp = NULL; 94 uint_t nelems, i; 95 96 *ihpp = NULL; 97 *epp = NULL; 98 *namep = NULL; 99 *sdrpp = NULL; 100 101 if ((ihp = topo_mod_ipmi_hold(mod)) == NULL) 102 return (topo_mod_seterrno(mod, ETOPO_METHOD_UNKNOWN)); 103 104 ep = topo_node_getspecific(tn); 105 if (ep != NULL) { 106 *ihpp = ihp; 107 *epp = ep; 108 return (0); 109 } 110 111 if (topo_prop_get_string(tn, TOPO_PGROUP_IPMI, 112 TOPO_PROP_IPMI_ENTITY_PRESENT, &name, &err) == 0) { 113 /* 114 * Some broken IPMI implementations don't export correct 115 * entities, so referring to an entity isn't sufficient. 116 * For these platforms, we allow the XML to specify a 117 * single SDR record that represents the current present 118 * state. 119 */ 120 sdrp = ipmi_sdr_lookup(ihp, name); 121 } else { 122 if (topo_prop_get_string_array(tn, TOPO_PGROUP_IPMI, 123 TOPO_PROP_IPMI_ENTITY_REF, &names, &nelems, &err) != 0) { 124 /* 125 * Not all nodes have an entity_ref attribute. 126 * For these cases, return ENOTSUP so that we 127 * fall back to the default hc presence 128 * detection. 129 */ 130 topo_mod_ipmi_rele(mod); 131 return (topo_mod_seterrno(mod, ETOPO_METHOD_NOTSUP)); 132 } 133 134 for (i = 0; i < nelems; i++) { 135 if ((ep = ipmi_entity_lookup_sdr(ihp, names[i])) 136 != NULL) { 137 name = names[i]; 138 names[i] = NULL; 139 break; 140 } 141 } 142 143 for (i = 0; i < nelems; i++) 144 topo_mod_strfree(mod, names[i]); 145 topo_mod_free(mod, names, (nelems * sizeof (char *))); 146 147 if (ep == NULL) { 148 topo_mod_dprintf(mod, 149 "Failed to get present state of %s=%" PRIu64 "\n", 150 topo_node_name(tn), topo_node_instance(tn)); 151 topo_mod_ipmi_rele(mod); 152 return (-1); 153 } 154 topo_node_setspecific(tn, ep); 155 } 156 157 *ihpp = ihp; 158 *namep = name; 159 *sdrpp = sdrp; 160 return (0); 161 } 162 163 /* 164 * Determine if the entity is present. 165 */ 166 /*ARGSUSED*/ 167 static int 168 ipmi_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 169 nvlist_t *in, nvlist_t **out) 170 { 171 ipmi_handle_t *ihp; 172 ipmi_entity_t *ep; 173 char *name; 174 ipmi_sdr_t *sdrp; 175 int err; 176 boolean_t present = B_FALSE; 177 nvlist_t *nvl; 178 179 err = ipmi_find_entity(mod, tn, &ihp, &ep, &name, &sdrp); 180 if (err != 0) 181 return (err); 182 183 if (ep != NULL) { 184 if (ipmi_entity_present(ihp, ep, &present) != 0) { 185 topo_mod_dprintf(mod, 186 "ipmi_entity_present() failed: %s", 187 ipmi_errmsg(ihp)); 188 topo_mod_strfree(mod, name); 189 topo_mod_ipmi_rele(mod); 190 return (-1); 191 } 192 193 topo_mod_dprintf(mod, 194 "ipmi_entity_present(%d, %d) = %d\n", ep->ie_type, 195 ep->ie_instance, present); 196 } else if (sdrp != NULL) { 197 if (ipmi_entity_present_sdr(ihp, sdrp, &present) != 0) { 198 topo_mod_dprintf(mod, 199 "Failed to get present state of %s (%s)\n", 200 name, ipmi_errmsg(ihp)); 201 topo_mod_strfree(mod, name); 202 topo_mod_ipmi_rele(mod); 203 return (-1); 204 } 205 206 topo_mod_dprintf(mod, "ipmi_entity_present_sdr(%s) = %d\n", 207 name, present); 208 } 209 210 topo_mod_strfree(mod, name); 211 topo_mod_ipmi_rele(mod); 212 213 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 214 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 215 216 if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, present) != 0) { 217 nvlist_free(nvl); 218 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 219 } 220 221 *out = nvl; 222 223 return (0); 224 } 225 226 /* 227 * Check whether an IPMI entity is a sensor that is unavailable 228 */ 229 static int 230 ipmi_check_sensor(ipmi_handle_t *ihp, ipmi_entity_t *ep, const char *name, 231 ipmi_sdr_t *sdrp, void *data) 232 { 233 ipmi_sdr_full_sensor_t *fsp; 234 ipmi_sdr_compact_sensor_t *csp; 235 uint8_t sensor_number; 236 ipmi_sensor_reading_t *reading; 237 238 switch (sdrp->is_type) { 239 case IPMI_SDR_TYPE_FULL_SENSOR: 240 fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record; 241 sensor_number = fsp->is_fs_number; 242 break; 243 244 case IPMI_SDR_TYPE_COMPACT_SENSOR: 245 csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record; 246 sensor_number = csp->is_cs_number; 247 break; 248 249 default: 250 return (0); 251 } 252 253 reading = ipmi_get_sensor_reading(ihp, sensor_number); 254 if (reading != NULL && reading->isr_state_unavailable) 255 return (1); 256 257 return (0); 258 } 259 260 /* 261 * Determine if the entity is unusable 262 */ 263 /*ARGSUSED*/ 264 static int 265 ipmi_unusable(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 266 nvlist_t *in, nvlist_t **out) 267 { 268 ipmi_handle_t *ihp; 269 ipmi_entity_t *ep; 270 char *name; 271 ipmi_sdr_t *sdrp; 272 int err; 273 boolean_t unusable = B_FALSE; 274 nvlist_t *nvl; 275 276 err = ipmi_find_entity(mod, tn, &ihp, &ep, &name, &sdrp); 277 if (err != 0) 278 return (err); 279 280 /* 281 * Check whether the IPMI presented us with an entity for a 282 * sensor that is unavailable. 283 */ 284 if (ep != NULL) { 285 unusable = (ipmi_entity_iter_sdr(ihp, ep, ipmi_check_sensor, 286 NULL) != 0); 287 } else if (sdrp != NULL) { 288 unusable = (ipmi_check_sensor(ihp, NULL, NULL, sdrp, 289 NULL) != 0); 290 } 291 292 topo_mod_strfree(mod, name); 293 topo_mod_ipmi_rele(mod); 294 295 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 296 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 297 298 if (nvlist_add_uint32(nvl, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 299 nvlist_free(nvl); 300 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 301 } 302 303 *out = nvl; 304 305 return (0); 306 } 307 308 /* 309 * This determines if the entity has a FRU locator record set, in which case we 310 * treat this as a FRU, even if it's part of an association. 311 */ 312 /*ARGSUSED*/ 313 static int 314 ipmi_check_sdr(ipmi_handle_t *ihp, ipmi_entity_t *ep, const char *name, 315 ipmi_sdr_t *sdrp, void *data) 316 { 317 ipmi_enum_data_t *edp = data; 318 319 if (sdrp->is_type == IPMI_SDR_TYPE_FRU_LOCATOR) 320 edp->ed_frusdr = (ipmi_sdr_fru_locator_t *)sdrp->is_record; 321 322 return (0); 323 } 324 325 /* 326 * Main entity enumerator. If we find a matching entity type, then instantiate 327 * a topo node. 328 */ 329 static int 330 ipmi_check_entity(ipmi_handle_t *ihp, ipmi_entity_t *ep, void *data) 331 { 332 ipmi_enum_data_t *edp = data; 333 ipmi_enum_data_t cdata; 334 tnode_t *pnode = edp->ed_pnode; 335 topo_mod_t *mod = edp->ed_mod; 336 topo_mod_t *fmod = topo_mod_getspecific(mod); 337 nvlist_t *auth, *fmri; 338 tnode_t *tn; 339 topo_pgroup_info_t pgi; 340 char *frudata = NULL, *part = NULL, *rev = NULL, *serial = NULL; 341 ipmi_fru_prod_info_t fruprod = {0}; 342 ipmi_fru_brd_info_t frubrd = {0}; 343 int err; 344 const char *labelname; 345 char label[64]; 346 size_t len; 347 348 /* 349 * Some questionable IPMI implementations group psu and fan entities 350 * under things like motherboard or chassis entities. So even if this 351 * entity type isn't typically associated with fans and psus, if it has 352 * children, then regardless of the type we need to decend down and 353 * iterate over them. 354 */ 355 if (ep->ie_type != edp->ed_entity) { 356 if (ep->ie_children != 0 && 357 ipmi_entity_iter_children(ihp, ep, ipmi_check_entity, 358 data) != 0) 359 return (1); 360 return (0); 361 } 362 363 /* 364 * The purpose of power and cooling domains is to group psus and fans 365 * together. Unfortunately, some broken IPMI implementations declare 366 * domains that don't contain other elements. Since the end goal is to 367 * only enumerate psus and fans, we'll just ignore such elements. 368 */ 369 if ((ep->ie_type == IPMI_ET_POWER_DOMAIN || 370 ep->ie_type == IPMI_ET_COOLING_DOMAIN) && 371 ep->ie_children == 0) 372 return (0); 373 374 if ((auth = topo_mod_auth(mod, pnode)) == NULL) { 375 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s", 376 topo_mod_errmsg(mod)); 377 return (1); 378 } 379 380 /* 381 * Determine if there's a FRU record associated with this entity. If 382 * so, then read in the FRU identity info so that it can be included 383 * in the authority portion of the FMRI. 384 * 385 * topo_mod_hcfmri() will safely except NULL values for the part, 386 * rev and serial params, so we opt to simply drive on in the face of 387 * any strdup failures. 388 */ 389 edp->ed_frusdr = NULL; 390 (void) ipmi_entity_iter_sdr(ihp, ep, ipmi_check_sdr, edp); 391 if (edp->ed_frusdr != NULL && 392 ipmi_fru_read(ihp, edp->ed_frusdr, &frudata) != -1) { 393 if (ipmi_fru_parse_product(ihp, frudata, &fruprod) == 0) { 394 part = strdup(fruprod.ifpi_part_number); 395 rev = strdup(fruprod.ifpi_product_version); 396 serial = strdup(fruprod.ifpi_product_serial); 397 } else if (ipmi_fru_parse_board(ihp, frudata, &frubrd) == 0) { 398 part = strdup(frubrd.ifbi_part_number); 399 serial = strdup(frubrd.ifbi_product_serial); 400 } 401 } 402 free(frudata); 403 404 if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 405 edp->ed_name, edp->ed_instance, NULL, auth, part, rev, 406 serial)) == NULL) { 407 nvlist_free(auth); 408 free(part); 409 free(rev); 410 free(serial); 411 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 412 topo_mod_errmsg(mod)); 413 return (1); 414 } 415 nvlist_free(auth); 416 free(part); 417 free(rev); 418 free(serial); 419 420 if ((tn = topo_node_bind(mod, pnode, edp->ed_name, 421 edp->ed_instance, fmri)) == NULL) { 422 nvlist_free(fmri); 423 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 424 topo_mod_errmsg(mod)); 425 return (1); 426 } 427 428 /* 429 * We inherit our label from our parent, appending our label in the 430 * process. This results in defaults labels of the form "FM 1 FAN 0" 431 * by default when given a hierarchy. 432 */ 433 if (edp->ed_label != NULL) 434 (void) snprintf(label, sizeof (label), "%s ", edp->ed_label); 435 else 436 label[0] = '\0'; 437 438 switch (edp->ed_entity) { 439 case IPMI_ET_POWER_DOMAIN: 440 labelname = "PM"; 441 break; 442 443 case IPMI_ET_PSU: 444 labelname = "PSU"; 445 break; 446 447 case IPMI_ET_COOLING_DOMAIN: 448 labelname = "FM"; 449 break; 450 451 case IPMI_ET_FAN: 452 labelname = "FAN"; 453 break; 454 455 default: 456 topo_mod_dprintf(mod, "unknown entity type, %u: cannot set " 457 "label name", edp->ed_entity); 458 nvlist_free(fmri); 459 return (1); 460 } 461 462 len = strlen(label); 463 (void) snprintf(label + len, sizeof (label) - len, "%s %d", 464 labelname, edp->ed_instance); 465 466 nvlist_free(fmri); 467 edp->ed_instance++; 468 469 if (topo_node_label_set(tn, label, &err) != 0) { 470 topo_mod_dprintf(mod, "failed to set label: %s\n", 471 topo_strerror(err)); 472 return (1); 473 } 474 475 /* 476 * Store IPMI entity details as properties on the node 477 */ 478 pgi.tpi_name = TOPO_PGROUP_IPMI; 479 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 480 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 481 pgi.tpi_version = TOPO_VERSION; 482 if (topo_pgroup_create(tn, &pgi, &err) != 0) { 483 if (err != ETOPO_PROP_DEFD) { 484 topo_mod_dprintf(mod, "failed to create propgroup " 485 "%s: %s\n", TOPO_PGROUP_IPMI, topo_strerror(err)); 486 return (1); 487 } 488 } 489 490 /* 491 * Add properties to contain the IPMI entity id and instance. This 492 * will be used by the fac_prov_ipmi module to discover and enumerate 493 * facility nodes for any associated sensors. 494 */ 495 if (topo_prop_set_uint32(tn, TOPO_PGROUP_IPMI, TOPO_PROP_IPMI_ENTITY_ID, 496 TOPO_PROP_IMMUTABLE, ep->ie_type, &err) != 0 || 497 topo_prop_set_uint32(tn, TOPO_PGROUP_IPMI, 498 TOPO_PROP_IPMI_ENTITY_INST, TOPO_PROP_IMMUTABLE, ep->ie_instance, 499 &err) != 0) { 500 topo_mod_dprintf(mod, "failed to add ipmi properties (%s)", 501 topo_strerror(err)); 502 return (1); 503 } 504 if (topo_method_register(mod, tn, ipmi_methods) != 0) { 505 topo_mod_dprintf(mod, "topo_method_register() failed: %s", 506 topo_mod_errmsg(mod)); 507 return (1); 508 } 509 510 /* 511 * Invoke the tmo_enum callback from the fac_prov_ipmi module on this 512 * node. This will have the effect of registering a method on this node 513 * for enumerating sensors. 514 */ 515 if (fmod == NULL && (fmod = topo_mod_load(mod, FAC_PROV_IPMI, 516 TOPO_VERSION)) == NULL) { 517 topo_mod_dprintf(mod, "failed to load %s: %s", 518 FAC_PROV_IPMI, topo_mod_errmsg(mod)); 519 return (-1); 520 } 521 topo_mod_setspecific(mod, fmod); 522 523 if (topo_mod_enumerate(fmod, tn, FAC_PROV_IPMI, FAC_PROV_IPMI, 0, 0, 524 NULL) != 0) { 525 topo_mod_dprintf(mod, "facility provider enum failed (%s)", 526 topo_mod_errmsg(mod)); 527 return (1); 528 } 529 530 /* 531 * If we are a child of a non-chassis node, and there isn't an explicit 532 * FRU locator record, then propagate the parent's FRU. Otherwise, set 533 * the FRU to be the same as the resource. 534 */ 535 if (strcmp(topo_node_name(pnode), CHASSIS) == 0 || 536 edp->ed_frusdr != NULL) { 537 if (topo_node_resource(tn, &fmri, &err) != 0) { 538 topo_mod_dprintf(mod, "topo_node_resource() failed: %s", 539 topo_strerror(err)); 540 (void) topo_mod_seterrno(mod, err); 541 return (1); 542 } 543 } else { 544 if (topo_node_fru(pnode, &fmri, NULL, &err) != 0) { 545 topo_mod_dprintf(mod, "topo_node_fru() failed: %s", 546 topo_strerror(err)); 547 (void) topo_mod_seterrno(mod, err); 548 return (1); 549 } 550 } 551 552 if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 553 nvlist_free(fmri); 554 topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s", 555 topo_strerror(err)); 556 (void) topo_mod_seterrno(mod, err); 557 return (1); 558 } 559 560 topo_node_setspecific(tn, ep); 561 562 nvlist_free(fmri); 563 564 /* 565 * Iterate over children, once for recursive domains and once for 566 * psu/fans. 567 */ 568 if (ep->ie_children != 0 && 569 (ep->ie_type == IPMI_ET_POWER_DOMAIN || 570 ep->ie_type == IPMI_ET_COOLING_DOMAIN)) { 571 cdata.ed_mod = edp->ed_mod; 572 cdata.ed_pnode = tn; 573 cdata.ed_instance = 0; 574 cdata.ed_name = edp->ed_name; 575 cdata.ed_entity = edp->ed_entity; 576 cdata.ed_label = label; 577 578 if (ipmi_entity_iter_children(ihp, ep, 579 ipmi_check_entity, &cdata) != 0) 580 return (1); 581 582 switch (cdata.ed_entity) { 583 case IPMI_ET_POWER_DOMAIN: 584 cdata.ed_entity = IPMI_ET_PSU; 585 cdata.ed_name = PSU; 586 break; 587 588 case IPMI_ET_COOLING_DOMAIN: 589 cdata.ed_entity = IPMI_ET_FAN; 590 cdata.ed_name = FAN; 591 break; 592 } 593 594 if (ipmi_entity_iter_children(ihp, ep, 595 ipmi_check_entity, &cdata) != 0) 596 return (1); 597 } 598 599 return (0); 600 } 601 602 /* 603 * libtopo enumeration point. This simply iterates over entities looking for 604 * the appropriate type. 605 */ 606 /*ARGSUSED*/ 607 static int 608 ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 609 topo_instance_t min, topo_instance_t max, void *arg, void *unused) 610 { 611 ipmi_handle_t *ihp; 612 ipmi_enum_data_t data; 613 int ret; 614 615 /* 616 * If the node being passed in ISN'T the chassis node, then we're being 617 * asked to post-process a statically defined node. 618 */ 619 if (strcmp(topo_node_name(rnode), CHASSIS) != 0) { 620 if (ipmi_post_process(mod, rnode) != 0) { 621 topo_mod_dprintf(mod, "post processing of node %s=%" 622 PRIu64 " failed!", topo_node_name(rnode), 623 topo_node_instance(rnode)); 624 return (-1); 625 } 626 return (0); 627 } 628 629 if (strcmp(name, POWERMODULE) == 0) { 630 data.ed_entity = IPMI_ET_POWER_DOMAIN; 631 } else if (strcmp(name, PSU) == 0) { 632 data.ed_entity = IPMI_ET_PSU; 633 } else if (strcmp(name, FANMODULE) == 0) { 634 data.ed_entity = IPMI_ET_COOLING_DOMAIN; 635 } else if (strcmp(name, FAN) == 0) { 636 data.ed_entity = IPMI_ET_FAN; 637 } else { 638 topo_mod_dprintf(mod, "unknown enumeration type '%s'", 639 name); 640 return (-1); 641 } 642 643 if ((ihp = topo_mod_ipmi_hold(mod)) == NULL) 644 return (0); 645 646 data.ed_mod = mod; 647 data.ed_pnode = rnode; 648 data.ed_name = name; 649 data.ed_instance = 0; 650 data.ed_label = NULL; 651 652 if ((ret = ipmi_entity_iter(ihp, ipmi_check_entity, &data)) != 0) { 653 /* 654 * We don't return failure if IPMI enumeration fails. This may 655 * be due to the SP being unavailable or an otherwise transient 656 * event. 657 */ 658 if (ret < 0) { 659 topo_mod_dprintf(mod, 660 "failed to enumerate entities: %s", 661 ipmi_errmsg(ihp)); 662 } else { 663 topo_mod_ipmi_rele(mod); 664 return (-1); 665 } 666 } 667 668 topo_mod_ipmi_rele(mod); 669 return (0); 670 } 671 672 static int 673 ipmi_post_process(topo_mod_t *mod, tnode_t *tn) 674 { 675 if (topo_method_register(mod, tn, ipmi_methods) != 0) { 676 topo_mod_dprintf(mod, "ipmi_post_process() failed: %s", 677 topo_mod_errmsg(mod)); 678 return (1); 679 } 680 return (0); 681 } 682 683 /*ARGSUSED*/ 684 int 685 _topo_init(topo_mod_t *mod, topo_version_t version) 686 { 687 if (getenv("TOPOIPMIDEBUG") != NULL) 688 topo_mod_setdebug(mod); 689 690 if (topo_mod_register(mod, &ipmi_info, TOPO_VERSION) != 0) { 691 topo_mod_dprintf(mod, "module registration failed: %s\n", 692 topo_mod_errmsg(mod)); 693 return (-1); /* mod errno already set */ 694 } 695 696 topo_mod_dprintf(mod, "IPMI enumerator initialized\n"); 697 return (0); 698 } 699 700 void 701 _topo_fini(topo_mod_t *mod) 702 { 703 /* 704 * This is the logical, and probably only safe spot where we could 705 * unload fac_prov_ipmi. But unfortunately, calling topo_mod_unload() 706 * in the context of a module's _topo_fini entry point would result 707 * in recursively grabbing the modhash lock and we'd deadlock. 708 * 709 * Unfortunately, libtopo doesn't currently have a mechanism for 710 * expressing and handling intermodule dependencies, so we're left 711 * with this situation where once a module loads another module, 712 * it's going to be with us until we teardown the process. 713 */ 714 topo_mod_unregister(mod); 715 } 716