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
ipmi_find_entity(topo_mod_t * mod,tnode_t * tn,ipmi_handle_t ** ihpp,ipmi_entity_t ** epp,char ** namep,ipmi_sdr_t ** sdrpp)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
ipmi_present(topo_mod_t * mod,tnode_t * tn,topo_version_t version,nvlist_t * in,nvlist_t ** out)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
ipmi_check_sensor(ipmi_handle_t * ihp,ipmi_entity_t * ep,const char * name,ipmi_sdr_t * sdrp,void * data)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
ipmi_unusable(topo_mod_t * mod,tnode_t * tn,topo_version_t version,nvlist_t * in,nvlist_t ** out)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
ipmi_check_sdr(ipmi_handle_t * ihp,ipmi_entity_t * ep,const char * name,ipmi_sdr_t * sdrp,void * data)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
ipmi_check_entity(ipmi_handle_t * ihp,ipmi_entity_t * ep,void * data)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
ipmi_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * unused)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
ipmi_post_process(topo_mod_t * mod,tnode_t * tn)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
_topo_init(topo_mod_t * mod,topo_version_t version)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
_topo_fini(topo_mod_t * mod)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